Skip to content

Commit bc374f9

Browse files
refactoring retry handler.
1 parent 3038fa3 commit bc374f9

File tree

2 files changed

+50
-95
lines changed

2 files changed

+50
-95
lines changed

src/SocketLabs/InjectionApi/Core/RetryHandler.cs

Lines changed: 22 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Threading;
66
using System.Threading.Tasks;
77

8+
// ReSharper disable MethodSupportsCancellation
89
namespace SocketLabs.InjectionApi.Core
910
{
1011
internal class RetryHandler
@@ -39,78 +40,50 @@ public RetryHandler(HttpClient httpClient, string endpointUrl, RetrySettings set
3940
public async Task<HttpResponseMessage> SendAsync(StringContent content, CancellationToken cancellationToken)
4041
{
4142
if (RetrySettings.MaximumNumberOfRetries == 0)
42-
{
43-
return await HttpClient.PostAsync(EndpointUrl, content, cancellationToken)
43+
return await HttpClient
44+
.PostAsync(EndpointUrl, content, cancellationToken)
4445
.ConfigureAwait(false);
45-
}
46+
4647

4748
HttpResponseMessage response = null;
4849

49-
var numberOfAttempts = 0;
50-
var sent = false;
50+
var attempts = 0;
51+
var waiting = true;
5152

52-
while (!sent)
53+
do
5354
{
54-
var waitFor = this.GetNextWaitInterval(numberOfAttempts);
55+
var waitInterval = RetrySettings.GetNextWaitInterval(attempts);
5556

5657
try
5758
{
58-
response = await HttpClient.PostAsync(EndpointUrl, content, cancellationToken).ConfigureAwait(false);
59-
59+
response = await HttpClient.PostAsync(EndpointUrl, content, cancellationToken)
60+
.ConfigureAwait(false);
61+
6062
if (ErrorStatusCodes.Contains(response.StatusCode))
61-
throw new HttpRequestException($"HttpStatusCode: '{response.StatusCode}'. Response contains server error.");
62-
63+
throw new HttpRequestException(
64+
$"HttpStatusCode: '{response.StatusCode}'. Response contains server error.");
6365

64-
sent = true;
66+
waiting = false;
6567
}
6668
catch (TaskCanceledException)
6769
{
68-
numberOfAttempts++;
69-
70-
if (numberOfAttempts > RetrySettings.MaximumNumberOfRetries)
71-
{
72-
throw new TimeoutException();
73-
}
74-
75-
// ReSharper disable once MethodSupportsCancellation, cancel will be indicated on the token
76-
await Task.Delay(waitFor).ConfigureAwait(false);
70+
attempts++;
71+
if (attempts > RetrySettings.MaximumNumberOfRetries) throw new TimeoutException();
72+
await Task.Delay(waitInterval).ConfigureAwait(false);
7773
}
7874
catch (HttpRequestException)
7975
{
80-
numberOfAttempts++;
81-
82-
if (numberOfAttempts > RetrySettings.MaximumNumberOfRetries)
83-
{
84-
throw;
85-
}
86-
87-
await Task.Delay(waitFor).ConfigureAwait(false);
76+
attempts++;
77+
if (attempts > RetrySettings.MaximumNumberOfRetries) throw;
78+
await Task.Delay(waitInterval).ConfigureAwait(false);
8879
}
89-
}
90-
91-
return response;
92-
}
93-
94-
95-
96-
internal virtual int GetRetryDelta(int numberOfAttempts)
97-
{
98-
var random = new Random();
9980

100-
var min = (int) (TimeSpan.FromSeconds(1).TotalMilliseconds * 0.8);
101-
var max = (int) (TimeSpan.FromSeconds(1).TotalMilliseconds * 1.2);
81+
} while (waiting);
10282

103-
return (int) ((Math.Pow(2.0, numberOfAttempts) - 1.0) * random.Next(min, max));
83+
return response;
10484
}
10585

106-
private TimeSpan GetNextWaitInterval(int numberOfAttempts)
107-
{
108-
var interval = (int)Math.Min(
109-
RetrySettings.MinimumRetryTimeBetween.TotalMilliseconds + GetRetryDelta(numberOfAttempts),
110-
RetrySettings.MaximumRetryTimeBetween.TotalMilliseconds);
11186

112-
return TimeSpan.FromMilliseconds(interval);
113-
}
11487

11588
}
11689
}

src/SocketLabs/InjectionApi/RetrySettings.cs

