Skip to content

Refactor code to move the current logic to search for WorkerConfigs to a default worker configuration resolver #11229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: dev
Choose a base branch
from

Conversation

surgupta-msft
Copy link
Contributor

Issue describing the changes in this PR

resolves #11218

Pull request checklist

IMPORTANT: Currently, changes must be backported to the in-proc branch to be included in Core Tools and non-Flex deployments.

  • Backporting to the in-proc branch is not required
    • Otherwise: Link to backporting PR
  • My changes do not require documentation changes
    • Otherwise: Documentation issue linked to PR
  • My changes should not be added to the release notes for the next release
    • Otherwise: I've added my notes to release_notes.md
  • My changes do not need to be backported to a previous version
    • Otherwise: Backport tracked by issue/PR #issue_or_pr
  • My changes do not require diagnostic events changes
    • Otherwise: I have added/updated all related diagnostic events and their documentation (Documentation issue linked to PR)
  • I have added all required tests (Unit tests, E2E tests)

Additional information

Additional PR information

Refactor code to move the current logic to search for WorkerConfigs to a default worker configuration resolver. The current logic finds workerConfigs by scanning workers directory within the Host. This resolver should use Options pattern to get the required input values.

These changes should make it easier to add a new Resolver in future which will resolve workerConfigs from specified worker probing paths.

@Copilot Copilot AI review requested due to automatic review settings August 1, 2025 19:04
@surgupta-msft surgupta-msft requested a review from a team as a code owner August 1, 2025 19:04
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the worker configuration resolution logic by extracting it from RpcWorkerConfigFactory into a dedicated DefaultWorkerConfigurationResolver class. The refactoring introduces the Options pattern for configuration management and enables easier extension with additional resolvers in the future.

Key changes include:

  • Extract worker configuration discovery logic into a new resolver pattern
  • Implement Options pattern for worker configuration resolver settings
  • Update dependency injection to support the new resolver architecture

Reviewed Changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
IWorkerConfigurationResolver.cs New interface defining worker configuration resolution contract
DefaultWorkerConfigurationResolver.cs New implementation that handles worker config path discovery from workers directory
WorkerConfigurationResolverOptions.cs Options class for resolver configuration with JSON serialization support
WorkerConfigurationResolverOptionsSetup.cs Setup class for configuring resolver options using Options pattern
RpcWorkerConfigFactory.cs Refactored to use the new resolver instead of inline logic
Various test files Updated to accommodate new dependency injection requirements

var workerConfigurationResolverOptions = p.GetService<IOptionsMonitor<WorkerConfigurationResolverOptions>>();
var loggerFactory = p.GetService<ILoggerFactory>();
return new DefaultWorkerConfigurationResolver(loggerFactory, workerConfigurationResolverOptions);
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DI of WorkerConfigurationResolverOptionsSetup has been done similar to LanguageWorkerOptionsSetup because the ultimate purpose of both is to get WorkerConfigs and LanguageWorkerOptionsSetup uses WorkerConfigurationResolverOptions as input.

