Skip to content

Commit 1768ee5

Browse files
committed
Merge branch 'release/3.0' into v3.x
2 parents cc2da60 + bebb280 commit 1768ee5

File tree

13 files changed

+204
-22
lines changed

13 files changed

+204
-22
lines changed

src/WebJobs.Script.WebHost/Diagnostics/LinuxContainerEventGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ private void ConsoleWriter(string evt)
104104

105105
public override void LogAzureMonitorDiagnosticLogEvent(LogLevel level, string resourceId, string operationName, string category, string regionName, string properties)
106106
{
107-
_writeEvent($"{ScriptConstants.LinuxAzureMonitorEventStreamName} {(int)ToEventLevel(level)},{resourceId},{operationName},{category},{regionName},{NormalizeString(properties)},{_containerName},{TenantId},{DateTime.UtcNow.ToString()}");
107+
// _writeEvent($"{ScriptConstants.LinuxAzureMonitorEventStreamName} {(int)ToEventLevel(level)},{resourceId},{operationName},{category},{regionName},{NormalizeString(properties)},{_containerName},{TenantId},{DateTime.UtcNow.ToString()}");
108108
}
109109
}
110110
}

src/WebJobs.Script/Description/DotNet/FunctionAssemblyLoadContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@ private static bool IsMinorMatchOrLowerPolicyEvaluator(AssemblyName requestedAss
155155
{
156156
AssemblyName runtimeAssemblyName = AssemblyNameCache.GetName(runtimeAssembly);
157157

158-
return requestedAssembly.Version.Major == runtimeAssemblyName.Version.Major &&
159-
requestedAssembly.Version.Minor <= runtimeAssemblyName.Version.Minor;
158+
return requestedAssembly.Version == null || (requestedAssembly.Version.Major == runtimeAssemblyName.Version.Major &&
159+
requestedAssembly.Version.Minor <= runtimeAssemblyName.Version.Minor);
160160
}
161161

162162
private bool IsRuntimeAssembly(AssemblyName assemblyName)

src/WebJobs.Script/Description/Workers/WorkerFunctionInvoker.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,11 @@ protected override async Task<object> InvokeCore(object[] parameters, FunctionIn
9090

9191
private async Task DelayUntilFunctionDispatcherInitializedOrShutdown()
9292
{
93-
if (_functionDispatcher != null && _functionDispatcher.State == FunctionInvocationDispatcherState.Initializing)
93+
// Don't delay if functionDispatcher is already initialized OR is skipping initialization for one of
94+
// these reasons: started in placeholder, has no functions, functions do not match set language.
95+
bool ready = _functionDispatcher.State == FunctionInvocationDispatcherState.Initialized || _functionDispatcher.State == FunctionInvocationDispatcherState.Default;
96+
97+
if (!ready)
9498
{
9599
_logger.LogDebug($"functionDispatcher state: {_functionDispatcher.State}");
96100
bool result = await Utility.DelayAsync((_functionDispatcher.ErrorEventsThreshold + 1) * WorkerConstants.ProcessStartTimeoutSeconds, WorkerConstants.WorkerReadyCheckPollingIntervalMilliseconds, () =>

src/WebJobs.Script/Host/ScriptHost.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -276,13 +276,16 @@ public async Task InitializeAsync(CancellationToken cancellationToken = default)
276276
{
277277
string runtimeStack = _workerRuntime;
278278

279-
// Appending the runtime version is currently only enabled for linux consumption. This will be eventually enabled for
280-
// Windows Consumption as well.
281-
string runtimeVersion = _environment.GetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeVersionSettingName);
282-
283-
if (!string.IsNullOrEmpty(runtimeVersion))
279+
if (!string.IsNullOrEmpty(_environment.GetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeSettingName)))
284280
{
285-
runtimeStack = string.Concat(runtimeStack, "-", runtimeVersion);
281+
// Appending the runtime version is currently only enabled for linux consumption. This will be eventually enabled for
282+
// Windows Consumption as well.
283+
string runtimeVersion = _environment.GetEnvironmentVariable(RpcWorkerConstants.FunctionWorkerRuntimeVersionSettingName);
284+
285+
if (!string.IsNullOrEmpty(runtimeVersion))
286+
{
287+
runtimeStack = string.Concat(runtimeStack, "-", runtimeVersion);
288+
}
286289
}
287290

288291
_metricsLogger.LogEvent(string.Format(MetricEventNames.HostStartupRuntimeLanguage, runtimeStack));

src/WebJobs.Script/Workers/FunctionInvocationDispatcherState.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,30 @@ public enum FunctionInvocationDispatcherState
1111
Default,
1212

1313
/// <summary>
14-
/// The FunctionDispatcherState is starting.
14+
/// The FunctionDispatcher is starting.
1515
/// </summary>
1616
Initializing,
1717

1818
/// <summary>
19-
/// The FunctionDispatcherState has been fully initialized and can accept direct function
20-
/// invocations. All functions have been indexed. Listeners may not yet
21-
/// be not yet running.
19+
/// The FunctionDispatcher has been fully initialized and can accept function
20+
/// invocations. All functions have been indexed. At least one worker process is initialized.
2221
/// </summary>
23-
Initialized
22+
Initialized,
23+
24+
/// <summary>
25+
/// The FunctionDispatcher was previously "Initialized" but no longer has any initialized
26+
/// worker processes to handle function invocations.
27+
/// </summary>
28+
WorkerProcessRestarting,
29+
30+
/// <summary>
31+
/// The FunctionDispatcher is disposing
32+
/// </summary>
33+
Disposing,
34+
35+
/// <summary>
36+
/// The FunctionDispatcher is disposed
37+
/// </summary>
38+
Disposed
2439
}
2540
}

src/WebJobs.Script/Workers/Http/HttpFunctionInvocationDispatcher.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public async void WorkerError(HttpWorkerErrorEvent workerError)
103103
{
104104
if (!_disposing)
105105
{
106-
_logger.LogDebug("Handling WorkerErrorEvent for workerId:{workerId}", workerError.WorkerId);
106+
_logger.LogDebug("Handling WorkerErrorEvent for workerId:{workerId}. Failed with: {exception}", workerError.WorkerId, workerError.Exception);
107107
AddOrUpdateErrorBucket(workerError);
108108
await DisposeAndRestartWorkerChannel(workerError.WorkerId);
109109
}
@@ -120,6 +120,9 @@ public async void WorkerRestart(HttpWorkerRestartEvent workerRestart)
120120

121121
private async Task DisposeAndRestartWorkerChannel(string workerId)
122122
{
123+
// Since we only have one HTTP worker process, as soon as we dispose it, InvokeAsync will fail. Set state to
124+
// indicate we are not ready to receive new requests.
125+
State = FunctionInvocationDispatcherState.WorkerProcessRestarting;
123126
_logger.LogDebug("Disposing channel for workerId: {channelId}", workerId);
124127
if (_httpWorkerChannel != null)
125128
{
@@ -166,13 +169,15 @@ protected virtual void Dispose(bool disposing)
166169
_workerErrorSubscription.Dispose();
167170
_workerRestartSubscription.Dispose();
168171
(_httpWorkerChannel as IDisposable)?.Dispose();
172+
State = FunctionInvocationDispatcherState.Disposed;
169173
_disposed = true;
170174
}
171175
}
172176

173177
public void Dispose()
174178
{
175179
_disposing = true;
180+
State = FunctionInvocationDispatcherState.Disposing;
176181
Dispose(true);
177182
}
178183

src/WebJobs.Script/Workers/Rpc/FunctionRegistration/RpcFunctionInvocationDispatcher.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ public async void WorkerError(WorkerErrorEvent workerError)
284284
{
285285
if (string.Equals(_workerRuntime, workerError.Language))
286286
{
287-
_logger.LogDebug("Handling WorkerErrorEvent for runtime:{runtime}, workerId:{workerId}", workerError.Language, workerError.WorkerId);
287+
_logger.LogDebug("Handling WorkerErrorEvent for runtime:{runtime}, workerId:{workerId}. Failed with: {exception}", workerError.Language, _workerRuntime, workerError.Exception);
288288
AddOrUpdateErrorBucket(workerError);
289289
await DisposeAndRestartWorkerChannel(workerError.Language, workerError.WorkerId);
290290
}
@@ -332,8 +332,16 @@ private async Task DisposeAndRestartWorkerChannel(string runtime, string workerI
332332

333333
if (ShouldRestartWorkerChannel(runtime, isWebHostChannel, isJobHostChannel))
334334
{
335+
// Set state to "WorkerProcessRestarting" if there are no other workers to handle work
336+
if ((await GetInitializedWorkerChannelsAsync()).Count() == 0)
337+
{
338+
State = FunctionInvocationDispatcherState.WorkerProcessRestarting;
339+
_logger.LogDebug("No initialized worker channels for runtime '{runtime}'. Delaying future invocations", runtime);
340+
}
341+
// Restart worker channel
335342
_logger.LogDebug("Restarting worker channel for runtime:{runtime}", runtime);
336343
await RestartWorkerChannel(runtime, workerId);
344+
// State is set back to "Initialized" when worker channel is up again
337345
}
338346
else
339347
{
@@ -386,13 +394,15 @@ protected virtual void Dispose(bool disposing)
386394
_processStartCancellationToken.Cancel();
387395
_processStartCancellationToken.Dispose();
388396
_jobHostLanguageWorkerChannelManager.DisposeAndRemoveChannels();
397+
State = FunctionInvocationDispatcherState.Disposed;
389398
_disposed = true;
390399
}
391400
}
392401

393402
public void Dispose()
394403
{
395404
_disposing = true;
405+
State = FunctionInvocationDispatcherState.Disposing;
396406
Dispose(true);
397407
}
398408
}

test/WebJobs.Script.Tests/Description/DotNet/FunctionAssemblyLoadContextTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ public class FunctionAssemblyLoadContextTests
2828
[InlineData("Microsoft.Azure.WebJobs.Script, Version=3.0.0.0")]
2929
[InlineData("Microsoft.Azure.WebJobs.Script.Grpc, Version=3.0.0.0")]
3030
[InlineData("Microsoft.Azure.WebJobs.Script.WebHost, Version=3.0.0.0")]
31-
[InlineData("Microsoft.Azure.WebSites.DataProtection, Version=0.0.0.0")]
32-
[InlineData("System.IO, Version=0.0.0.0")] // System.*
31+
[InlineData("Microsoft.Azure.WebSites.DataProtection")]
32+
[InlineData("System.IO")] // System.*
3333
public void RuntimeAssemblies_AreLoadedInDefaultContext(string assemblyName)
3434
{
3535
var functionContext = new FunctionAssemblyLoadContext(AppContext.BaseDirectory);

test/WebJobs.Script.Tests/Description/Worker/WorkerFunctionInvokerTests.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,29 @@ public async Task InvokeTimeout_CallsShutdown()
5656
_applicationLifetime.Verify(a => a.StopApplication(), Times.Once);
5757
}
5858

59+
[Theory]
60+
[InlineData(FunctionInvocationDispatcherState.Default, false)]
61+
[InlineData(FunctionInvocationDispatcherState.Initializing, true)]
62+
[InlineData(FunctionInvocationDispatcherState.Initialized, false)]
63+
[InlineData(FunctionInvocationDispatcherState.WorkerProcessRestarting, true)]
64+
[InlineData(FunctionInvocationDispatcherState.Disposing, true)]
65+
[InlineData(FunctionInvocationDispatcherState.Disposed, true)]
66+
public async Task FunctionDispatcher_DelaysInvoke_WhenNotReady(FunctionInvocationDispatcherState state, bool delaysExecution)
67+
{
68+
_mockFunctionInvocationDispatcher.Setup(a => a.State).Returns(state);
69+
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(5));
70+
var invokeCoreTask = _testFunctionInvoker.InvokeCore(new object[] { }, null);
71+
var result = await Task.WhenAny(invokeCoreTask, timeoutTask);
72+
if (delaysExecution)
73+
{
74+
Assert.Equal(timeoutTask, result);
75+
}
76+
else
77+
{
78+
Assert.Equal(invokeCoreTask, result);
79+
}
80+
}
81+
5982
[Fact]
6083
public async Task InvokeInitialized_DoesNotCallShutdown()
6184
{

test/WebJobs.Script.Tests/Diagnostics/LinuxContainerEventGeneratorTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ public void ToEventLevel_ReturnsExpectedValue(LogLevel logLevel, EventLevel even
193193
Assert.Equal(eventLevel, LinuxEventGenerator.ToEventLevel(logLevel));
194194
}
195195

196-
[Theory]
196+
[Theory(Skip = "https://github.com/Azure/azure-functions-host/issues/5636")]
197197
[MemberData(nameof(LinuxEventGeneratorTestData.GetAzureMonitorEvents), MemberType = typeof(LinuxEventGeneratorTestData))]
198198
public void ParseAzureMonitoringEvents(LogLevel level, string resourceId, string operationName, string category, string regionName, string properties)
199199
{

0 commit comments

Comments
 (0)