Skip to content

Commit 7bc5015

Browse files
committed
More docs regarding retry_handling
1 parent d31a9e4 commit 7bc5015

6 files changed

Lines changed: 85 additions & 21 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
id: retry_handling
3+
title: Retry Handling
4+
---
5+
6+
In [Error Handling](error_handling.md) we already found out that requests can fail. We provide a way to automatically retry requests via retry handlers. Note, by default no retries are performed.
7+
8+
```csharp
9+
var config = SpotifyClientConfig
10+
.CreateDefault()
11+
.WithRetryHandler(new YourCustomRetryHandler())
12+
```
13+
14+
[`IRetryHandler`](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Http/Interfaces/IRetryHandler.cs) only needs one function:
15+
16+
```csharp
17+
public class YourCustomRetryHandler : IRetryHandler
18+
{
19+
public Task<IResponse> HandleRetry(IRequest request, IResponse response, IRetryHandler.RetryFunc retry)
20+
{
21+
// request is the sent request and response the received response, obviously?
22+
23+
// don't retry:
24+
return response;
25+
26+
// retry once
27+
var newResponse = retry(request);
28+
return newResponse;
29+
30+
// use retry as often as you want, make sure to return a response
31+
}
32+
}
33+
```
34+
35+
## SimpleRetryHandler
36+
37+
A `SimpleRetryHandler` is included, which contains the following retry logic:
38+
39+
* Retries the (configurable) status codes: 500, 502, 503 and 429
40+
* `RetryAfter` - specifies the delay between retried calls
41+
* `RetryTimes` - specifies the maxiumum amount of performed retries per call
42+
* `TooManyRequestsConsumesARetry` - Whether a failure of type "Too Many Requests" should use up one of the retry attempts.
43+
44+
```csharp
45+
var config = SpotifyClientConfig
46+
.CreateDefault()
47+
.WithRetryHandler(new SimpleRetryHandler() { RetryAfter = TimeSpan.FromSeconds(1) });
48+
49+
var spotify = new SpotifyClient(config);
50+
```

‎SpotifyAPI.Docs/docusaurus.config.js‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ module.exports = {
4040
]
4141
},
4242
{ to: 'news', label: 'News', position: 'left' },
43-
{
44-
href: 'https://www.nuget.org/packages/SpotifyAPI.Web/',
45-
label: 'NuGET',
46-
position: 'right',
47-
},
4843
{
4944
href: 'https://github.com/JohnnyCrazy/SpotifyAPI-NET',
5045
label: 'GitHub',

‎SpotifyAPI.Docs/sidebars.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module.exports = {
1212
'logging',
1313
'proxy',
1414
'pagination',
15+
'retry_handling',
1516
]
1617
},
1718
{

‎SpotifyAPI.Docs/src/pages/index.js‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,25 @@ function Home() {
9393
<h1 className="hero__title">
9494
{siteConfig.title}
9595
<span style={{ marginLeft: '50px' }} />
96-
<GitHubButton href="https://github.com/JohnnyCrazy/SpotifyAPI-NET" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star JohnnyCrazy/SpotifyAPI-NET on GitHub">Star</GitHubButton>
96+
<GitHubButton
97+
href="https://github.com/JohnnyCrazy/SpotifyAPI-NET"
98+
data-icon="octicon-star"
99+
data-size="large"
100+
data-show-count="true"
101+
aria-label="Star JohnnyCrazy/SpotifyAPI-NET on GitHub">Star</GitHubButton>
102+
<br />
103+
<a href="https://www.nuget.org/packages/SpotifyAPI.Web/" rel="noopener noreferrer">
104+
<img
105+
alt="Nuget"
106+
src="https://img.shields.io/nuget/v/SpotifyAPI.Web?label=SpotifyAPI.Web&style=flat-square">
107+
</img>{' '}
108+
</a>
109+
<a href="https://www.nuget.org/packages/SpotifyAPI.Web.Auth/" rel="noopener noreferrer">
110+
<img
111+
alt="Nuget"
112+
src="https://img.shields.io/nuget/v/SpotifyAPI.Web.Auth?label=SpotifyAPI.Web.Auth&style=flat-square">
113+
</img>
114+
</a>
97115
</h1>
98116
<p className="hero__subtitle">{siteConfig.tagline}</p>
99117
<div className={styles.buttons}>

‎SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public async Task HandleRetry_TooManyRequestsWithNoSuccess()
3636

3737
Assert.AreEqual(2, retryCalled);
3838
Assert.AreEqual(setup.Response.Object, response);
39-
setup.Sleep.Verify(s => s(50000), Times.Exactly(2));
39+
setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Exactly(2));
4040
}
4141

4242
[Test]
@@ -67,7 +67,7 @@ public async Task HandleRetry_TooManyRetriesWithSuccess()
6767

6868
Assert.AreEqual(1, retryCalled);
6969
Assert.AreEqual(successResponse.Object, response);
70-
setup.Sleep.Verify(s => s(50000), Times.Once);
70+
setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Once);
7171
}
7272

