3
3
4
4
using System ;
5
5
using System . Collections . Generic ;
6
- using System . Linq ;
7
6
using System . Net . Http ;
8
- using System . Text ;
9
7
using System . Threading . Tasks ;
10
- using Microsoft . Azure . Storage ;
11
- using Microsoft . Azure . Storage . File ;
12
8
using Microsoft . Azure . WebJobs . Script . Diagnostics ;
13
- using Microsoft . Azure . WebJobs . Script . WebHost . Configuration ;
14
- using Microsoft . Azure . WebJobs . Script . WebHost . Management . LinuxSpecialization ;
15
9
using Microsoft . Azure . WebJobs . Script . WebHost . Models ;
16
10
using Microsoft . Extensions . Logging ;
17
- using Microsoft . Extensions . Options ;
18
- using Newtonsoft . Json ;
19
11
20
12
namespace Microsoft . Azure . WebJobs . Script . WebHost . Management
21
13
{
@@ -29,6 +21,7 @@ public abstract class LinuxInstanceManager : IInstanceManager
29
21
private readonly IEnvironment _environment ;
30
22
private readonly HttpClient _client ;
31
23
private readonly IScriptWebHostEnvironment _webHostEnvironment ;
24
+ private Task _assignment ;
32
25
33
26
private HostAssignmentContext _assignmentContext ;
34
27
@@ -44,70 +37,101 @@ public LinuxInstanceManager(IHttpClientFactory httpClientFactory, IScriptWebHost
44
37
45
38
public abstract Task < string > SpecializeMSISidecar ( HostAssignmentContext context ) ;
46
39
47
- public bool StartAssignment ( HostAssignmentContext context )
40
+ public async Task < bool > AssignInstanceAsync ( HostAssignmentContext context )
48
41
{
49
- if ( ! _webHostEnvironment . InStandbyMode )
42
+ if ( ! IsValidEnvironment ( context ) )
50
43
{
51
- // This is only true when specializing pinned containers.
52
- if ( ! context . Environment . TryGetValue ( EnvironmentSettingNames . ContainerStartContext , out string startContext ) )
44
+ return false ;
45
+ }
46
+
47
+ if ( context . IsWarmupRequest )
48
+ {
49
+ await HandleWarmupRequestAsync ( context ) ;
50
+ return true ;
51
+ }
52
+
53
+ lock ( _assignmentLock )
54
+ {
55
+ if ( _assignmentContext == null )
56
+ {
57
+ _assignmentContext = context ;
58
+ _assignment = AssignAsync ( context ) ;
59
+ }
60
+ else if ( ! _assignmentContext . Equals ( context ) )
53
61
{
54
- _logger . LogError ( "Assign called while host is not in placeholder mode and start context is not present." ) ;
55
62
return false ;
56
63
}
57
64
}
58
65
59
- if ( _environment . IsContainerReady ( ) )
66
+ await _assignment ;
67
+ return true ;
68
+ }
69
+
70
+ public bool StartAssignment ( HostAssignmentContext context )
71
+ {
72
+ if ( ! IsValidEnvironment ( context ) )
60
73
{
61
- _logger . LogError ( "Assign called while container is marked as specialized." ) ;
62
74
return false ;
63
75
}
64
76
65
77
if ( context . IsWarmupRequest )
66
78
{
67
- // Based on profiling download code jit-ing holds up cold start.
68
- // Pre-jit to avoid paying the cost later.
69
- Task . Run ( async ( ) => await DownloadWarmupAsync ( context . GetRunFromPkgContext ( ) ) ) ;
79
+ Task . Run ( async ( ) => await HandleWarmupRequestAsync ( context ) ) ;
70
80
return true ;
71
81
}
72
- else if ( _assignmentContext == null )
82
+
83
+ lock ( _assignmentLock )
73
84
{
74
- lock ( _assignmentLock )
85
+ if ( _assignmentContext != null )
75
86
{
76
- if ( _assignmentContext != null )
77
- {
78
- return _assignmentContext . Equals ( context ) ;
79
- }
80
- _assignmentContext = context ;
87
+ return _assignmentContext . Equals ( context ) ;
81
88
}
89
+ _assignmentContext = context ;
90
+ _assignment = AssignAsync ( context ) ;
91
+ }
82
92
83
- _logger . LogInformation ( $ "Starting Assignment. Cloud Name: { _environment . GetCloudName ( ) } ") ;
84
-
85
- // set a flag which will cause any incoming http requests to buffer
86
- // until specialization is complete
87
- // the host is guaranteed not to receive any requests until AFTER assign
88
- // has been initiated, so setting this flag here is sufficient to ensure
89
- // that any subsequent incoming requests while the assign is in progress
90
- // will be delayed until complete
91
- _webHostEnvironment . DelayRequests ( ) ;
93
+ return true ;
94
+ }
92
95
93
- // start the specialization process in the background
94
- Task . Run ( async ( ) => await AssignAsync ( context ) ) ;
96
+ public abstract Task < string > ValidateContext ( HostAssignmentContext assignmentContext ) ;
95
97
96
- return true ;
98
+ private bool IsValidEnvironment ( HostAssignmentContext context )
99
+ {
100
+ if ( ! _webHostEnvironment . InStandbyMode )
101
+ {
102
+ // This is only true when specializing pinned containers.
103
+ if ( ! context . Environment . TryGetValue ( EnvironmentSettingNames . ContainerStartContext , out string startContext ) )
104
+ {
105
+ _logger . LogError ( "Assign called while host is not in placeholder mode and start context is not present." ) ;
106
+ return false ;
107
+ }
97
108
}
98
- else
109
+
110
+ if ( _environment . IsContainerReady ( ) )
99
111
{
100
- // No lock needed here since _assignmentContext is not null when we are here
101
- return _assignmentContext . Equals ( context ) ;
112
+ _logger . LogError ( "Assign called while container is marked as specialized." ) ;
113
+ return false ;
102
114
}
103
- }
104
115
105
- public abstract Task < string > ValidateContext ( HostAssignmentContext assignmentContext ) ;
116
+ return true ;
117
+ }
106
118
107
119
private async Task AssignAsync ( HostAssignmentContext assignmentContext )
108
120
{
121
+ await Task . Yield ( ) ; // This may be called from within a lock. When AssignAsync is awaited, control flow will return to the caller and the lock will be released when it exits the lock scope.
122
+
109
123
try
110
124
{
125
+ _logger . LogInformation ( $ "Starting Assignment. Cloud Name: { _environment . GetCloudName ( ) } ") ;
126
+
127
+ // set a flag which will cause any incoming http requests to buffer
128
+ // until specialization is complete
129
+ // the host is guaranteed not to receive any requests until AFTER assign
130
+ // has been initiated, so setting this flag here is sufficient to ensure
131
+ // that any subsequent incoming requests while the assign is in progress
132
+ // will be delayed until complete
133
+ _webHostEnvironment . DelayRequests ( ) ;
134
+
111
135
// first make all environment and file system changes required for
112
136
// the host to be specialized
113
137
_logger . LogInformation ( "Applying {environmentCount} app setting(s)" , assignmentContext . Environment . Count ) ;
@@ -133,6 +157,21 @@ private async Task AssignAsync(HostAssignmentContext assignmentContext)
133
157
}
134
158
}
135
159
160
+ private async Task HandleWarmupRequestAsync ( HostAssignmentContext assignmentContext )
161
+ {
162
+ try
163
+ {
164
+ await DownloadWarmupAsync ( assignmentContext . GetRunFromPkgContext ( ) ) ;
165
+ }
166
+ catch ( Exception ex )
167
+ {
168
+ _logger . LogError ( ex , "Warmup download failed" ) ;
169
+ await _meshServiceClient . NotifyHealthEvent ( ContainerHealthEventType . Warning , GetType ( ) , "Warmup download failed" ) ;
170
+ throw ;
171
+ }
172
+ return ;
173
+ }
174
+
136
175
protected abstract Task ApplyContextAsync ( HostAssignmentContext assignmentContext ) ;
137
176
138
177
protected abstract Task < string > DownloadWarmupAsync ( RunFromPackageContext context ) ;
0 commit comments