1111using Microsoft . Azure . WebJobs . Script . Description ;
1212using Microsoft . Azure . WebJobs . Script . Diagnostics . Extensions ;
1313using Microsoft . Azure . WebJobs . Script . Workers . Rpc ;
14+ using Microsoft . Extensions . DependencyInjection ;
15+ using Microsoft . Extensions . Hosting ;
1416using Microsoft . Extensions . Logging ;
1517using Microsoft . Extensions . Options ;
1618using Newtonsoft . Json ;
1719using Newtonsoft . Json . Linq ;
1820
1921namespace Microsoft . Azure . WebJobs . Script
2022{
21- internal class WorkerFunctionMetadataProvider : IWorkerFunctionMetadataProvider
23+ internal class WorkerFunctionMetadataProvider : IWorkerFunctionMetadataProvider , IDisposable
2224 {
2325 private const string _metadataProviderName = "Worker" ;
2426 private readonly Dictionary < string , ICollection < string > > _functionErrors = new Dictionary < string , ICollection < string > > ( ) ;
@@ -30,6 +32,7 @@ internal class WorkerFunctionMetadataProvider : IWorkerFunctionMetadataProvider
3032 private readonly JsonSerializerSettings _dateTimeSerializerSettings ;
3133 private string _workerRuntime ;
3234 private ImmutableArray < FunctionMetadata > _functions ;
35+ private IHost _currentJobHost = null ;
3336
3437 public WorkerFunctionMetadataProvider (
3538 IOptionsMonitor < ScriptApplicationHostOptions > scriptOptions ,
@@ -45,6 +48,8 @@ public WorkerFunctionMetadataProvider(
4548 _scriptHostManager = scriptHostManager ;
4649 _workerRuntime = _environment . GetEnvironmentVariable ( EnvironmentSettingNames . FunctionWorkerRuntime ) ;
4750 _dateTimeSerializerSettings = new JsonSerializerSettings { DateParseHandling = DateParseHandling . None } ;
51+
52+ _scriptHostManager . ActiveHostChanged += OnHostChanged ;
4853 }
4954
5055 public ImmutableDictionary < string , ImmutableArray < string > > FunctionErrors
@@ -83,19 +88,14 @@ public async Task<FunctionMetadataResult> GetFunctionMetadataAsync(IEnumerable<R
8388 // Start up GRPC channels if they are not already running.
8489 if ( channels ? . Any ( ) != true )
8590 {
86- if ( _scriptHostManager . State is ScriptHostState . Default
87- || _scriptHostManager . State is ScriptHostState . Starting
88- || _scriptHostManager . State is ScriptHostState . Initialized )
91+ if ( IsJobHostStarting ( ) )
8992 {
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 ) ;
9294 await _channelManager . InitializeChannelAsync ( workerConfigs , _workerRuntime ) ;
9395 }
9496 else
9597 {
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 ) ;
9999 await _scriptHostManager . RestartHostAsync ( ) ;
100100 }
101101
@@ -149,6 +149,38 @@ public async Task<FunctionMetadataResult> GetFunctionMetadataAsync(IEnumerable<R
149149 return new FunctionMetadataResult ( useDefaultMetadataIndexing : false , _functions ) ;
150150 }
151151
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+
152184 internal void ValidateFunctionAppFormat ( string scriptPath , ILogger logger , IEnvironment environment , IFileSystem fileSystem = null )
153185 {
154186 fileSystem = fileSystem ?? FileUtility . Instance ;
@@ -298,5 +330,10 @@ private bool IsNullOrEmpty(IEnumerable<RawFunctionMetadata> functions)
298330 }
299331 return false ;
300332 }
333+
334+ public void Dispose ( )
335+ {
336+ _scriptHostManager . ActiveHostChanged -= OnHostChanged ;
337+ }
301338 }
302339}
0 commit comments