if (_workerConfigResolverOptionsChangeTokenSource is HostBuiltChangeTokenSource<WorkerConfigurationResolverOptions> { } hostBuiltChangeToken)
{
hostBuiltChangeToken.TriggerChange();
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines 385-388 are implemented similarly to lines 390-393

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I filed #11208 to make this easier in the future.

var workerResolverOptions = applicationHostOptions.RootServiceProvider.GetService<IOptionsMonitor<WorkerConfigurationResolverOptions>>();
services.AddSingleton(workerResolverOptions);
services.AddSingleton<IOptions<WorkerConfigurationResolverOptions>>(s => new OptionsWrapper<WorkerConfigurationResolverOptions>(workerResolverOptions.CurrentValue));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines 342-345 are implemented similarly to lines 347-350

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you remove this, does it work? I think we did this with the LanguageWorkerOptions below b/c it was a complex setup. This new one is not. I think the registration will be copied to the child container automatically and all just work.

{
WorkersDirPath = workersDirectorySection.Value;
}
WorkersDirPath = _workerConfigurationResolverOptions.CurrentValue.WorkersDirPath;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines 51-57 and lines 71-84 moved to WorkerConfigurationResolverOptionsSetup class.

@surgupta-msft surgupta-msft requested a review from kshyju August 4, 2025 16:12
@surgupta-msft surgupta-msft requested review from kshyju and jviau August 6, 2025 19:27
@surgupta-msft surgupta-msft requested a review from kshyju August 12, 2025 15:20
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
{
PropertyNameCaseInsensitive = true
};

private Dictionary<string, RpcWorkerConfig> _workerDescriptionDictionary = new Dictionary<string, RpcWorkerConfig>();
private WorkerConfigurationResolutionInfo _workerConfigurationResolutionInfo;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this. It's not really needed.


foreach (var workerConfig in workerConfigs)
{
AddProvider(workerConfig);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass the root worker dir path into this method instead of storing it in a field.

@@ -122,13 +106,15 @@ internal void AddProvider(string workerDir)
{
try
{
string workersDirPath = _workerConfigurationResolutionInfo.WorkersDirPath;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this. It should be passed in instead of being a field.

Comment on lines +10 to +12
public readonly string WorkersDirPath => WorkersDirectoryPath;

public readonly IReadOnlyList<string> WorkerConfigPaths => WorkerConfigPathsList;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure you don't need these properties. Records auto-generate properties based on the constructor params. This (note that I also removed struct) generates what we want, for example (init-only properties are auto-created):

internal record WorkerConfigurationResolutionInfo(string WorkersDirectoryPath, IReadOnlyList<string> WorkerConfigPathsList);

/// <summary>
/// Gets or sets the workers directory path within the Host or defined by IConfiguration.
/// </summary>
public string WorkersDirPath { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think renaming this to WorkersPathRoot would be clearer for what this is giving us.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I prefer WorkerRootDir - multiple workers is already implied

/// </summary>
internal interface IWorkerConfigurationResolver
{
WorkerConfigurationResolutionInfo GetWorkerConfigPaths();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to GetConfigurationInfo()? -- also maybe drop the Resolution from that record to make it a little shorter?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also prefer WorkerConfigurationInfo here


namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc.Configuration
{
internal record struct WorkerConfigurationResolutionInfo(string WorkersDirectoryPath, IReadOnlyList<string> WorkerConfigPathsList)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd name the first parameter here WorkersPathRoot. Secone one WorkerConfigurationFiles? I think it's giving us the actual full file paths, right?

@@ -217,6 +219,7 @@ public static void AddWebJobsScriptHost(this IServiceCollection services, IConfi
services.ConfigureOptions<ScriptApplicationHostOptionsSetup>();
services.AddSingleton<IOptionsChangeTokenSource<ScriptApplicationHostOptions>, ScriptApplicationHostOptionsChangeTokenSource>();
services.ConfigureOptions<StandbyOptionsSetup>();
services.ConfigureOptions<WorkerConfigurationResolverOptionsSetup>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe this (or the next line, actually) are required. If you look at what ConfigureOptionsWithChangeTokenSource does -- it does this already.

var workerResolverOptions = applicationHostOptions.RootServiceProvider.GetService<IOptionsMonitor<WorkerConfigurationResolverOptions>>();
services.AddSingleton(workerResolverOptions);
services.AddSingleton<IOptions<WorkerConfigurationResolverOptions>>(s => new OptionsWrapper<WorkerConfigurationResolverOptions>(workerResolverOptions.CurrentValue));

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you remove this, does it work? I think we did this with the LanguageWorkerOptions below b/c it was a complex setup. This new one is not. I think the registration will be copied to the child container automatically and all just work.

private static readonly Action<ILogger, string, Exception> _defaultWorkersDirectoryPath =
LoggerMessage.Define<string>(LogLevel.Debug,
new EventId(343, nameof(DefaultWorkersDirectoryPath)),
"Workers Directory set to: {workersDirPath}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

                   "Workers Directory set to: {workersDirPath}");
      
      
        
                   "Worker directory set to: {workerDirPath}");

Use “Worker Directory” — in noun–noun phrases, the first noun is usually singular unless it’s a fixed name, so “worker” is grammatically cleaner than “workers” here. I know its named "workers" because multiple workers would be found here, but "Worker directory” means “a directory for workers” — the fact that it holds multiple workers is already implied.

{
AddProvider(workerDir);
}
_logger.LogTrace("No worker configs found.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens upstream if no worker configs are found? We just return here so I am wondering if we should throw, is this a bad state?

/// </summary>
internal interface IWorkerConfigurationResolver
{
WorkerConfigurationResolutionInfo GetWorkerConfigPaths();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also prefer WorkerConfigurationInfo here

/// <summary>
/// Gets or sets the workers directory path within the Host or defined by IConfiguration.
/// </summary>
public string WorkersDirPath { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I prefer WorkerRootDir - multiple workers is already implied

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refactor code to move the current logic to search for WorkerConfigs to a default worker configuration resolver
5 participants