11
11
using Microsoft . Azure . WebJobs . Script . Description ;
12
12
using Microsoft . Azure . WebJobs . Script . Diagnostics . Extensions ;
13
13
using Microsoft . Azure . WebJobs . Script . Workers . Rpc ;
14
+ using Microsoft . Extensions . DependencyInjection ;
15
+ using Microsoft . Extensions . Hosting ;
14
16
using Microsoft . Extensions . Logging ;
15
17
using Microsoft . Extensions . Options ;
16
18
using Newtonsoft . Json ;
17
19
using Newtonsoft . Json . Linq ;
18
20
19
21
namespace Microsoft . Azure . WebJobs . Script
20
22
{
21
- internal class WorkerFunctionMetadataProvider : IWorkerFunctionMetadataProvider
23
+ internal class WorkerFunctionMetadataProvider : IWorkerFunctionMetadataProvider , IDisposable
22
24
{
23
25
private const string _metadataProviderName = "Worker" ;
24
26
private readonly Dictionary < string , ICollection < string > > _functionErrors = new Dictionary < string , ICollection < string > > ( ) ;
@@ -30,6 +32,7 @@ internal class WorkerFunctionMetadataProvider : IWorkerFunctionMetadataProvider
30
32
private readonly JsonSerializerSettings _dateTimeSerializerSettings ;
31
33
private string _workerRuntime ;
32
34
private ImmutableArray < FunctionMetadata > _functions ;
35
+ private IHost _currentJobHost = null ;
33
36
34
37
public WorkerFunctionMetadataProvider (
35
38
IOptionsMonitor < ScriptApplicationHostOptions > scriptOptions ,
@@ -45,6 +48,8 @@ public WorkerFunctionMetadataProvider(
45
48
_scriptHostManager = scriptHostManager ;
46
49
_workerRuntime = _environment . GetEnvironmentVariable ( EnvironmentSettingNames . FunctionWorkerRuntime ) ;
47
50
_dateTimeSerializerSettings = new JsonSerializerSettings { DateParseHandling = DateParseHandling . None } ;
51
+
52
+ _scriptHostManager . ActiveHostChanged += OnHostChanged ;
48
53
}
49
54
50
55
public ImmutableDictionary < string , ImmutableArray < string > > FunctionErrors
@@ -83,19 +88,14 @@ public async Task<FunctionMetadataResult> GetFunctionMetadataAsync(IEnumerable<R
83
88
// Start up GRPC channels if they are not already running.
84
89
if ( channels ? . Any ( ) != true )
85
90
{
86
- if ( _scriptHostManager . State is ScriptHostState . Default
87
- || _scriptHostManager . State is ScriptHostState . Starting
88
- || _scriptHostManager . State is ScriptHostState . Initialized )
91
+ if ( IsJobHostStarting ( ) )
89
92
{
90
- // We don't need to restart if the host hasn't even been created yet.
91
- _logger . LogDebug ( "Host is starting up, initializing language worker channel" ) ;
93
+ _logger . LogDebug ( "JobHost is starting with state '{State}'. Initializing worker channel." , _scriptHostManager . State ) ;
92
94
await _channelManager . InitializeChannelAsync ( workerConfigs , _workerRuntime ) ;
93
95
}
94
96
else
95
97
{
96
- // During the restart flow, GetFunctionMetadataAsync gets invoked
97
- // again through a new script host initialization flow.
98
- _logger . LogDebug ( "Host is running without any initialized channels, restarting the JobHost." ) ;
98
+ _logger . LogDebug ( "JobHost has started and has state '{State}' without any worker channels. Restarting host to reinitialize." , _scriptHostManager . State ) ;
99
99
await _scriptHostManager . RestartHostAsync ( ) ;
100
100
}
101
101
@@ -149,6 +149,38 @@ public async Task<FunctionMetadataResult> GetFunctionMetadataAsync(IEnumerable<R
149
149
return new FunctionMetadataResult ( useDefaultMetadataIndexing : false , _functions ) ;
150
150
}
151
151
152
+ private void OnHostChanged ( object sender , ActiveHostChangedEventArgs args )
153
+ {
154
+ // Track the current host so we can get state later if needed.
155
+ _currentJobHost = args . NewHost ;
156
+ }
157
+
158
+ private bool IsJobHostStarting ( )
159
+ {
160
+ if ( _scriptHostManager . State is ScriptHostState . Default
161
+ || _scriptHostManager . State is ScriptHostState . Starting
162
+ || _scriptHostManager . State is ScriptHostState . Initialized )
163
+ {
164
+ return true ;
165
+ }
166
+
167
+ // The Error state can occur when the host is in a "final" state after completely starting,
168
+ // or during a retry of a transient error. This check allows us to determine the difference. If
169
+ // the host has not completely started, it means that it is still in the process of starting.
170
+ if ( _currentJobHost is not null && _scriptHostManager . State == ScriptHostState . Error )
171
+ {
172
+ var lifetime = _currentJobHost . Services ? . GetService < IHostApplicationLifetime > ( ) ;
173
+
174
+ if ( lifetime is not null &&
175
+ ! lifetime . ApplicationStarted . IsCancellationRequested )
176
+ {
177
+ return true ;
178
+ }
179
+ }
180
+
181
+ return false ;
182
+ }
183
+
152
184
internal void ValidateFunctionAppFormat ( string scriptPath , ILogger logger , IEnvironment environment , IFileSystem fileSystem = null )
153
185
{
154
186
fileSystem = fileSystem ?? FileUtility . Instance ;
@@ -298,5 +330,10 @@ private bool IsNullOrEmpty(IEnumerable<RawFunctionMetadata> functions)
298
330
}
299
331
return false ;
300
332
}
333
+
334
+ public void Dispose ( )
335
+ {
336
+ _scriptHostManager . ActiveHostChanged -= OnHostChanged ;
337
+ }
301
338
}
302
339
}
0 commit comments