44using System ;
55using System . Collections . Generic ;
66using System . IO ;
7+ using System . Threading ;
78using System . Threading . Tasks ;
89using Microsoft . Azure . WebJobs . Script . Config ;
910using Microsoft . Azure . WebJobs . Script . Eventing ;
@@ -15,21 +16,19 @@ namespace Microsoft.Azure.WebJobs.Script.WebHost
1516{
1617 public sealed class WebHostResolver : IDisposable
1718 {
19+ private static ScriptSettingsManager _settingsManager ;
20+ private static ILoggerFactory _emptyLoggerFactory = new LoggerFactory ( ) ;
1821 private static object _syncLock = new object ( ) ;
1922
2023 private readonly ISecretManagerFactory _secretManagerFactory ;
2124 private readonly IScriptEventManager _eventManager ;
2225 private ScriptHostConfiguration _standbyScriptHostConfig ;
2326 private WebScriptHostManager _standbyHostManager ;
2427 private WebHookReceiverManager _standbyReceiverManager ;
25-
2628 private ScriptHostConfiguration _activeScriptHostConfig ;
2729 private WebScriptHostManager _activeHostManager ;
2830 private WebHookReceiverManager _activeReceiverManager ;
29-
30- private static ScriptSettingsManager _settingsManager ;
31-
32- private static ILoggerFactory _emptyLoggerFactory = new LoggerFactory ( ) ;
31+ private Timer _specializationTimer ;
3332
3433 public WebHostResolver ( ScriptSettingsManager settingsManager , ISecretManagerFactory secretManagerFactory , IScriptEventManager eventManager )
3534 {
@@ -61,17 +60,7 @@ public ISwaggerDocumentManager GetSwaggerDocumentManager(WebHostSettings setting
6160
6261 public ScriptHostConfiguration GetScriptHostConfiguration ( WebHostSettings settings )
6362 {
64- if ( _activeScriptHostConfig != null )
65- {
66- return _activeScriptHostConfig ;
67- }
68-
69- lock ( _syncLock )
70- {
71- EnsureInitialized ( settings ) ;
72-
73- return _activeScriptHostConfig ?? _standbyScriptHostConfig ;
74- }
63+ return GetActiveInstance ( settings , ref _activeScriptHostConfig , ref _standbyScriptHostConfig ) ;
7564 }
7665
7766 public ISecretManager GetSecretManager ( WebHostSettings settings )
@@ -81,83 +70,79 @@ public ISecretManager GetSecretManager(WebHostSettings settings)
8170
8271 public WebScriptHostManager GetWebScriptHostManager ( WebHostSettings settings )
8372 {
84- if ( _activeHostManager != null )
85- {
86- return _activeHostManager ;
87- }
88-
89- lock ( _syncLock )
90- {
91- EnsureInitialized ( settings ) ;
92-
93- return _activeHostManager ?? _standbyHostManager ;
94- }
73+ return GetActiveInstance ( settings , ref _activeHostManager , ref _standbyHostManager ) ;
9574 }
9675
9776 public WebHookReceiverManager GetWebHookReceiverManager ( WebHostSettings settings )
9877 {
99- if ( _activeReceiverManager != null )
100- {
101- return _activeReceiverManager ;
102- }
103-
104- lock ( _syncLock )
105- {
106- EnsureInitialized ( settings ) ;
107-
108- return _activeReceiverManager ?? _standbyReceiverManager ;
109- }
78+ return GetActiveInstance ( settings , ref _activeReceiverManager , ref _standbyReceiverManager ) ;
11079 }
11180
81+ /// <summary>
82+ /// This method ensures that all services managed by this class are initialized
83+ /// correctly taking into account specialization state transitions.
84+ /// </summary>
11285 internal void EnsureInitialized ( WebHostSettings settings )
11386 {
114- if ( ! WebScriptHostManager . InStandbyMode )
87+ lock ( _syncLock )
11588 {
116- // standby mode can only change from true to false
117- // when standby mode changes, we reset all instances
118- if ( _activeHostManager == null )
89+ if ( ! WebScriptHostManager . InStandbyMode )
11990 {
120- _settingsManager . Reset ( ) ;
91+ // standby mode can only change from true to false
92+ // when standby mode changes, we reset all instances
93+ if ( _activeHostManager == null )
94+ {
95+ _settingsManager . Reset ( ) ;
96+ _specializationTimer ? . Dispose ( ) ;
97+ _specializationTimer = null ;
12198
122- _activeScriptHostConfig = CreateScriptHostConfiguration ( settings ) ;
123- _activeHostManager = new WebScriptHostManager ( _activeScriptHostConfig , _secretManagerFactory , _eventManager , _settingsManager , settings ) ;
124- _activeReceiverManager = new WebHookReceiverManager ( _activeHostManager . SecretManager ) ;
125- InitializeFileSystem ( ) ;
99+ _activeScriptHostConfig = CreateScriptHostConfiguration ( settings ) ;
100+ _activeHostManager = new WebScriptHostManager ( _activeScriptHostConfig , _secretManagerFactory , _eventManager , _settingsManager , settings ) ;
101+ _activeReceiverManager = new WebHookReceiverManager ( _activeHostManager . SecretManager ) ;
102+ InitializeFileSystem ( ) ;
126103
127- if ( _standbyHostManager != null )
128- {
129- // we're starting the one and only one
130- // standby mode specialization
131- _activeScriptHostConfig . TraceWriter . Info ( Resources . HostSpecializationTrace ) ;
132-
133- // After specialization, we need to ensure that custom timezone
134- // settings configured by the user (WEBSITE_TIME_ZONE) are honored.
135- // DateTime caches timezone information, so we need to clear the cache.
136- TimeZoneInfo . ClearCachedData ( ) ;
137- }
104+ if ( _standbyHostManager != null )
105+ {
106+ // we're starting the one and only one
107+ // standby mode specialization
108+ _activeScriptHostConfig . TraceWriter . Info ( Resources . HostSpecializationTrace ) ;
109+
110+ // After specialization, we need to ensure that custom timezone
111+ // settings configured by the user (WEBSITE_TIME_ZONE) are honored.
112+ // DateTime caches timezone information, so we need to clear the cache.
113+ TimeZoneInfo . ClearCachedData ( ) ;
114+ }
138115
139- if ( _standbyHostManager != null )
140- {
141- _standbyHostManager . Stop ( ) ;
142- _standbyHostManager . Dispose ( ) ;
116+ if ( _standbyHostManager != null )
117+ {
118+ _standbyHostManager . Stop ( ) ;
119+ _standbyHostManager . Dispose ( ) ;
120+ }
121+ _standbyReceiverManager ? . Dispose ( ) ;
122+ _standbyScriptHostConfig = null ;
123+ _standbyHostManager = null ;
124+ _standbyReceiverManager = null ;
143125 }
144- _standbyReceiverManager ? . Dispose ( ) ;
145- _standbyScriptHostConfig = null ;
146- _standbyHostManager = null ;
147- _standbyReceiverManager = null ;
148126 }
149- }
150- else
151- {
152- if ( _standbyHostManager == null )
127+ else
153128 {
154- var standbySettings = CreateStandbySettings ( settings ) ;
155- _standbyScriptHostConfig = CreateScriptHostConfiguration ( standbySettings , true ) ;
156- _standbyHostManager = new WebScriptHostManager ( _standbyScriptHostConfig , _secretManagerFactory , _eventManager , _settingsManager , standbySettings ) ;
157- _standbyReceiverManager = new WebHookReceiverManager ( _standbyHostManager . SecretManager ) ;
158-
159- InitializeFileSystem ( ) ;
160- StandbyManager . Initialize ( _standbyScriptHostConfig ) ;
129+ // we're in standby (placeholder) mode
130+ if ( _standbyHostManager == null )
131+ {
132+ var standbySettings = CreateStandbySettings ( settings ) ;
133+ _standbyScriptHostConfig = CreateScriptHostConfiguration ( standbySettings , true ) ;
134+ _standbyHostManager = new WebScriptHostManager ( _standbyScriptHostConfig , _secretManagerFactory , _eventManager , _settingsManager , standbySettings ) ;
135+ _standbyReceiverManager = new WebHookReceiverManager ( _standbyHostManager . SecretManager ) ;
136+
137+ InitializeFileSystem ( ) ;
138+ StandbyManager . Initialize ( _standbyScriptHostConfig ) ;
139+
140+ // start a background timer to identify when specialization happens
141+ // specialization usually happens via an http request (e.g. scale controller
142+ // ping) but this timer is started as well to handle cases where we
143+ // might not receive a request
144+ _specializationTimer = new Timer ( OnSpecializationTimerTick , settings , 1000 , 1000 ) ;
145+ }
161146 }
162147 }
163148 }
@@ -206,6 +191,37 @@ internal static ScriptHostConfiguration CreateScriptHostConfiguration(WebHostSet
206191 return scriptHostConfig ;
207192 }
208193
194+ /// <summary>
195+ /// Helper function used to manage active/standby transitions for objects managed
196+ /// by this class.
197+ /// </summary>
198+ private TInstance GetActiveInstance < TInstance > ( WebHostSettings settings , ref TInstance activeInstance , ref TInstance standbyInstance )
199+ {
200+ if ( activeInstance != null )
201+ {
202+ // if we have an active (specialized) instance, return it
203+ return activeInstance ;
204+ }
205+
206+ // we're either uninitialized or in standby mode
207+ // perform intitialization
208+ EnsureInitialized ( settings ) ;
209+
210+ if ( activeInstance != null )
211+ {
212+ return activeInstance ;
213+ }
214+ else
215+ {
216+ return standbyInstance ;
217+ }
218+ }
219+
220+ private void OnSpecializationTimerTick ( object state )
221+ {
222+ EnsureInitialized ( ( WebHostSettings ) state ) ;
223+ }
224+
209225 private static void InitializeFileSystem ( )
210226 {
211227 if ( ScriptSettingsManager . Instance . IsAzureEnvironment )
@@ -252,6 +268,7 @@ public void Dispose()
252268
253269 _activeHostManager ? . Dispose ( ) ;
254270 _activeReceiverManager ? . Dispose ( ) ;
271+ _specializationTimer ? . Dispose ( ) ;
255272 }
256273 }
257274}
0 commit comments