Skip to content

Commit d0e0904

Browse files
committed
Made reliabilty settings immutable, refactored associated code
1 parent cc03333 commit d0e0904

File tree

6 files changed

+183
-108
lines changed

6 files changed

+183
-108
lines changed

USE_CASES.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -587,28 +587,37 @@ namespace Example
587587
<a name="transient_faults"></a>
588588
# Transient Fault Handling
589589

590-
The SendGridClient provides functionality for handling transient errors that might occur when sending an HttpRequest. This includes client side timeouts while sending the mail, or any errors returned within the 500 range.
590+
The SendGridClient provides functionality for handling transient errors that might occur when sending an HttpRequest. This includes client side timeouts while sending the mail, or certain errors returned within the 500 range. Errors within the 500 range are limited to 500 Internal Server Error, 502 Bad Gateway, 503 Service unavailable and 504 Gateway timeout.
591591

592592
By default, retry behaviour is off, you must explicitly enable it by setting the retry count to a value greater than zero. To set the retry count, you must use the SendGridClient construct that takes a **SendGridClientOptions** object, allowing you to configure the **ReliabilitySettings**
593593

594594
### RetryCount
595595

596-
The amount of times to retry the operation before reporting an exception to the caller. This is in addition to the initial attempt, so setting a value of 1 would result in 2 attempts - the initial attempt and the retry. Defaults to zero, so retry behaviour is not enabled. The maximum amount of retries permitted is 5.
596+
The amount of times to retry the operation before reporting an exception to the caller. This is in addition to the initial attempt so setting a value of 1 would result in 2 attempts, the initial attempt and the retry. Defaults to zero, retry behaviour is not enabled. The maximum amount of retries permitted is 5.
597597

598-
### RetryInterval
598+
### MinimumBackOff
599+
600+
The minimum amount of time to wait between retries.
601+
602+
### MaximumBackOff
603+
604+
The maximum possible amount of time to wait between retries. The maximum value allowed is 30 seconds
605+
606+
### DeltaBackOff
607+
608+
The value that will be used to calculate a random delta in the exponential delay between retries. A random element of time is factored into the delta calculation as this helps avoid many clients retrying at regular intervals.
599609

600-
The policy implemented is a 'wait and retry'. The RetryInterval setting defines the amount of time to wait between operations that fail before attempting a retry. By default, this is set to 1 second - the maximum amount of time allowed is 30 seconds.
601610

602611
## Examples
603612

604-
In this example, we are setting RetryCount to 1, with an interval between attempts of 5 seconds. This means that if the inital attempt to send mail fails, then it will wait 5 seconds then try again a single time.
613+
In this example we are setting RetryCount to 2, with a mimimum wait time of 1 seconds, a maximum of 10 seconds and a delta of 3 seconds
605614

