@@ -82,53 +82,48 @@ private static bool ModifyEnvironmentVariablesWithRetry(
82
82
// If the IIS host config is being modified, this may fail
83
83
// We retry multiple times, as the final update is atomic
84
84
// We could consider adding backoff here, but it's not clear that it's necessary
85
- var attempt = 0 ;
86
- while ( attempt < 3 )
85
+ HashSet < string > appPoolsWeMustReenableRecycling = [ ] ;
86
+ if ( ! Retry ( log , "disable app pool recycling" , ( ) => DisableRecycling ( log , out appPoolsWeMustReenableRecycling ) ) )
87
87
{
88
- attempt ++ ;
89
- if ( attempt > 1 )
90
- {
91
- log . WriteInfo ( $ "Attempt { attempt } to update IIS failed, retrying.") ;
92
- }
93
-
94
- if ( ModifyEnvironmentVariables ( log , envVars , updateEnvVars ) )
95
- {
96
- return true ;
97
- }
88
+ // We failed to disable recycling, so we can't do anything more
89
+ return false ;
98
90
}
99
91
100
- log . WriteError ( $ "Failed to update IIS after { attempt } attempts") ;
101
- return false ;
102
- }
92
+ var success = Retry ( log , "update app pool environment variables" , ( ) => SetEnvironmentVariables ( log , envVars , updateEnvVars ) ) ;
103
93
104
- private static bool ModifyEnvironmentVariables (
105
- ILogger log ,
106
- ReadOnlyDictionary < string , string > envVars ,
107
- Action < ILogger , ConfigurationElementCollection , ReadOnlyDictionary < string , string > > updateEnvVars )
108
- {
109
- if ( ! SetEnvironmentVariables ( log , envVars , updateEnvVars , out var appPoolsWeMustReenableRecycling ) )
94
+ // We must try to re-enable recycling even if we failed to set the environment variables
95
+ return Retry ( log , "re-enable app pool recycling" , ( ) => ReEnableRecycling ( log , appPoolsWeMustReenableRecycling ) ) && success ;
96
+
97
+ static bool Retry ( ILogger log , string actionName , Func < bool > action )
110
98
{
111
- // If we failed to set the environment variables, we don't need to re-enable recycling
112
- // because by definition we can't have saved successfully
99
+ var attempt = 0 ;
100
+ while ( attempt < 3 )
101
+ {
102
+ attempt ++ ;
103
+ if ( attempt > 1 )
104
+ {
105
+ log . WriteInfo ( $ "Attempt { attempt } to { actionName } failed, retrying.") ;
106
+ }
107
+
108
+ if ( action ( ) )
109
+ {
110
+ return true ;
111
+ }
112
+ }
113
+
114
+ log . WriteError ( $ "Failed to { actionName } after { attempt } attempts") ;
113
115
return false ;
114
116
}
115
-
116
- // We do this separately, because we have to do all the work again no matter what we do
117
- return ReEnableRecycling ( log , appPoolsWeMustReenableRecycling ) ;
118
117
}
119
118
120
119
private static bool SetEnvironmentVariables (
121
120
ILogger log ,
122
121
ReadOnlyDictionary < string , string > envVars ,
123
- Action < ILogger , ConfigurationElementCollection , ReadOnlyDictionary < string , string > > updateEnvVars ,
124
- out HashSet < string > appPoolsWeMustReenableRecycling )
122
+ Action < ILogger , ConfigurationElementCollection , ReadOnlyDictionary < string , string > > updateEnvVars )
125
123
{
126
- appPoolsWeMustReenableRecycling = [ ] ;
127
-
128
124
try
129
125
{
130
126
using var serverManager = new ServerManager ( ) ;
131
- appPoolsWeMustReenableRecycling = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
132
127
133
128
var appPoolsSection = GetApplicationPoolsSection ( log , serverManager ) ;
134
129
if ( appPoolsSection is null )
@@ -157,6 +152,46 @@ private static bool SetEnvironmentVariables(
157
152
}
158
153
159
154
log . WriteInfo ( $ "Updating app pool '{ poolName } ' environment variables") ;
155
+ updateEnvVars ( log , appPoolElement . GetCollection ( "environmentVariables" ) , envVars ) ;
156
+ }
157
+ }
158
+
159
+ log . WriteInfo ( "Saving applicationHost.config" ) ;
160
+ serverManager . CommitChanges ( ) ;
161
+ return true ;
162
+ }
163
+ catch ( Exception ex )
164
+ {
165
+ log . WriteError ( ex , "Error updating application pools" ) ;
166
+ return false ;
167
+ }
168
+ }
169
+
170
+ private static bool DisableRecycling ( ILogger log , out HashSet < string > appPoolsWeMustReenableRecycling )
171
+ {
172
+ try
173
+ {
174
+ log . WriteInfo ( "Temporarily disabling app pool recycling" ) ;
175
+ using var serverManager = new ServerManager ( ) ;
176
+ appPoolsWeMustReenableRecycling = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
177
+
178
+ var appPoolsSection = GetApplicationPoolsSection ( log , serverManager ) ;
179
+ if ( appPoolsSection is null )
180
+ {
181
+ return false ;
182
+ }
183
+
184
+ foreach ( var appPoolElement in appPoolsSection . Value . AppPools )
185
+ {
186
+ if ( string . Equals ( appPoolElement . ElementTagName , "add" , StringComparison . OrdinalIgnoreCase ) )
187
+ {
188
+ // An app pool element
189
+ if ( appPoolElement . GetAttributeValue ( "name" ) is not string poolName )
190
+ {
191
+ // poolName can never be null, if it is, weirdness is afoot, so bail out
192
+ log . WriteInfo ( "Found app pool element without a name, skipping" ) ;
193
+ continue ;
194
+ }
160
195
161
196
// disable recycling of the pool, so that we don't force a restart when we update the pool
162
197
// we can't distinguish between "not set" and "set to false", but we only really care about
@@ -171,9 +206,6 @@ private static bool SetEnvironmentVariables(
171
206
appPoolsWeMustReenableRecycling . Add ( poolName ) ;
172
207
appPoolElement . GetChildElement ( "recycling" ) [ "disallowRotationOnConfigChange" ] = true ;
173
208
}
174
-
175
- // Set the pool-specific env variables
176
- updateEnvVars ( log , appPoolElement . GetCollection ( "environmentVariables" ) , envVars ) ;
177
209
}
178
210
}
179
211
@@ -183,7 +215,8 @@ private static bool SetEnvironmentVariables(
183
215
}
184
216
catch ( Exception ex )
185
217
{
186
- log . WriteError ( ex , $ "Error updating application pools") ;
218
+ appPoolsWeMustReenableRecycling = [ ] ;
219
+ log . WriteError ( ex , "Error updating application pools" ) ;
187
220
return false ;
188
221
}
189
222
}
0 commit comments