Skip to content

Commit a0b23d3

Browse files
authored
Enable worker indexing by default (#9574)
1 parent 76f1208 commit a0b23d3

File tree

17 files changed

+306
-59
lines changed

17 files changed

+306
-59
lines changed

release_notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- Limit dotnet-isolated specialization to 64 bit host process (#9548)
2020
- Sending command line arguments to language workers with `functions-` prefix to prevent conflicts (#9514)
2121
- Adding code to simulate placeholder and specilization locally (#9618)
22+
- Enable worker indexing by default without the need for hosting config (#9574)
2223
- Delaying execution of `LogWorkerMetadata` method until after coldstart is done. (#9627)
2324
- Update PowerShell Worker 7.2 to 4.0.3070 [Release Note](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.3070)
2425
- Update PowerShell Worker 7.4 to 4.0.3030 [Release Note](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.3030)

sample/Node/host.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
{
22
"version": "2.0",
3+
"extensionBundle": {
4+
"id": "Microsoft.Azure.Functions.ExtensionBundle",
5+
"version": "[3.*, 4.0.0)"
6+
},
37
"watchDirectories": [ "Shared", "Test" ],
48
"healthMonitor": {
59
"enabled": true,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"bindings": [
3+
{
4+
"type": "httpTrigger",
5+
"name": "req",
6+
"direction": "in",
7+
"methods": [ "get" ]
8+
},
9+
{
10+
"type": "http",
11+
"name": "$return",
12+
"direction": "out"
13+
}
14+
]
15+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
var test = require('../Shared/test');
2+
3+
module.exports = function (context, req) {
4+
context.log('Node.js HTTP trigger function processed a request. Name=%s', req.query.name);
5+
6+
var headerValue = req.headers['test-header'];
7+
if (headerValue) {
8+
context.log('test-header=' + headerValue);
9+
}
10+
11+
var res;
12+
if (typeof req.query.name === 'undefined') {
13+
res = {
14+
status: 400,
15+
body: "Please pass a name on the query string",
16+
headers: {
17+
'Content-Type': 'text/plain'
18+
}
19+
};
20+
}
21+
else {
22+
res = {
23+
status: 200,
24+
body: test.greeting(req.query.name),
25+
headers: {
26+
'Content-Type': 'text/plain',
27+
'Shared-Module': test.timestamp
28+
}
29+
};
30+
}
31+
32+
context.done(null, res);
33+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
var timestamp = new Date().getTime();
2+
3+
module.exports = {
4+
timestamp: timestamp,
5+
greeting: function (name) {
6+
return 'Hello ' + name;
7+
}
8+
};

sample/NodeWithoutBundle/host.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"version": "2.0",
3+
"healthMonitor": {
4+
"enabled": true,
5+
"healthCheckInterval": "00:00:10",
6+
"healthCheckWindow": "00:02:00",
7+
"healthCheckThreshold": 6,
8+
"counterThreshold": 0.80
9+
},
10+
"functionTimeout": "00:00:10",
11+
"logging": {
12+
"fileLoggingMode": "always"
13+
},
14+
"extensions": {
15+
"sendGrid": {
16+
"from": "Azure Functions <[email protected]>"
17+
},
18+
"http": {
19+
"routePrefix": "api",
20+
"maxConcurrentRequests": 5,
21+
"maxOutstandingRequests": 30
22+
},
23+
"queues": {
24+
"visibilityTimeout": "00:00:10",
25+
"maxDequeueCount": 3
26+
},
27+
"eventHubs": {
28+
"maxBatchSize": 1000,
29+
"prefetchCount": 1000,
30+
"batchCheckpointFrequency": 1
31+
},
32+
"serviceBus": {
33+
"prefetchCount": 100,
34+
"messageHandlerOptions": {
35+
"maxConcurrentCalls": 32
36+
}
37+
}
38+
}
39+
}

src/WebJobs.Script/Config/FunctionsHostingConfigOptions.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ public bool DisableLinuxAppServiceExecutionDetails
8181
}
8282
}
8383

84+
/// <summary>
85+
/// Gets a value indicating whether the host should revert the worker shutdown behavior in the webhostworkerchannelmanager.
86+
/// </summary>
87+
public bool RevertWorkerShutdownBehaviour
88+
{
89+
get
90+
{
91+
return GetFeature(RpcWorkerConstants.RevertWorkerShutdownBehavior) == "1";
92+
}
93+
}
94+
8495
/// <summary>
8596
/// Gets feature by name.
8697
/// </summary>

src/WebJobs.Script/Utility.cs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,17 +1001,22 @@ public static void ValidateRetryOptions(RetryOptions
10011001
}
10021002
}
10031003

1004-
// EnableWorkerIndexing set through AzureWebjobsFeatuerFlag always take precdence
1005-
// if AzureWebjobsFeatuerFlag is not set then WORKER_INDEXING_ENABLED hosting config controls stamplevel enablement
1006-
// if WORKER_INDEXING_ENABLED is set and WORKER_INDEXING_DISABLED contains the customers app name worker indexing is then disabled for that customer only
1007-
// Also Worker indexing is disabled for Logic apps
1004+
// Worker indexing is disabled for Logic apps
1005+
// EnableWorkerIndexing set through AzureWebjobsFeatureFlag always take precedence
1006+
// If AzureWebjobsFeatureFlag is not set and
1007+
// WORKER_INDEXING_DISABLED contains the customers app name worker indexing is then disabled for that customer only
10081008
public static bool CanWorkerIndex(IEnumerable<RpcWorkerConfig> workerConfigs, IEnvironment environment, FunctionsHostingConfigOptions functionsHostingConfigOptions)
10091009
{
1010+
if (environment.IsLogicApp())
1011+
{
1012+
return false;
1013+
}
1014+
10101015
string appName = environment.GetAzureWebsiteUniqueSlotName();
1011-
bool workerIndexingEnabled = FeatureFlags.IsEnabled(ScriptConstants.FeatureFlagEnableWorkerIndexing, environment)
1012-
|| (functionsHostingConfigOptions.WorkerIndexingEnabled
1013-
&& !functionsHostingConfigOptions.WorkerIndexingDisabledApps.ToLowerInvariant().Split("|").Contains(appName)
1014-
&& !environment.IsLogicApp());
1016+
1017+
bool workerIndexingFeatureFlagSet = FeatureFlags.IsEnabled(ScriptConstants.FeatureFlagEnableWorkerIndexing, environment);
1018+
bool workerIndexingDisabledViaHostingConfig = functionsHostingConfigOptions.WorkerIndexingDisabledApps.ToLowerInvariant().Split("|").Contains(appName);
1019+
bool workerIndexingEnabled = workerIndexingFeatureFlagSet || !workerIndexingDisabledViaHostingConfig;
10151020

10161021
if (!workerIndexingEnabled)
10171022
{

src/WebJobs.Script/Workers/Rpc/RpcWorkerConstants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,6 @@ public static class RpcWorkerConstants
8484

8585
public const string WorkerIndexingEnabled = "WORKER_INDEXING_ENABLED";
8686
public const string WorkerIndexingDisabledApps = "WORKER_INDEXING_DISABLED_APPS";
87+
public const string RevertWorkerShutdownBehavior = "REVERT_WORKER_SHUTDOWN_BEHAVIOR";
8788
}
8889
}

src/WebJobs.Script/Workers/Rpc/WebHostRpcWorkerChannelManager.cs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
using System.Linq;
88
using System.Reactive.Linq;
99
using System.Threading.Tasks;
10+
using Microsoft.Azure.AppService.Proxy.Common.Extensions;
1011
using Microsoft.Azure.AppService.Proxy.Common.Infra;
12+
using Microsoft.Azure.AppService.Proxy.Runtime;
13+
using Microsoft.Azure.WebJobs.Script.Config;
1114
using Microsoft.Azure.WebJobs.Script.Diagnostics;
1215
using Microsoft.Azure.WebJobs.Script.Eventing;
1316
using Microsoft.Azure.WebJobs.Script.Workers.Profiles;
@@ -22,6 +25,7 @@ public class WebHostRpcWorkerChannelManager : IWebHostRpcWorkerChannelManager
2225
private readonly ILogger _logger = null;
2326
private readonly TimeSpan workerInitTimeout = TimeSpan.FromSeconds(30);
2427
private readonly IOptionsMonitor<ScriptApplicationHostOptions> _applicationHostOptions = null;
28+
private readonly IOptions<FunctionsHostingConfigOptions> _hostingConfigOptions;
2529
private readonly IScriptEventManager _eventManager = null;
2630
private readonly IEnvironment _environment;
2731
private readonly ILoggerFactory _loggerFactory = null;
@@ -41,7 +45,8 @@ public WebHostRpcWorkerChannelManager(IScriptEventManager eventManager,
4145
IOptionsMonitor<ScriptApplicationHostOptions> applicationHostOptions,
4246
IMetricsLogger metricsLogger,
4347
IConfiguration config,
44-
IWorkerProfileManager workerProfileManager)
48+
IWorkerProfileManager workerProfileManager,
49+
IOptions<FunctionsHostingConfigOptions> hostingConfigOptions)
4550
{
4651
_environment = environment ?? throw new ArgumentNullException(nameof(environment));
4752
_config = config ?? throw new ArgumentNullException(nameof(config));
@@ -52,6 +57,7 @@ public WebHostRpcWorkerChannelManager(IScriptEventManager eventManager,
5257
_rpcWorkerChannelFactory = rpcWorkerChannelFactory;
5358
_logger = loggerFactory.CreateLogger<WebHostRpcWorkerChannelManager>();
5459
_applicationHostOptions = applicationHostOptions;
60+
_hostingConfigOptions = hostingConfigOptions;
5561

5662
_shutdownStandbyWorkerChannels = ScheduleShutdownStandbyChannels;
5763
_shutdownStandbyWorkerChannels = _shutdownStandbyWorkerChannels.Debounce(milliseconds: 5000);
@@ -228,9 +234,38 @@ public Task<bool> ShutdownChannelIfExistsAsync(string language, string workerId,
228234
{
229235
throw new ArgumentNullException(nameof(language));
230236
}
231-
if (_workerChannels.TryRemove(language, out Dictionary<string, TaskCompletionSource<IRpcWorkerChannel>> rpcWorkerChannels))
237+
238+
if (_hostingConfigOptions.Value.RevertWorkerShutdownBehaviour)
232239
{
233-
if (rpcWorkerChannels.TryGetValue(workerId, out TaskCompletionSource<IRpcWorkerChannel> value))
240+
if (_workerChannels.TryRemove(language, out Dictionary<string, TaskCompletionSource<IRpcWorkerChannel>> rpcWorkerChannels))
241+
{
242+
if (rpcWorkerChannels.TryGetValue(workerId, out TaskCompletionSource<IRpcWorkerChannel> value))
243+
{
244+
value?.Task.ContinueWith(channelTask =>
245+
{
246+
if (channelTask.Status == TaskStatus.Faulted)
247+
{
248+
_logger.LogDebug(channelTask.Exception, "Removing errored worker channel");
249+
}
250+
else
251+
{
252+
IRpcWorkerChannel workerChannel = channelTask.Result;
253+
if (workerChannel != null)
254+
{
255+
_logger.LogDebug("Disposing WebHost channel for workerId: {channelId}, for runtime:{language}", workerId, language);
256+
workerChannel.TryFailExecutions(workerException);
257+
(channelTask.Result as IDisposable)?.Dispose();
258+
}
259+
}
260+
});
261+
return Task.FromResult(true);
262+
}
263+
}
264+
}
265+
else
266+
{
267+
if (_workerChannels.TryGetValue(language, out Dictionary<string, TaskCompletionSource<IRpcWorkerChannel>> rpcWorkerChannels)
268+
&& rpcWorkerChannels.TryRemove(workerId, out TaskCompletionSource<IRpcWorkerChannel> value))
234269
{
235270
value?.Task.ContinueWith(channelTask =>
236271
{
@@ -252,6 +287,7 @@ public Task<bool> ShutdownChannelIfExistsAsync(string language, string workerId,
252287
return Task.FromResult(true);
253288
}
254289
}
290+
255291
return Task.FromResult(false);
256292
}
257293

0 commit comments

Comments
 (0)