Lines changed: 28 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,16 @@ namespace SocketLabs.InjectionApi
88
public class RetrySettings
99
{
1010

11-
/// <summary>
12-
/// The maximum number of retries when sending an Injection API Request before throwing an exception. Default: 0, no retries, you must explicitly enable retry settings
13-
/// </summary>
14-
public int MaximumNumberOfRetries { get; }
15-
16-
/// <summary>
17-
/// The minimum wait time between between HTTP retries. Default: 1s
18-
/// </summary>
19-
public TimeSpan MinimumRetryTimeBetween { get; }
20-
21-
/// <summary>
22-
/// The maximum wait time between between retries. Default: 10s
23-
/// </summary>
24-
public TimeSpan MaximumRetryTimeBetween { get; }
25-
2611
private const int _defaultNumberOfRetries = 0;
2712
private const int _maximumAllowedNumberOfRetries = 5;
28-
private readonly TimeSpan _defaultMinimumRetryTime = TimeSpan.FromSeconds(1);
29-
private readonly TimeSpan _defaultMaximumRetryTime = TimeSpan.FromSeconds(10);
13+
private readonly TimeSpan _minimumRetryTime = TimeSpan.FromSeconds(1);
14+
private readonly TimeSpan _maximumRetryTime = TimeSpan.FromSeconds(10);
3015

3116
/// <summary>
3217
/// Creates a new instance of the <c>RetrySettings</c>.
3318
/// </summary>
3419
/// <param name="maximumNumberOfRetries"></param>
35-
/// <param name="minimumRetryTimeBetween"></param>
36-
/// <param name="maximumRetryTimeBetween"></param>
37-
public RetrySettings(int? maximumNumberOfRetries = null, TimeSpan? minimumRetryTimeBetween = null, TimeSpan? maximumRetryTimeBetween = null)
20+
public RetrySettings(int? maximumNumberOfRetries = null)
3821
{
3922

4023
if (maximumNumberOfRetries != null)
@@ -46,38 +29,37 @@ public RetrySettings(int? maximumNumberOfRetries = null, TimeSpan? minimumRetryT
4629
}
4730
else
4831
MaximumNumberOfRetries = _defaultNumberOfRetries;
32+
33+
}
4934

5035

36+
/// <summary>
37+
/// The maximum number of retries when sending an Injection API Request before throwing an exception. Default: 0, no retries, you must explicitly enable retry settings
38+
/// </summary>
39+
public int MaximumNumberOfRetries { get; }
40+
5141

52-
if (minimumRetryTimeBetween != null)
53-
{
54-
if (minimumRetryTimeBetween.Value.Ticks < 0) throw new ArgumentOutOfRangeException(nameof(minimumRetryTimeBetween), "minimumRetryTimeBetween must be greater than 0");
55-
56-
MinimumRetryTimeBetween = minimumRetryTimeBetween.Value;
57-
}
58-
else
59-
MinimumRetryTimeBetween = _defaultMinimumRetryTime;
60-
61-
62-
if (maximumRetryTimeBetween != null)
63-
{
64-
if (maximumRetryTimeBetween.Value.Ticks < 0) throw new ArgumentOutOfRangeException(nameof(maximumRetryTimeBetween), "maximumRetryTimeBetween must be greater than 0");
65-
if (maximumRetryTimeBetween.Value.TotalSeconds > 30) throw new ArgumentOutOfRangeException(nameof(maximumRetryTimeBetween), "maximumRetryTimeBetween must be less than 30 seconds");
66-
67-
MaximumRetryTimeBetween = maximumRetryTimeBetween.Value;
68-
}
69-
else
70-
MaximumRetryTimeBetween = _defaultMaximumRetryTime;
71-
42+
/// <summary>
43+
/// Get the time period to wait before next call
44+
/// </summary>
45+
/// <param name="numberOfAttempts"></param>
46+
/// <returns></returns>
47+
public TimeSpan GetNextWaitInterval(int numberOfAttempts)
48+
{
49+
var interval = (int)Math.Min(
50+
_minimumRetryTime.TotalMilliseconds + GetRetryDelta(numberOfAttempts),
51+
_maximumRetryTime.TotalMilliseconds);
7252

73-
if (minimumRetryTimeBetween != null && maximumRetryTimeBetween != null)
74-
{
75-
if (minimumRetryTimeBetween.Value.TotalMilliseconds > maximumRetryTimeBetween.Value.TotalMilliseconds)
76-
throw new ArgumentOutOfRangeException(nameof(minimumRetryTimeBetween),
77-
"minimumRetryTimeBetween must be less than maximumRetryTimeBetween");
78-
}
53+
return TimeSpan.FromMilliseconds(interval);
54+
}
55+
internal virtual int GetRetryDelta(int numberOfAttempts)
56+
{
57+
var random = new Random();
7958

59+
var min = (int)(TimeSpan.FromSeconds(1).TotalMilliseconds * 0.8);
60+
var max = (int)(TimeSpan.FromSeconds(1).TotalMilliseconds * 1.2);
8061

62+
return (int)((Math.Pow(2.0, numberOfAttempts) - 1.0) * random.Next(min, max));
8163
}
8264
}
8365
}

0 commit comments

Comments
 (0)