4
4
using System ;
5
5
using System . Collections . Generic ;
6
6
using System . IO ;
7
+ using System . Threading ;
7
8
using System . Threading . Tasks ;
8
9
using Microsoft . Azure . WebJobs . Script . Config ;
9
10
using Microsoft . Azure . WebJobs . Script . Eventing ;
@@ -15,21 +16,19 @@ namespace Microsoft.Azure.WebJobs.Script.WebHost
15
16
{
16
17
public sealed class WebHostResolver : IDisposable
17
18
{
19
+ private static ScriptSettingsManager _settingsManager ;
20
+ private static ILoggerFactory _emptyLoggerFactory = new LoggerFactory ( ) ;
18
21
private static object _syncLock = new object ( ) ;
19
22
20
23
private readonly ISecretManagerFactory _secretManagerFactory ;
21
24
private readonly IScriptEventManager _eventManager ;
22
25
private ScriptHostConfiguration _standbyScriptHostConfig ;
23
26
private WebScriptHostManager _standbyHostManager ;
24
27
private WebHookReceiverManager _standbyReceiverManager ;
25
-
26
28
private ScriptHostConfiguration _activeScriptHostConfig ;
27
29
private WebScriptHostManager _activeHostManager ;
28
30
private WebHookReceiverManager _activeReceiverManager ;
29
-
30
- private static ScriptSettingsManager _settingsManager ;
31
-
32
- private static ILoggerFactory _emptyLoggerFactory = new LoggerFactory ( ) ;
31
+ private Timer _specializationTimer ;
33
32
34
33
public WebHostResolver ( ScriptSettingsManager settingsManager , ISecretManagerFactory secretManagerFactory , IScriptEventManager eventManager )
35
34
{
@@ -61,17 +60,7 @@ public ISwaggerDocumentManager GetSwaggerDocumentManager(WebHostSettings setting
61
60
62
61
public ScriptHostConfiguration GetScriptHostConfiguration ( WebHostSettings settings )
63
62
{
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 ) ;
75
64
}
76
65
77
66
public ISecretManager GetSecretManager ( WebHostSettings settings )
@@ -81,83 +70,79 @@ public ISecretManager GetSecretManager(WebHostSettings settings)
81
70
82
71
public WebScriptHostManager GetWebScriptHostManager ( WebHostSettings settings )
83
72
{
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 ) ;
95
74
}
96
75
97
76
public WebHookReceiverManager GetWebHookReceiverManager ( WebHostSettings settings )
98
77
{
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 ) ;
110
79
}
111
80
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>
112
85
internal void EnsureInitialized ( WebHostSettings settings )
113
86
{
114
- if ( ! WebScriptHostManager . InStandbyMode )
87
+ lock ( _syncLock )
115
88
{
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 )
119
90
{
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 ;
121
98
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 ( ) ;
126
103
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
+ }
138
115
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 ;
143
125
}
144
- _standbyReceiverManager ? . Dispose ( ) ;
145
- _standbyScriptHostConfig = null ;
146
- _standbyHostManager = null ;
147
- _standbyReceiverManager = null ;
148
126
}
149
- }
150
- else
151
- {
152
- if ( _standbyHostManager == null )
127
+ else
153
128
{
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
+ }
161
146
}
162
147
}
163
148
}
@@ -206,6 +191,37 @@ internal static ScriptHostConfiguration CreateScriptHostConfiguration(WebHostSet
206
191
return scriptHostConfig ;
207
192
}
208
193
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
+
209
225
private static void InitializeFileSystem ( )
210
226
{
211
227
if ( ScriptSettingsManager . Instance . IsAzureEnvironment )
@@ -252,6 +268,7 @@ public void Dispose()
252
268
253
269
_activeHostManager ? . Dispose ( ) ;
254
270
_activeReceiverManager ? . Dispose ( ) ;
271
+ _specializationTimer ? . Dispose ( ) ;
255
272
}
256
273
}
257
274
}
0 commit comments