Skip to content

Commit 945efe1

Browse files
authored
Updating RetryProxyHandler strategy and retry counts (#10566)
1 parent 2d36bbe commit 945efe1

File tree

3 files changed

+70
-17
lines changed

3 files changed

+70
-17
lines changed

release_notes.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@
1515
- Update PowerShell 7.2 worker to [4.0.4025](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.4025)
1616
- Update PowerShell 7.4 worker to [4.0.4026](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.4026)
1717
- Added support for identity-based connections to Diagnostic Events (#10438)
18-
- Updating Microsoft.Azure.WebJobs.Logging.ApplicationInsights to 3.0.42-12121
18+
- Updating Microsoft.Azure.WebJobs.Logging.ApplicationInsights to 3.0.42-12121
19+
- Updated retry logic in Worker HTTP proxy to allow for longer worker HTTP listener initialization times (#10566).

src/WebJobs.Script.Grpc/Server/RetryProxyHandler.cs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ namespace Microsoft.Azure.WebJobs.Script.Grpc
1212
internal sealed class RetryProxyHandler : DelegatingHandler
1313
{
1414
// The maximum number of retries
15-
private readonly int _maxRetries = 3;
15+
internal const int MaxRetries = 10;
1616

1717
// The initial delay in milliseconds
18-
private readonly int _initialDelay = 50;
18+
internal const int InitialDelay = 50;
19+
20+
// The maximum delay in milliseconds
21+
internal const int MaximumDelay = 250;
22+
1923
private readonly ILogger _logger;
2024

2125
public RetryProxyHandler(HttpMessageHandler innerHandler, ILogger logger)
@@ -26,32 +30,29 @@ public RetryProxyHandler(HttpMessageHandler innerHandler, ILogger logger)
2630

2731
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
2832
{
29-
var currentDelay = _initialDelay;
30-
for (int attemptCount = 1; attemptCount <= _maxRetries; attemptCount++)
33+
var currentDelay = InitialDelay;
34+
for (int attemptCount = 1; attemptCount <= MaxRetries; attemptCount++)
3135
{
3236
try
3337
{
3438
return await base.SendAsync(request, cancellationToken);
3539
}
36-
catch (HttpRequestException) when (attemptCount < _maxRetries)
40+
catch (HttpRequestException) when (attemptCount < MaxRetries)
3741
{
38-
currentDelay *= attemptCount;
39-
4042
_logger.LogWarning("Failed to proxy request to the worker. Retrying in {delay}ms. Attempt {attemptCount} of {maxRetries}.",
41-
currentDelay, attemptCount, _maxRetries);
43+
currentDelay, attemptCount, MaxRetries);
4244

4345
await Task.Delay(currentDelay, cancellationToken);
46+
47+
currentDelay = Math.Min(currentDelay * 2, MaximumDelay);
4448
}
4549
catch (Exception ex)
4650
{
47-
if (attemptCount == _maxRetries)
48-
{
49-
_logger.LogWarning("Reached the maximum retry count for worker request proxying. Error: {exception}", ex);
50-
}
51-
else
52-
{
53-
_logger.LogWarning($"Unsupported exception type in {nameof(RetryProxyHandler)}. Request will not be retried. Exception: {{exception}}", ex);
54-
}
51+
var message = attemptCount == MaxRetries
52+
? "Reached the maximum retry count for worker request proxying. Error: {exception}"
53+
: $"Unsupported exception type in {nameof(RetryProxyHandler)}. Request will not be retried. Exception: {{exception}}";
54+
55+
_logger.LogWarning(message, ex);
5556

5657
throw;
5758
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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;
5+
using System.Collections.Generic;
6+
using System.Net.Http;
7+
using System.Reflection;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
using System.Web.Http;
11+
using Microsoft.Azure.WebJobs.Script.Grpc;
12+
using Microsoft.Extensions.Logging.Abstractions;
13+
using Xunit;
14+
15+
namespace Microsoft.Azure.WebJobs.Script.Tests.Workers
16+
{
17+
public class RetryProxyHandlerTests
18+
{
19+
[Fact]
20+
public async Task SendAsync_RetriesToMax()
21+
{
22+
var inner = new TestHandler();
23+
var handler = new RetryProxyHandler(inner, NullLogger.Instance);
24+
var request = new HttpRequestMessage();
25+
26+
var response = typeof(RetryProxyHandler)!
27+
.GetMethod("SendAsync", BindingFlags.NonPublic | BindingFlags.Instance)!
28+
.Invoke(handler, new object[] { request, CancellationToken.None })
29+
as Task<HttpResponseMessage>;
30+
31+
var result = await response.ContinueWith(t => t);
32+
33+
Assert.True(result.IsFaulted);
34+
Assert.True(result.Exception.InnerException is HttpRequestException);
35+
Assert.Equal(RetryProxyHandler.MaxRetries, inner.Attempts);
36+
}
37+
38+
private class TestHandler : HttpMessageHandler
39+
{
40+
public int Attempts { get; set; }
41+
42+
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
43+
CancellationToken cancellationToken)
44+
{
45+
Attempts++;
46+
47+
throw new HttpRequestException();
48+
}
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)