606615
```csharp
607616

608617
var options = new SendGridClientOptions
609618
{
610619
ApiKey = "Your-Api-Key",
611-
ReliabilitySettings = {RetryCount = 1, RetryInterval = TimeSpan.FromSeconds(5)}
620+
ReliabilitySettings = new ReliabilitySettings(2, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(3))
612621
};
613622

614623
var client = new SendGridClient(options);
@@ -622,7 +631,7 @@ The SendGridClientOptions object defines all the settings that can be set for th
622631
var options = new SendGridClientOptions
623632
{
624633
ApiKey = "Your-Api-Key",
625-
ReliabilitySettings = {RetryCount = 1, RetryInterval = TimeSpan.FromSeconds(5)},
634+
ReliabilitySettings = new ReliabilitySettings(2, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(3)),
626635
Host = "Your-Host",
627636
UrlPath = "Url-Path",
628637
Version = "3",

src/SendGrid/Reliability/ReliabilitySettings.cs

Lines changed: 46 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -7,104 +7,82 @@ namespace SendGrid.Helpers.Reliability
77
/// </summary>
88
public class ReliabilitySettings
99
{
10-
private int maximumNumberOfRetries;
11-
12-
private TimeSpan minimumBackOff;
13-
private TimeSpan maximumBackOff;
14-
private TimeSpan deltaBackOff;
15-
1610
/// <summary>
17-
/// Initializes a new instance of the <see cref="ReliabilitySettings"/> class.
11+
/// Initializes a new instance of the <see cref="ReliabilitySettings"/> class with default settings.
1812
/// </summary>
1913
public ReliabilitySettings()
14+
: this(0, TimeSpan.Zero, TimeSpan.Zero, TimeSpan.Zero)
2015
{
21-
this.maximumNumberOfRetries = 0;
22-
this.minimumBackOff = TimeSpan.FromSeconds(1);
23-
this.deltaBackOff = TimeSpan.FromSeconds(1);
24-
this.maximumBackOff = TimeSpan.FromSeconds(10);
2516
}
2617

2718
/// <summary>
28-
/// Gets or sets the maximum number of retries to execute against when sending an HTTP Request before throwing an exception. Defaults to 0 (no retries, you must explicitly enable)
19+
/// Initializes a new instance of the <see cref="ReliabilitySettings"/> class.
2920
/// </summary>
30-
public int MaximumNumberOfRetries
21+
/// <param name="maximumNumberOfRetries">The maximum number of retries to execute against when sending an HTTP Request before throwing an exception</param>
22+
/// <param name="minimumBackoff">The minimum amount of time to wait between between HTTP retries</param>
23+
/// <param name="maximumBackOff">the maximum amount of time to wait between between HTTP retries</param>
24+
/// <param name="deltaBackOff">the value that will be used to calculate a random delta in the exponential delay between retries</param>
25+
public ReliabilitySettings(int maximumNumberOfRetries, TimeSpan minimumBackoff, TimeSpan maximumBackOff, TimeSpan deltaBackOff)
3126
{
32-
get
27+
if (maximumNumberOfRetries < 0)
3328
{
34-
return this.maximumNumberOfRetries;
29+
throw new ArgumentOutOfRangeException(nameof(maximumNumberOfRetries), "maximumNumberOfRetries must be greater than 0");
3530
}
3631

37-
set
32+
if (maximumNumberOfRetries > 5)
3833
{
39-
if (value < 0)
40-
{
41-
throw new ArgumentException("Retry count must be greater than zero");
42-
}
43-
44-
if (value > 5)
45-
{
46-
throw new ArgumentException("The maximum number of retries that can be attempted is 5");
47-
}
48-
49-
this.maximumNumberOfRetries = value;
34+
throw new ArgumentOutOfRangeException(nameof(maximumNumberOfRetries), "The maximum number of retries allowed is 5");
5035
}
51-
}
5236

53-
/// <summary>
54-
/// Gets or sets the interval between HTTP retries. Defaults to 1 second
55-
/// </summary>
56-
public TimeSpan MinimumBackOff
57-
{
58-
get
37+
if (minimumBackoff.Ticks < 0)
5938
{
60-
return this.minimumBackOff;
39+
throw new ArgumentOutOfRangeException(nameof(minimumBackoff), "minimumBackoff must be greater than 0");
6140
}
6241

63-
set
42+
if (maximumBackOff.Ticks < 0)
6443
{
65-
if (value.TotalSeconds > 30)
66-
{
67-
throw new ArgumentException("The maximum setting for minimum back off is 30 seconds");
68-
}
69-
70-
this.minimumBackOff = value;
44+
throw new ArgumentOutOfRangeException(nameof(maximumBackOff), "maximumBackOff must be greater than 0");
7145
}
72-
}
7346

74-
public TimeSpan MaximumBackOff
75-
{
76-
get
47+
if (maximumBackOff.TotalSeconds > 30)
7748
{
78-
return this.maximumBackOff;
49+
throw new ArgumentOutOfRangeException(nameof(maximumBackOff), "maximumBackOff must be less than 30 seconds");
7950
}
8051

81-
set
52+
if (deltaBackOff.Ticks < 0)
8253
{
83-
if (value.TotalSeconds > 30)
84-
{
85-
throw new ArgumentException("The maximum setting to back off for is 30 seconds");
86-
}
87-
88-
this.maximumBackOff = value;
54+
throw new ArgumentOutOfRangeException(nameof(deltaBackOff), "deltaBackOff must be greater than 0");
8955
}
90-
}
9156

92-
public TimeSpan DeltaBackOff
93-
{
94-
get
57+
if (minimumBackoff.TotalMilliseconds > maximumBackOff.TotalMilliseconds)
9558
{
96-
return this.deltaBackOff;
59+
throw new ArgumentOutOfRangeException(nameof(minimumBackoff), "minimumBackoff must be less than maximumBackOff");
9760
}
9861

99-
set
100-
{
101-
if (value.TotalSeconds > 30)
102-
{
103-
throw new ArgumentException("The maximum delta interval is 5 seconds");
104-
}
105-
106-
this.deltaBackOff = value;
107-
}
62+
this.MaximumNumberOfRetries = maximumNumberOfRetries;
63+
this.MinimumBackOff = minimumBackoff;
64+
this.DeltaBackOff = deltaBackOff;
65+
this.MaximumBackOff = maximumBackOff;
10866
}
67+
68+
/// <summary>
69+
/// Gets the maximum number of retries to execute against when sending an HTTP Request before throwing an exception. Defaults to 0 (no retries, you must explicitly enable)
70+
/// </summary>
71+
public int MaximumNumberOfRetries { get; }
72+
73+
/// <summary>
74+
/// Gets the minimum amount of time to wait between between HTTP retries. Defaults to 1 second
75+
/// </summary>
76+
public TimeSpan MinimumBackOff { get; }
77+
78+
/// <summary>
79+
/// Gets the maximum amount of time to wait between between HTTP retries. Defaults to 10 seconds
80+
/// </summary>
81+
public TimeSpan MaximumBackOff { get; }
82+
83+
/// <summary>
84+
/// Gets the value that will be used to calculate a random delta in the exponential delay between retries. Defaults to 1 second
85+
/// </summary>
86+
public TimeSpan DeltaBackOff { get; }
10987
}
11088
}

src/SendGrid/SendGridClientOptions.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
namespace SendGrid
22
{
3+
using System;
34
using System.Collections.Generic;
45
using SendGrid.Helpers.Reliability;
56

@@ -8,21 +9,27 @@
89
/// </summary>
910
public class SendGridClientOptions
1011
{
12+
private ReliabilitySettings reliabilitySettings = new ReliabilitySettings();
13+
1114
/// <summary>
1215
/// Initializes a new instance of the <see cref="SendGridClientOptions"/> class.
1316
/// </summary>
1417
public SendGridClientOptions()
1518
{
16-
ReliabilitySettings = new ReliabilitySettings();
1719
RequestHeaders = new Dictionary<string, string>();
1820
Host = "https://api.sendgrid.com";
1921
Version = "v3";
2022
}
2123

2224
/// <summary>
23-
/// Gets the reliability settings to use on HTTP Requests
25+
/// Gets or sets the reliability settings to use on HTTP Requests
2426
/// </summary>
25-
public ReliabilitySettings ReliabilitySettings { get; }
27+
public ReliabilitySettings ReliabilitySettings
28+
{
29+
get => this.reliabilitySettings;
30+
31+
set => this.reliabilitySettings = value ?? throw new ArgumentNullException(nameof(value));
32+
}
2633

2734
/// <summary>
2835
/// Gets or sets the SendGrid API key

tests/SendGrid.Tests/Integration.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6141,7 +6141,7 @@ public async Task TestRetryBehaviourThrowsTimeoutException()
61416141
var options = new SendGridClientOptions
61426142
{
61436143
ApiKey = fixture.apiKey,
6144-
ReliabilitySettings = { MaximumNumberOfRetries = 1 },
6144+
ReliabilitySettings = new ReliabilitySettings(1, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(1)),
61456145
Host = "http://localhost:4010"
61466146
};
61476147

@@ -6169,7 +6169,7 @@ public async Task TestRetryBehaviourSucceedsOnSecondAttempt()
61696169
var options = new SendGridClientOptions
61706170
{
61716171
ApiKey = fixture.apiKey,
6172-
ReliabilitySettings = { MaximumNumberOfRetries = 1 }
6172+
ReliabilitySettings = new ReliabilitySettings(1, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(1))
61736173
};
61746174

61756175
var id = "test_url_param";
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
namespace SendGrid.Tests.Reliability
2+
{
3+
using System;
4+
5+
using Helpers.Reliability;
6+
using Xunit;
7+
8+
public class ReliabilitySettingsTests
9+
{
10+
[Fact]
11+
public void ShouldNotAllowNegativeRetryCount()
12+
{
13+
var exception = Assert.Throws<ArgumentOutOfRangeException>(() =>
14+
new ReliabilitySettings(-1,
15+
TimeSpan.FromSeconds(1),
16+
TimeSpan.FromSeconds(1),
17+
TimeSpan.FromSeconds(1)));
18+
19+
Assert.Contains("maximumNumberOfRetries must be greater than 0", exception.Message);
20+
}
21+
22+
[Fact]
23+
public void ShouldNotAllowNegativeMinimumBackoffTime()
24+
{
25+
var exception = Assert.Throws<ArgumentOutOfRangeException>(() =>
26+
new ReliabilitySettings(1,
27+
TimeSpan.FromSeconds(-1),
28+
TimeSpan.FromSeconds(1),
29+
TimeSpan.FromSeconds(1)));
30+
31+
Assert.Contains("minimumBackoff must be greater than 0", exception.Message);
32+
}
33+
34+
[Fact]
35+
public void ShouldNotAllowNegativeMaximumBackoffTime()
36+
{
37+
var exception = Assert.Throws<ArgumentOutOfRangeException>(() =>
38+
new ReliabilitySettings(1,
39+
TimeSpan.FromSeconds(1),
40+
TimeSpan.FromSeconds(-11),
41+
TimeSpan.FromSeconds(1)));
42+
43+
Assert.Contains("maximumBackOff must be greater than 0", exception.Message);
44+
}
45+
46+
[Fact]
47+
public void ShouldNotAllowNegativeDeltaBackoffTime()
48+
{
49+
var exception = Assert.Throws<ArgumentOutOfRangeException>(() =>
50+
new ReliabilitySettings(1,
51+
TimeSpan.FromSeconds(1),
52+
TimeSpan.FromSeconds(1),
53+
TimeSpan.FromSeconds(-1)));
54+
55+
Assert.Contains("deltaBackOff must be greater than 0", exception.Message);
56+
}
57+
58+
[Fact]
59+
public void ShouldNotAllowRetryCountGreaterThan5()
60+
{
61+
var exception = Assert.Throws<ArgumentOutOfRangeException>(() =>
62+
new ReliabilitySettings(6,
63+
TimeSpan.FromSeconds(1),
64+
TimeSpan.FromSeconds(1),
65+
TimeSpan.FromSeconds(1)));
66+
67+
Assert.Contains("The maximum number of retries allowed is 5", exception.Message);
68+
}
69+
70+
[Fact]
71+
public void ShouldNotAllowMinimumBackOffGreaterThanMaximumBackoff()
72+
{
73+
var exception = Assert.Throws<ArgumentOutOfRangeException>(() =>
74+
new ReliabilitySettings(1,
75+
TimeSpan.FromSeconds(11),
76+
TimeSpan.FromSeconds(10),
77+
TimeSpan.FromSeconds(1)));
78+
79+
Assert.Contains("minimumBackoff must be less than maximumBackOff", exception.Message);
80+
}
81+
82+
[Fact]
83+
public void ShouldNotAllowMaximumBackOffGreaterThan30Seconds()
84+
{
85+
var exception = Assert.Throws<ArgumentOutOfRangeException>(() =>
86+
new ReliabilitySettings(1,
87+
TimeSpan.FromSeconds(1),
88+
TimeSpan.FromSeconds(31),
89+
TimeSpan.FromSeconds(1)));
90+
91+
Assert.Contains("maximumBackOff must be less than 30 seconds", exception.Message);
92+
}
93+
94+
[Fact]
95+
public void ShouldPassValidValuesFromDefaultConstruct()
96+
{
97+
var defaultSettings = new ReliabilitySettings();
98+
99+
Assert.Equal(TimeSpan.Zero, defaultSettings.MaximumBackOff);
100+
Assert.Equal(TimeSpan.Zero, defaultSettings.MinimumBackOff);
101+
Assert.Equal(TimeSpan.Zero, defaultSettings.DeltaBackOff);
102+
Assert.Equal(0, defaultSettings.MaximumNumberOfRetries);
103+
}
104+
}
105+
}
106+

0 commit comments

Comments
 (0)