7373
[Test]
@@ -97,7 +97,7 @@ public async Task HandleRetry_TooManyRetriesWithSuccessNoConsume()
9797

9898
Assert.AreEqual(1, retryCalled);
9999
Assert.AreEqual(successResponse.Object, response);
100-
setup.Sleep.Verify(s => s(50000), Times.Once);
100+
setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Once);
101101
}
102102

103103
[Test]
@@ -117,13 +117,13 @@ public async Task HandleRetry_ServerErrors()
117117
{
118118
TooManyRequestsConsumesARetry = true,
119119
RetryTimes = 10,
120-
RetryAfter = 50
120+
RetryAfter = TimeSpan.FromMilliseconds(50)
121121
};
122122
var response = await handler.HandleRetry(setup.Request.Object, setup.Response.Object, setup.Retry);
123123

124124
Assert.AreEqual(10, retryCalled);
125125
Assert.AreEqual(setup.Response.Object, response);
126-
setup.Sleep.Verify(s => s(50), Times.Exactly(10));
126+
setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Exactly(10));
127127
}
128128

129129
[Test]
@@ -143,18 +143,18 @@ public async Task HandleRetry_DirectSuccess()
143143
{
144144
TooManyRequestsConsumesARetry = true,
145145
RetryTimes = 10,
146-
RetryAfter = 50
146+
RetryAfter = TimeSpan.FromMilliseconds(50)
147147
};
148148
var response = await handler.HandleRetry(setup.Request.Object, setup.Response.Object, setup.Retry);
149149

150150
Assert.AreEqual(0, retryCalled);
151151
Assert.AreEqual(setup.Response.Object, response);
152-
setup.Sleep.Verify(s => s(50), Times.Exactly(0));
152+
setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Exactly(0));
153153
}
154154

155155
private class Setup
156156
{
157-
public Mock<Func<int, Task>> Sleep { get; set; } = new Mock<Func<int, Task>>();
157+
public Mock<Func<TimeSpan, Task>> Sleep { get; set; } = new Mock<Func<TimeSpan, Task>>();
158158
public Mock<IResponse> Response { get; set; } = new Mock<IResponse>();
159159
public Mock<IRequest> Request { get; set; } = new Mock<IRequest>();
160160
public IRetryHandler.RetryFunc Retry { get; set; }

‎SpotifyAPI.Web/RetryHandlers/SimpleRetryHandler.cs‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ namespace SpotifyAPI.Web
99
{
1010
public class SimpleRetryHandler : IRetryHandler
1111
{
12-
private readonly Func<int, Task> _sleep;
12+
private readonly Func<TimeSpan, Task> _sleep;
1313

1414
/// <summary>
1515
/// Specifies after how many miliseconds should a failed request be retried.
1616
/// </summary>
17-
public int RetryAfter { get; set; }
17+
public TimeSpan RetryAfter { get; set; }
1818

1919
/// <summary>
2020
/// Maximum number of tries for one failed request.
@@ -38,10 +38,10 @@ public class SimpleRetryHandler : IRetryHandler
3838
/// </summary>
3939
/// <returns></returns>
4040
public SimpleRetryHandler() : this(Task.Delay) { }
41-
public SimpleRetryHandler(Func<int, Task> sleep)
41+
public SimpleRetryHandler(Func<TimeSpan, Task> sleep)
4242
{
4343
_sleep = sleep;
44-
RetryAfter = 50;
44+
RetryAfter = TimeSpan.FromMilliseconds(50);
4545
RetryTimes = 10;
4646
TooManyRequestsConsumesARetry = false;
4747
RetryErrorCodes = new[] {
@@ -51,15 +51,15 @@ public SimpleRetryHandler(Func<int, Task> sleep)
5151
};
5252
}
5353

54-
private static int? ParseTooManyRetriesToMs(IResponse response)
54+
private static TimeSpan? ParseTooManyRetries(IResponse response)
5555
{
5656
if (response.StatusCode != (HttpStatusCode)429)
5757
{
5858
return null;
5959
}
6060
if (int.TryParse(response.Headers["Retry-After"], out int secondsToWait))
6161
{
62-
return secondsToWait * 1000;
62+
return TimeSpan.FromSeconds(secondsToWait);
6363
}
6464

6565
throw new APIException("429 received, but unable to parse Retry-After Header. This should not happen!");
@@ -78,7 +78,7 @@ private async Task<IResponse> HandleRetryInternally(
7878
IRetryHandler.RetryFunc retry,
7979
int triesLeft)
8080
{
81-
var secondsToWait = ParseTooManyRetriesToMs(response);
81+
var secondsToWait = ParseTooManyRetries(response);
8282
if (secondsToWait != null && (!TooManyRequestsConsumesARetry || triesLeft > 0))
8383
{
8484
await _sleep(secondsToWait.Value).ConfigureAwait(false);

0 commit comments

Comments
 (0)