Skip to content

Commit cd4ad63

Browse files
authored
Fix flaky test by restructuring task model (#10145)
1 parent 31032fc commit cd4ad63

File tree

3 files changed

+41
-23
lines changed

3 files changed

+41
-23
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System.Threading.Tasks;
5+
6+
namespace Microsoft.Azure.WebJobs.Script.Extensions
7+
{
8+
internal static class TaskExtensions
9+
{
10+
/// <summary>
11+
/// Forgets the task. This method is used to indicating not awaiting a task is intentional.
12+
/// </summary>
13+
/// <param name="task">The task to forget.</param>
14+
public static void Forget(this Task task)
15+
{
16+
// No op - this method is used to suppress the compiler warning.
17+
}
18+
}
19+
}

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

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Microsoft.Azure.WebJobs.Script.Description;
1313
using Microsoft.Azure.WebJobs.Script.Diagnostics;
1414
using Microsoft.Azure.WebJobs.Script.Eventing;
15+
using Microsoft.Azure.WebJobs.Script.Extensions;
1516
using Microsoft.Extensions.Logging;
1617
using Microsoft.Extensions.Options;
1718

@@ -68,16 +69,12 @@ internal HttpFunctionInvocationDispatcher()
6869

6970
public int ErrorEventsThreshold { get; private set; }
7071

71-
internal Task InitializeHttpWorkerChannelAsync(int attemptCount, CancellationToken cancellationToken = default)
72+
internal async Task InitializeHttpWorkerChannelAsync(int attemptCount, CancellationToken cancellationToken = default)
7273
{
7374
_httpWorkerChannel = _httpWorkerChannelFactory.Create(_scriptOptions.RootScriptPath, _metricsLogger, attemptCount);
74-
_httpWorkerChannel.StartWorkerProcessAsync(cancellationToken).ContinueWith(workerInitTask =>
75-
{
76-
_logger.LogDebug("Adding http worker channel. workerId:{id}", _httpWorkerChannel.Id);
77-
SetFunctionDispatcherStateToInitializedAndLog();
78-
}, TaskContinuationOptions.OnlyOnRanToCompletion);
79-
80-
return Task.CompletedTask;
75+
await _httpWorkerChannel.StartWorkerProcessAsync(cancellationToken);
76+
_logger.LogDebug("Adding http worker channel. workerId:{id}", _httpWorkerChannel.Id);
77+
SetFunctionDispatcherStateToInitializedAndLog();
8178
}
8279

8380
private void SetFunctionDispatcherStateToInitializedAndLog()
@@ -86,41 +83,42 @@ private void SetFunctionDispatcherStateToInitializedAndLog()
8683
_logger.LogInformation("Worker process started and initialized.");
8784
}
8885

89-
public async Task InitializeAsync(IEnumerable<FunctionMetadata> functions, CancellationToken cancellationToken = default)
86+
public Task InitializeAsync(IEnumerable<FunctionMetadata> functions, CancellationToken cancellationToken = default)
9087
{
9188
cancellationToken.ThrowIfCancellationRequested();
9289

9390
if (functions == null || !functions.Any())
9491
{
9592
// do not initialize function dispachter if there are no functions
96-
return;
93+
return Task.CompletedTask;
9794
}
9895

9996
State = FunctionInvocationDispatcherState.Initializing;
100-
await InitializeHttpWorkerChannelAsync(0, cancellationToken);
97+
InitializeHttpWorkerChannelAsync(0, cancellationToken).Forget();
98+
return Task.CompletedTask;
10199
}
102100

103101
public Task InvokeAsync(ScriptInvocationContext invocationContext)
104102
{
105103
return _httpWorkerChannel.InvokeAsync(invocationContext);
106104
}
107105

108-
public async void WorkerError(HttpWorkerErrorEvent workerError)
106+
public void WorkerError(HttpWorkerErrorEvent workerError)
109107
{
110108
if (!_disposing)
111109
{
112110
_logger.LogDebug("Handling WorkerErrorEvent for workerId:{workerId}. Failed with: {exception}", workerError.WorkerId, workerError.Exception);
113111
AddOrUpdateErrorBucket(workerError);
114-
await DisposeAndRestartWorkerChannel(workerError.WorkerId);
112+
DisposeAndRestartWorkerChannel(workerError.WorkerId);
115113
}
116114
}
117115

118-
public async void WorkerRestart(HttpWorkerRestartEvent workerRestart)
116+
public void WorkerRestart(HttpWorkerRestartEvent workerRestart)
119117
{
120118
if (!_disposing)
121119
{
122120
_logger.LogDebug("Handling WorkerRestartEvent for workerId:{workerId}", workerRestart.WorkerId);
123-
await DisposeAndRestartWorkerChannel(workerRestart.WorkerId);
121+
DisposeAndRestartWorkerChannel(workerRestart.WorkerId);
124122
}
125123
}
126124

@@ -130,7 +128,7 @@ public Task StartWorkerChannel()
130128
return Task.CompletedTask;
131129
}
132130

133-
private async Task DisposeAndRestartWorkerChannel(string workerId)
131+
private void DisposeAndRestartWorkerChannel(string workerId)
134132
{
135133
// Since we only have one HTTP worker process, as soon as we dispose it, InvokeAsync will fail. Set state to
136134
// indicate we are not ready to receive new requests.
@@ -140,15 +138,16 @@ private async Task DisposeAndRestartWorkerChannel(string workerId)
140138
{
141139
(_httpWorkerChannel as IDisposable)?.Dispose();
142140
}
143-
await RestartWorkerChannel(workerId);
141+
142+
RestartWorkerChannel(workerId);
144143
}
145144

146-
private async Task RestartWorkerChannel(string workerId)
145+
private void RestartWorkerChannel(string workerId)
147146
{
148147
if (_invokerErrors.Count < ErrorEventsThreshold)
149148
{
150149
_logger.LogDebug("Restarting http invoker channel");
151-
await InitializeHttpWorkerChannelAsync(_invokerErrors.Count);
150+
InitializeHttpWorkerChannelAsync(_invokerErrors.Count).Forget();
152151
}
153152
else
154153
{
@@ -207,10 +206,11 @@ public Task ShutdownAsync()
207206
return Task.CompletedTask;
208207
}
209208

210-
public async Task<bool> RestartWorkerWithInvocationIdAsync(string invocationId)
209+
public Task<bool> RestartWorkerWithInvocationIdAsync(string invocationId)
211210
{
212-
await DisposeAndRestartWorkerChannel(_httpWorkerChannel.Id); // Since there's only one channel for httpworker
213-
return true;
211+
// Since there's only one channel for httpworker
212+
DisposeAndRestartWorkerChannel(_httpWorkerChannel.Id);
213+
return Task.FromResult(true);
214214
}
215215

216216
public void PreShutdown()

test/WebJobs.Script.Tests/Workers/Http/HttpFunctionInvocationDispatcherTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ public async Task TestDelay_StartProcess(bool startWorkerProcessResult)
4949
{
5050
}
5151

52-
await Task.Delay(TimeSpan.FromMilliseconds(500));
5352
if (startWorkerProcessResult)
5453
{
5554
Assert.Equal(dispatcher.State, FunctionInvocationDispatcherState.Initialized);

0 commit comments

Comments
 (0)