Skip to content

Commit

Permalink
Forwarding the LanguageWorkerOptions from the host
Browse files Browse the repository at this point in the history
to the job host scope and ensuring we use the provided instances.
  • Loading branch information
fabiocav committed Aug 13, 2024
1 parent 6a22f1f commit af614bb
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 18 deletions.
10 changes: 4 additions & 6 deletions src/WebJobs.Script/Description/FunctionDescriptorProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,20 +181,18 @@ private bool TryParseTriggerParameter(BindingMetadata metadata, out ParameterDes

protected internal virtual void ValidateFunction(FunctionMetadata functionMetadata)
{
HashSet<string> names = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
HashSet<string> names = new(StringComparer.OrdinalIgnoreCase);
foreach (var binding in functionMetadata.Bindings)
{
ValidateBinding(binding);

// Ensure no duplicate binding names
if (names.Contains(binding.Name))
{
throw new InvalidOperationException(string.Format("Multiple bindings with name '{0}' discovered. Binding names must be unique.", binding.Name));
}
else
{
names.Add(binding.Name);
throw new InvalidOperationException($"{nameof(FunctionDescriptorProvider)}: Multiple bindings with name '{binding.Name}' discovered. Binding names must be unique.");
}

names.Add(binding.Name);
}

// Verify there aren't multiple triggers defined
Expand Down
8 changes: 3 additions & 5 deletions src/WebJobs.Script/Host/WorkerFunctionMetadataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,13 +257,11 @@ internal static FunctionMetadata ValidateBindings(IEnumerable<string> rawBinding
// Ensure no duplicate binding names exist
if (bindingNames.Contains(functionBinding.Name))
{
throw new InvalidOperationException(string.Format("Multiple bindings with name '{0}' discovered. Binding names must be unique.", functionBinding.Name));
}
else
{
bindingNames.Add(functionBinding.Name);
throw new InvalidOperationException($"{nameof(WorkerFunctionDescriptorProvider)}: Multiple bindings with name '{functionBinding.Name}' discovered. Binding names must be unique.");
}

bindingNames.Add(functionBinding.Name);

// add binding to function.Bindings once validation is complete
function.Bindings.Add(functionBinding);
}
Expand Down
14 changes: 11 additions & 3 deletions src/WebJobs.Script/ScriptHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,9 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp
// Configuration
services.AddSingleton<IOptions<ScriptApplicationHostOptions>>(new OptionsWrapper<ScriptApplicationHostOptions>(applicationHostOptions));
services.AddSingleton<IOptionsMonitor<ScriptApplicationHostOptions>>(new ScriptApplicationHostOptionsMonitor(applicationHostOptions));

services.ConfigureOptions<ScriptJobHostOptionsSetup>();
services.ConfigureOptions<JobHostFunctionTimeoutOptionsSetup>();
// LanguageWorkerOptionsSetup should be registered in WebHostServiceCollection as well to enable starting worker processing in placeholder mode.
services.ConfigureOptions<LanguageWorkerOptionsSetup>();
services.AddOptions<WorkerConcurrencyOptions>();
services.ConfigureOptions<HttpWorkerOptionsSetup>();
services.ConfigureOptions<ManagedDependencyOptionsSetup>();
Expand All @@ -329,8 +328,17 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp

services.AddSingleton<IFileLoggingStatusManager, FileLoggingStatusManager>();

if (!applicationHostOptions.HasParentScope)
if (applicationHostOptions.HasParentScope)
{
// Forward th host LanguageWorkerOptions to the Job Host.
var languageWorkerOptions = applicationHostOptions.RootServiceProvider.GetService<IOptionsMonitor<LanguageWorkerOptions>>();
services.AddSingleton(languageWorkerOptions);
services.AddSingleton<IOptions<LanguageWorkerOptions>>((s) => new OptionsWrapper<LanguageWorkerOptions>(languageWorkerOptions.CurrentValue));
services.ConfigureOptions<JobHostLanguageWorkerOptionsSetup>();
}
else
{
services.ConfigureOptions<LanguageWorkerOptionsSetup>();
AddCommonServices(services);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public FunctionInvocationDispatcherFactory(IOptions<ScriptJobHostOptions> script
IHttpWorkerChannelFactory httpWorkerChannelFactory,
IRpcWorkerChannelFactory rpcWorkerChannelFactory,
IOptions<HttpWorkerOptions> httpWorkerOptions,
IOptionsMonitor<LanguageWorkerOptions> rpcWorkerOptions,
IOptionsMonitor<LanguageWorkerOptions> workerOptions,
IEnvironment environment,
IWebHostRpcWorkerChannelManager webHostLanguageWorkerChannelManager,
IJobHostRpcWorkerChannelManager jobHostLanguageWorkerChannelManager,
Expand All @@ -54,7 +54,7 @@ public FunctionInvocationDispatcherFactory(IOptions<ScriptJobHostOptions> script
eventManager,
loggerFactory,
rpcWorkerChannelFactory,
rpcWorkerOptions,
workerOptions,
webHostLanguageWorkerChannelManager,
jobHostLanguageWorkerChannelManager,
managedDependencyOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Azure.WebJobs.Script.Diagnostics;
using Microsoft.Azure.WebJobs.Script.Workers.Profiles;
using Microsoft.Extensions.Configuration;
Expand Down Expand Up @@ -56,4 +57,24 @@ public void Configure(LanguageWorkerOptions options)
options.WorkerConfigs = configFactory.GetConfigs();
}
}

internal class JobHostLanguageWorkerOptionsSetup : IPostConfigureOptions<LanguageWorkerOptions>
{
private readonly ILoggerFactory _loggerFactory;

public JobHostLanguageWorkerOptionsSetup(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}

public void PostConfigure(string name, LanguageWorkerOptions options)
{
var message = $"Call to configure {nameof(LanguageWorkerOptions)} from the JobHost scope. " +
$"If using {nameof(IOptions<LanguageWorkerOptions>)}, please use {nameof(IOptionsMonitor<LanguageWorkerOptions>)} instead.";
Debug.Fail(message);

var logger = _loggerFactory.CreateLogger("Host.LanguageWorkerConfig");
logger.LogInformation(message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public RpcFunctionInvocationDispatcher(IOptions<ScriptJobHostOptions> scriptHost
IScriptEventManager eventManager,
ILoggerFactory loggerFactory,
IRpcWorkerChannelFactory rpcWorkerChannelFactory,
IOptionsMonitor<LanguageWorkerOptions> languageWorkerOptions,
IOptionsMonitor<LanguageWorkerOptions> workerOptions,
IWebHostRpcWorkerChannelManager webHostLanguageWorkerChannelManager,
IJobHostRpcWorkerChannelManager jobHostLanguageWorkerChannelManager,
IOptions<ManagedDependencyOptions> managedDependencyOptions,
Expand All @@ -83,7 +83,11 @@ public RpcFunctionInvocationDispatcher(IOptions<ScriptJobHostOptions> scriptHost
_webHostLanguageWorkerChannelManager = webHostLanguageWorkerChannelManager;
_jobHostLanguageWorkerChannelManager = jobHostLanguageWorkerChannelManager;
_eventManager = eventManager;
_workerConfigs = languageWorkerOptions?.CurrentValue?.WorkerConfigs ?? throw new ArgumentNullException(nameof(languageWorkerOptions));

// The state of worker configuration and the LanguageWorkerOptions will match the lifetime of the JobHost and the
// RpcFunctionInvocationDispatcher. So, we can safely cache the workerConfigs and workerRuntime here.
// Using IOptionsMonitor here to get the same cached version used by other copmponents and avoid a new instance initialization.
_workerConfigs = workerOptions?.CurrentValue?.WorkerConfigs ?? throw new ArgumentNullException(nameof(workerOptions));
_managedDependencyOptions = managedDependencyOptions ?? throw new ArgumentNullException(nameof(managedDependencyOptions));
_logger = loggerFactory.CreateLogger<RpcFunctionInvocationDispatcher>();
_rpcWorkerChannelFactory = rpcWorkerChannelFactory;
Expand Down

0 comments on commit af614bb

Please sign in to comment.