@@ -31,15 +31,15 @@ internal class RpcFunctionInvocationDispatcher : IFunctionInvocationDispatcher
31
31
private readonly SemaphoreSlim _startWorkerProcessLock = new SemaphoreSlim ( 1 , 1 ) ;
32
32
private readonly TimeSpan _thresholdBetweenRestarts = TimeSpan . FromMinutes ( WorkerConstants . WorkerRestartErrorIntervalThresholdInMinutes ) ;
33
33
private readonly IOptions < WorkerConcurrencyOptions > _workerConcurrencyOptions ;
34
+ private readonly IEnumerable < RpcWorkerConfig > _workerConfigs ;
35
+ private readonly Lazy < Task < int > > _maxProcessCount ;
34
36
35
37
private IScriptEventManager _eventManager ;
36
- private IEnumerable < RpcWorkerConfig > _workerConfigs ;
37
38
private IWebHostRpcWorkerChannelManager _webHostLanguageWorkerChannelManager ;
38
39
private IJobHostRpcWorkerChannelManager _jobHostLanguageWorkerChannelManager ;
39
40
private IDisposable _workerErrorSubscription ;
40
41
private IDisposable _workerRestartSubscription ;
41
42
private ScriptJobHostOptions _scriptOptions ;
42
- private int _maxProcessCount ;
43
43
private IRpcFunctionInvocationDispatcherLoadBalancer _functionDispatcherLoadBalancer ;
44
44
private bool _disposed = false ;
45
45
private bool _disposing = false ;
@@ -71,12 +71,12 @@ public RpcFunctionInvocationDispatcher(IOptions<ScriptJobHostOptions> scriptHost
71
71
{
72
72
_metricsLogger = metricsLogger ;
73
73
_scriptOptions = scriptHostOptions . Value ;
74
- _environment = environment ;
74
+ _environment = environment ?? throw new ArgumentNullException ( nameof ( environment ) ) ;
75
75
_applicationLifetime = applicationLifetime ;
76
76
_webHostLanguageWorkerChannelManager = webHostLanguageWorkerChannelManager ;
77
77
_jobHostLanguageWorkerChannelManager = jobHostLanguageWorkerChannelManager ;
78
78
_eventManager = eventManager ;
79
- _workerConfigs = languageWorkerOptions . CurrentValue . WorkerConfigs ;
79
+ _workerConfigs = languageWorkerOptions ? . CurrentValue ? . WorkerConfigs ?? throw new ArgumentNullException ( nameof ( languageWorkerOptions ) ) ;
80
80
_managedDependencyOptions = managedDependencyOptions ?? throw new ArgumentNullException ( nameof ( managedDependencyOptions ) ) ;
81
81
_logger = loggerFactory . CreateLogger < RpcFunctionInvocationDispatcher > ( ) ;
82
82
_rpcWorkerChannelFactory = rpcWorkerChannelFactory ;
@@ -86,13 +86,13 @@ public RpcFunctionInvocationDispatcher(IOptions<ScriptJobHostOptions> scriptHost
86
86
_workerIndexing = Utility . CanWorkerIndex ( _workerConfigs , _environment ) ;
87
87
State = FunctionInvocationDispatcherState . Default ;
88
88
89
- _workerErrorSubscription = _eventManager . OfType < WorkerErrorEvent > ( )
90
- . Subscribe ( WorkerError ) ;
91
- _workerRestartSubscription = _eventManager . OfType < WorkerRestartEvent > ( )
92
- . Subscribe ( WorkerRestart ) ;
89
+ _workerErrorSubscription = _eventManager . OfType < WorkerErrorEvent > ( ) . Subscribe ( WorkerError ) ;
90
+ _workerRestartSubscription = _eventManager . OfType < WorkerRestartEvent > ( ) . Subscribe ( WorkerRestart ) ;
93
91
94
92
_shutdownStandbyWorkerChannels = ShutdownWebhostLanguageWorkerChannels ;
95
93
_shutdownStandbyWorkerChannels = _shutdownStandbyWorkerChannels . Debounce ( milliseconds : 5000 ) ;
94
+
95
+ _maxProcessCount = new Lazy < Task < int > > ( GetMaxProcessCount ) ;
96
96
}
97
97
98
98
public FunctionInvocationDispatcherState State { get ; private set ; }
@@ -101,12 +101,27 @@ public RpcFunctionInvocationDispatcher(IOptions<ScriptJobHostOptions> scriptHost
101
101
102
102
public IJobHostRpcWorkerChannelManager JobHostLanguageWorkerChannelManager => _jobHostLanguageWorkerChannelManager ;
103
103
104
- internal ConcurrentStack < WorkerErrorEvent > LanguageWorkerErrors => _languageWorkerErrors ;
104
+ internal Task < int > MaxProcessCount => _maxProcessCount . Value ;
105
105
106
- internal int MaxProcessCount => _maxProcessCount ;
106
+ internal ConcurrentStack < WorkerErrorEvent > LanguageWorkerErrors => _languageWorkerErrors ;
107
107
108
108
internal IWebHostRpcWorkerChannelManager WebHostLanguageWorkerChannelManager => _webHostLanguageWorkerChannelManager ;
109
109
110
+ private async Task < int > GetMaxProcessCount ( )
111
+ {
112
+ if ( _workerConcurrencyOptions != null && ! string . IsNullOrEmpty ( _workerRuntime ) )
113
+ {
114
+ var workerConfig = _workerConfigs . Where ( c => c . Description . Language . Equals ( _workerRuntime , StringComparison . InvariantCultureIgnoreCase ) )
115
+ . FirstOrDefault ( ) ;
116
+ if ( workerConfig != null )
117
+ {
118
+ return _environment . IsWorkerDynamicConcurrencyEnabled ( ) ? _workerConcurrencyOptions . Value . MaxWorkerCount : workerConfig . CountOptions . ProcessCount ;
119
+ }
120
+ }
121
+
122
+ return ( await GetAllWorkerChannelsAsync ( ) ) . Count ( ) ;
123
+ }
124
+
110
125
internal async Task InitializeJobhostLanguageWorkerChannelAsync ( )
111
126
{
112
127
await InitializeJobhostLanguageWorkerChannelAsync ( 0 ) ;
@@ -131,7 +146,7 @@ internal async Task InitializeJobhostLanguageWorkerChannelAsync(int attemptCount
131
146
private void SetFunctionDispatcherStateToInitializedAndLog ( )
132
147
{
133
148
State = FunctionInvocationDispatcherState . Initialized ;
134
- // Do not change this log message. Vs Code relies on this to figure out when to attach debuger to the worker process.
149
+ // Do not change this log message. Vs Code relies on this to figure out when to attach debugger to the worker process.
135
150
_logger . LogInformation ( "Worker process started and initialized." ) ;
136
151
}
137
152
@@ -170,7 +185,7 @@ private void StartWorkerProcesses(int startIndex, Func<Task> startAction, bool i
170
185
{
171
186
Task . Run ( async ( ) =>
172
187
{
173
- for ( var count = startIndex ; count < _maxProcessCount
188
+ for ( var count = startIndex ; count < ( await _maxProcessCount . Value )
174
189
&& ! _processStartCancellationToken . IsCancellationRequested ; count ++ )
175
190
{
176
191
if ( _environment . IsWorkerDynamicConcurrencyEnabled ( ) && count > 0 )
@@ -229,6 +244,7 @@ public async Task InitializeAsync(IEnumerable<FunctionMetadata> functions, Cance
229
244
}
230
245
231
246
var workerConfig = _workerConfigs . Where ( c => c . Description . Language . Equals ( _workerRuntime , StringComparison . InvariantCultureIgnoreCase ) ) . FirstOrDefault ( ) ;
247
+
232
248
if ( workerConfig == null && ( functions == null || functions . Count ( ) == 0 ) )
233
249
{
234
250
// Only throw if workerConfig is null AND some functions have been found.
@@ -239,16 +255,15 @@ public async Task InitializeAsync(IEnumerable<FunctionMetadata> functions, Cance
239
255
if ( ( functions == null || functions . Count ( ) == 0 ) && ! _workerIndexing )
240
256
{
241
257
// do not initialize function dispatcher if there are no functions, unless the worker is indexing
242
- _logger . LogDebug ( " RpcFunctionInvocationDispatcher received no functions") ;
258
+ _logger . LogDebug ( $ " { nameof ( RpcFunctionInvocationDispatcher ) } received no functions") ;
243
259
return ;
244
260
}
245
261
246
262
_functions = functions ?? new List < FunctionMetadata > ( ) ;
247
- _maxProcessCount = workerConfig . CountOptions . ProcessCount ;
248
263
_processStartupInterval = workerConfig . CountOptions . ProcessStartupInterval ;
249
264
_restartWait = workerConfig . CountOptions . ProcessRestartInterval ;
250
265
_shutdownTimeout = workerConfig . CountOptions . ProcessShutdownTimeout ;
251
- ErrorEventsThreshold = 3 * _maxProcessCount ;
266
+ ErrorEventsThreshold = 3 * ( await _maxProcessCount . Value ) ;
252
267
253
268
if ( Utility . IsSupportedRuntime ( _workerRuntime , _workerConfigs ) )
254
269
{
@@ -419,10 +434,11 @@ internal async Task<IEnumerable<IRpcWorkerChannel>> GetInitializedWorkerChannels
419
434
{
420
435
IEnumerable < IRpcWorkerChannel > workerChannels = await GetAllWorkerChannelsAsync ( ) ;
421
436
IEnumerable < IRpcWorkerChannel > initializedWorkers = workerChannels . Where ( ch => ch . IsChannelReadyForInvocations ( ) ) ;
422
- int workerCount = _environment . IsWorkerDynamicConcurrencyEnabled ( ) ? _workerConcurrencyOptions . Value . MaxWorkerCount : _maxProcessCount ;
437
+ int workerCount = await _maxProcessCount . Value ;
438
+
423
439
if ( initializedWorkers . Count ( ) > workerCount )
424
440
{
425
- throw new InvalidOperationException ( $ "Number of initialized language workers exceeded:{ initializedWorkers . Count ( ) } exceeded maxProcessCount: { _maxProcessCount } ") ;
441
+ throw new InvalidOperationException ( $ "Number of initialized language workers exceeded:{ initializedWorkers . Count ( ) } exceeded maxProcessCount: { workerCount } ") ;
426
442
}
427
443
428
444
return initializedWorkers ;
0 commit comments