Skip to content

Commit 30a2fc9

Browse files
authored
Do not use cached worker options when creating channel (#5969)
1 parent ccd713a commit 30a2fc9

14 files changed

+53
-38
lines changed

src/WebJobs.Script/Host/ScriptHost.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ public class ScriptHost : JobHost, IScriptJobHost
5252
private readonly IFileLoggingStatusManager _fileLoggingStatusManager;
5353
private readonly IHostIdProvider _hostIdProvider;
5454
private readonly IHttpRoutesManager _httpRoutesManager;
55-
private readonly IEnumerable<RpcWorkerConfig> _workerConfigs;
5655
private readonly IMetricsLogger _metricsLogger = null;
5756
private readonly string _hostLogPath;
5857
private readonly Stopwatch _stopwatch = new Stopwatch();
@@ -83,7 +82,6 @@ public class ScriptHost : JobHost, IScriptJobHost
8382
// Map from BindingType to the Assembly Qualified Type name for its IExtensionConfigProvider object.
8483

8584
public ScriptHost(IOptions<JobHostOptions> options,
86-
IOptions<LanguageWorkerOptions> languageWorkerOptions,
8785
IOptions<HttpWorkerOptions> httpWorkerOptions,
8886
IEnvironment environment,
8987
IJobHostContextFactory jobHostContextFactory,
@@ -123,7 +121,6 @@ public ScriptHost(IOptions<JobHostOptions> options,
123121
_applicationLifetime = applicationLifetime;
124122
_hostIdProvider = hostIdProvider;
125123
_httpRoutesManager = httpRoutesManager;
126-
_workerConfigs = languageWorkerOptions.Value.WorkerConfigs;
127124
_isHttpWorker = httpWorkerOptions.Value.Description != null;
128125
ScriptOptions = scriptHostOptions.Value;
129126
_scriptHostManager = scriptHostManager;

src/WebJobs.Script/ScriptHostBuilderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp
165165
services.AddSingleton<IOptionsMonitor<ScriptApplicationHostOptions>>(new ScriptApplicationHostOptionsMonitor(applicationHostOptions));
166166
services.ConfigureOptions<ScriptHostOptionsSetup>();
167167
services.ConfigureOptions<JobHostFunctionTimeoutOptionsSetup>();
168-
// TODO: pgopa only add this to WebHostServiceCollection
168+
// LanguageWorkerOptionsSetup should be registered in WebHostServiceCollection as well to enable starting worker processing in placeholder mode.
169169
services.ConfigureOptions<LanguageWorkerOptionsSetup>();
170170
services.ConfigureOptions<HttpWorkerOptionsSetup>();
171171
services.ConfigureOptions<ManagedDependencyOptionsSetup>();

src/WebJobs.Script/Workers/FunctionInvocationDispatcherFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public FunctionInvocationDispatcherFactory(IOptions<ScriptJobHostOptions> script
2525
IHttpWorkerChannelFactory httpWorkerChannelFactory,
2626
IRpcWorkerChannelFactory rpcWorkerChannelFactory,
2727
IOptions<HttpWorkerOptions> httpWorkerOptions,
28-
IOptions<LanguageWorkerOptions> rpcWorkerOptions,
28+
IOptionsMonitor<LanguageWorkerOptions> rpcWorkerOptions,
2929
IEnvironment environment,
3030
IWebHostRpcWorkerChannelManager webHostLanguageWorkerChannelManager,
3131
IJobHostRpcWorkerChannelManager jobHostLanguageWorkerChannelManager,

src/WebJobs.Script/Workers/Rpc/FunctionRegistration/RpcFunctionInvocationDispatcher.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public RpcFunctionInvocationDispatcher(IOptions<ScriptJobHostOptions> scriptHost
5858
IScriptEventManager eventManager,
5959
ILoggerFactory loggerFactory,
6060
IRpcWorkerChannelFactory rpcWorkerChannelFactory,
61-
IOptions<LanguageWorkerOptions> languageWorkerOptions,
61+
IOptionsMonitor<LanguageWorkerOptions> languageWorkerOptions,
6262
IWebHostRpcWorkerChannelManager webHostLanguageWorkerChannelManager,
6363
IJobHostRpcWorkerChannelManager jobHostLanguageWorkerChannelManager,
6464
IOptions<ManagedDependencyOptions> managedDependencyOptions,
@@ -71,7 +71,7 @@ public RpcFunctionInvocationDispatcher(IOptions<ScriptJobHostOptions> scriptHost
7171
_webHostLanguageWorkerChannelManager = webHostLanguageWorkerChannelManager;
7272
_jobHostLanguageWorkerChannelManager = jobHostLanguageWorkerChannelManager;
7373
_eventManager = eventManager;
74-
_workerConfigs = languageWorkerOptions.Value.WorkerConfigs;
74+
_workerConfigs = languageWorkerOptions.CurrentValue.WorkerConfigs;
7575
_managedDependencyOptions = managedDependencyOptions ?? throw new ArgumentNullException(nameof(managedDependencyOptions));
7676
_logger = loggerFactory.CreateLogger<RpcFunctionInvocationDispatcher>();
7777
_rpcWorkerChannelFactory = rpcWorkerChannelFactory;
@@ -113,7 +113,7 @@ internal async void InitializeJobhostLanguageWorkerChannelAsync()
113113

114114
internal Task InitializeJobhostLanguageWorkerChannelAsync(int attemptCount)
115115
{
116-
var rpcWorkerChannel = _rpcWorkerChannelFactory.Create(_scriptOptions.RootScriptPath, _workerRuntime, _metricsLogger, attemptCount);
116+
var rpcWorkerChannel = _rpcWorkerChannelFactory.Create(_scriptOptions.RootScriptPath, _workerRuntime, _metricsLogger, attemptCount, _workerConfigs);
117117
rpcWorkerChannel.SetupFunctionInvocationBuffers(_functions);
118118
_jobHostLanguageWorkerChannelManager.AddChannel(rpcWorkerChannel);
119119
rpcWorkerChannel.StartWorkerProcessAsync().ContinueWith(workerInitTask =>
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System.Collections.Generic;
45
using Microsoft.Azure.WebJobs.Script.Diagnostics;
56

67
namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc
78
{
89
public interface IRpcWorkerChannelFactory
910
{
10-
IRpcWorkerChannel Create(string scriptRootPath, string language, IMetricsLogger metricsLogger, int attemptCount);
11+
IRpcWorkerChannel Create(string scriptRootPath, string language, IMetricsLogger metricsLogger, int attemptCount, IEnumerable<RpcWorkerConfig> workerConfigs);
1112
}
1213
}

src/WebJobs.Script/Workers/Rpc/RpcWorkerChannel.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ internal RpcWorkerChannel(
112112

113113
internal IWorkerProcess WorkerProcess => _rpcWorkerProcess;
114114

115+
internal RpcWorkerConfig Config => _workerConfig;
116+
115117
public bool IsChannelReadyForInvocations()
116118
{
117119
return !_disposing && !_disposed && _state.HasFlag(RpcWorkerChannelState.InvocationBuffersInitialized | RpcWorkerChannelState.Initialized);

src/WebJobs.Script/Workers/Rpc/RpcWorkerChannelFactory.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using Microsoft.Azure.WebJobs.Script.Abstractions;
99
using Microsoft.Azure.WebJobs.Script.Diagnostics;
1010
using Microsoft.Azure.WebJobs.Script.Eventing;
11-
using Microsoft.Azure.WebJobs.Script.ManagedDependencies;
1211
using Microsoft.Extensions.Logging;
1312
using Microsoft.Extensions.Options;
1413

@@ -19,24 +18,22 @@ public class RpcWorkerChannelFactory : IRpcWorkerChannelFactory
1918
private readonly ILoggerFactory _loggerFactory = null;
2019
private readonly IRpcWorkerProcessFactory _rpcWorkerProcessFactory = null;
2120
private readonly IScriptEventManager _eventManager = null;
22-
private readonly IEnumerable<RpcWorkerConfig> _workerConfigs = null;
2321
private readonly IEnvironment _environment = null;
2422
private readonly IOptionsMonitor<ScriptApplicationHostOptions> _applicationHostOptions = null;
2523

26-
public RpcWorkerChannelFactory(IScriptEventManager eventManager, IEnvironment environment, IRpcServer rpcServer, ILoggerFactory loggerFactory, IOptions<LanguageWorkerOptions> languageWorkerOptions,
24+
public RpcWorkerChannelFactory(IScriptEventManager eventManager, IEnvironment environment, IRpcServer rpcServer, ILoggerFactory loggerFactory, IOptionsMonitor<LanguageWorkerOptions> languageWorkerOptions,
2725
IOptionsMonitor<ScriptApplicationHostOptions> applicationHostOptions, IRpcWorkerProcessFactory rpcWorkerProcessManager)
2826
{
2927
_eventManager = eventManager;
3028
_loggerFactory = loggerFactory;
31-
_workerConfigs = languageWorkerOptions.Value.WorkerConfigs;
3229
_rpcWorkerProcessFactory = rpcWorkerProcessManager;
3330
_environment = environment;
3431
_applicationHostOptions = applicationHostOptions;
3532
}
3633

37-
public IRpcWorkerChannel Create(string scriptRootPath, string runtime, IMetricsLogger metricsLogger, int attemptCount)
34+
public IRpcWorkerChannel Create(string scriptRootPath, string runtime, IMetricsLogger metricsLogger, int attemptCount, IEnumerable<RpcWorkerConfig> workerConfigs)
3835
{
39-
var languageWorkerConfig = _workerConfigs.Where(c => c.Description.Language.Equals(runtime, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
36+
var languageWorkerConfig = workerConfigs.Where(c => c.Description.Language.Equals(runtime, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
4037
if (languageWorkerConfig == null)
4138
{
4239
throw new InvalidOperationException($"WorkerCofig for runtime: {runtime} not found");

src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class WebHostRpcWorkerChannelManager : IWebHostRpcWorkerChannelManager
1919
private readonly ILogger _logger = null;
2020
private readonly TimeSpan workerInitTimeout = TimeSpan.FromSeconds(30);
2121
private readonly IOptionsMonitor<ScriptApplicationHostOptions> _applicationHostOptions = null;
22+
private readonly IOptionsMonitor<LanguageWorkerOptions> _lanuageworkerOptions = null;
2223
private readonly IScriptEventManager _eventManager = null;
2324
private readonly IEnvironment _environment;
2425
private readonly ILoggerFactory _loggerFactory = null;
@@ -29,7 +30,7 @@ public class WebHostRpcWorkerChannelManager : IWebHostRpcWorkerChannelManager
2930

3031
private ConcurrentDictionary<string, Dictionary<string, TaskCompletionSource<IRpcWorkerChannel>>> _workerChannels = new ConcurrentDictionary<string, Dictionary<string, TaskCompletionSource<IRpcWorkerChannel>>>(StringComparer.OrdinalIgnoreCase);
3132

32-
public WebHostRpcWorkerChannelManager(IScriptEventManager eventManager, IEnvironment environment, ILoggerFactory loggerFactory, IRpcWorkerChannelFactory rpcWorkerChannelFactory, IOptionsMonitor<ScriptApplicationHostOptions> applicationHostOptions, IMetricsLogger metricsLogger)
33+
public WebHostRpcWorkerChannelManager(IScriptEventManager eventManager, IEnvironment environment, ILoggerFactory loggerFactory, IRpcWorkerChannelFactory rpcWorkerChannelFactory, IOptionsMonitor<ScriptApplicationHostOptions> applicationHostOptions, IMetricsLogger metricsLogger, IOptionsMonitor<LanguageWorkerOptions> languageWorkerOptions)
3334
{
3435
_environment = environment ?? throw new ArgumentNullException(nameof(environment));
3536
_eventManager = eventManager;
@@ -38,6 +39,7 @@ public WebHostRpcWorkerChannelManager(IScriptEventManager eventManager, IEnviron
3839
_rpcWorkerChannelFactory = rpcWorkerChannelFactory;
3940
_logger = loggerFactory.CreateLogger<WebHostRpcWorkerChannelManager>();
4041
_applicationHostOptions = applicationHostOptions;
42+
_lanuageworkerOptions = languageWorkerOptions;
4143

4244
_shutdownStandbyWorkerChannels = ScheduleShutdownStandbyChannels;
4345
_shutdownStandbyWorkerChannels = _shutdownStandbyWorkerChannels.Debounce(milliseconds: 5000);
@@ -56,7 +58,7 @@ internal async Task<IRpcWorkerChannel> InitializeLanguageWorkerChannel(string ru
5658
_logger.LogDebug("Creating language worker channel for runtime:{runtime}", runtime);
5759
try
5860
{
59-
rpcWorkerChannel = _rpcWorkerChannelFactory.Create(scriptRootPath, runtime, _metricsLogger, 0);
61+
rpcWorkerChannel = _rpcWorkerChannelFactory.Create(scriptRootPath, runtime, _metricsLogger, 0, _lanuageworkerOptions.CurrentValue.WorkerConfigs);
6062
AddOrUpdateWorkerChannels(runtime, rpcWorkerChannel);
6163
await rpcWorkerChannel.StartWorkerProcessAsync().ContinueWith(processStartTask =>
6264
{

test/WebJobs.Script.Tests.Integration/Host/StandbyManager/StandbyManagerE2ETests_Windows.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,4 +236,4 @@ await TestHelpers.Await(() =>
236236
Assert.Equal(0, result.Count());
237237
}
238238
}
239-
}
239+
}

test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/SpecializationE2ETests.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ public async Task Specialization_ResetsSharedLoadContext()
221221
}
222222

223223
[Fact]
224-
public async Task StartAsync_SetsCorrectActiveHost()
224+
public async Task StartAsync_SetsCorrectActiveHost_RefreshesLanguageWorkerOptions()
225225
{
226226
var builder = CreateStandbyHostBuilder();
227227

@@ -231,27 +231,40 @@ public async Task StartAsync_SetsCorrectActiveHost()
231231
Task ignore = Task.Delay(3000).ContinueWith(_ => _pauseAfterStandbyHostBuild.Release());
232232

233233
IWebHost host = builder.Build();
234-
var manager = host.Services.GetService<WebJobsScriptHostService>();
234+
var scriptHostService = host.Services.GetService<WebJobsScriptHostService>();
235+
var channelFactory = host.Services.GetService<IRpcWorkerChannelFactory>();
236+
var workerOptionsPlaceholderMode = host.Services.GetService<IOptions<LanguageWorkerOptions>>();
237+
Assert.Equal(4, workerOptionsPlaceholderMode.Value.WorkerConfigs.Count);
238+
var rpcChannelInPlaceholderMode = (RpcWorkerChannel)channelFactory.Create("/", "powershell", null, 0, workerOptionsPlaceholderMode.Value.WorkerConfigs);
239+
Assert.Equal("6", rpcChannelInPlaceholderMode.Config.Description.DefaultRuntimeVersion);
240+
235241

236242
// TestServer will block in the constructor so pull out the StandbyManager and use it
237243
// directly for this test.
238244
var standbyManager = host.Services.GetService<IStandbyManager>();
239245

240-
var standbyStart = Task.Run(async () => await manager.StartAsync(CancellationToken.None));
246+
var standbyStart = Task.Run(async () => await scriptHostService.StartAsync(CancellationToken.None));
241247

242248
// Wait until we've completed the build once. The standby host is built and now waiting for
243249
// _pauseAfterHostBuild to release it.
244250
await TestHelpers.Await(() => _buildCount.CurrentCount == 1);
245251

246252
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteContainerReady, "1");
247-
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");
253+
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");
254+
_environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName, "powershell");
255+
_environment.SetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeVersionSettingName, "7");
248256

249257
var specializeTask = Task.Run(async () => await standbyManager.SpecializeHostAsync());
250258

251259
await Task.WhenAll(standbyStart, specializeTask);
252260

253-
var options = manager.Services.GetService<IOptions<ScriptJobHostOptions>>();
261+
var options = scriptHostService.Services.GetService<IOptions<ScriptJobHostOptions>>();
254262
Assert.Equal(_specializedScriptRoot, options.Value.RootScriptPath);
263+
264+
var workerOptionsAtJobhostLevel = scriptHostService.Services.GetService<IOptions<LanguageWorkerOptions>>();
265+
Assert.Equal(1, workerOptionsAtJobhostLevel.Value.WorkerConfigs.Count);
266+
var rpcChannelAfterSpecialization = (RpcWorkerChannel)channelFactory.Create("/", "powershell", null, 0, workerOptionsAtJobhostLevel.Value.WorkerConfigs);
267+
Assert.Equal("7", rpcChannelAfterSpecialization.Config.Description.DefaultRuntimeVersion);
255268
}
256269

257270
private IWebHostBuilder CreateStandbyHostBuilder(params string[] functions)

0 commit comments

Comments
 (0)