diff --git a/SpotifyAPI.Docs/docs/retry_handling.md b/SpotifyAPI.Docs/docs/retry_handling.md new file mode 100644 index 00000000..9407b184 --- /dev/null +++ b/SpotifyAPI.Docs/docs/retry_handling.md @@ -0,0 +1,50 @@ +--- +id: retry_handling +title: Retry Handling +--- + +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. + +```csharp +var config = SpotifyClientConfig + .CreateDefault() + .WithRetryHandler(new YourCustomRetryHandler()) +``` + +[`IRetryHandler`](https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/SpotifyAPI.Web/Http/Interfaces/IRetryHandler.cs) only needs one function: + +```csharp +public class YourCustomRetryHandler : IRetryHandler +{ + public Task HandleRetry(IRequest request, IResponse response, IRetryHandler.RetryFunc retry) + { + // request is the sent request and response the received response, obviously? + + // don't retry: + return response; + + // retry once + var newResponse = retry(request); + return newResponse; + + // use retry as often as you want, make sure to return a response + } +} +``` + +## SimpleRetryHandler + +A `SimpleRetryHandler` is included, which contains the following retry logic: + +* Retries the (configurable) status codes: 500, 502, 503 and 429 +* `RetryAfter` - specifies the delay between retried calls +* `RetryTimes` - specifies the maxiumum amount of performed retries per call +* `TooManyRequestsConsumesARetry` - Whether a failure of type "Too Many Requests" should use up one of the retry attempts. + +```csharp +var config = SpotifyClientConfig + .CreateDefault() + .WithRetryHandler(new SimpleRetryHandler() { RetryAfter = TimeSpan.FromSeconds(1) }); + +var spotify = new SpotifyClient(config); +``` diff --git a/SpotifyAPI.Docs/docusaurus.config.js b/SpotifyAPI.Docs/docusaurus.config.js index 440eccd3..c84835d2 100644 --- a/SpotifyAPI.Docs/docusaurus.config.js +++ b/SpotifyAPI.Docs/docusaurus.config.js @@ -40,11 +40,6 @@ module.exports = { ] }, { to: 'news', label: 'News', position: 'left' }, - { - href: 'https://www.nuget.org/packages/SpotifyAPI.Web/', - label: 'NuGET', - position: 'right', - }, { href: 'https://github.com/JohnnyCrazy/SpotifyAPI-NET', label: 'GitHub', diff --git a/SpotifyAPI.Docs/sidebars.js b/SpotifyAPI.Docs/sidebars.js index b8569d24..e3175973 100644 --- a/SpotifyAPI.Docs/sidebars.js +++ b/SpotifyAPI.Docs/sidebars.js @@ -12,6 +12,7 @@ module.exports = { 'logging', 'proxy', 'pagination', + 'retry_handling', ] }, { diff --git a/SpotifyAPI.Docs/src/pages/index.js b/SpotifyAPI.Docs/src/pages/index.js index 8d009ee9..8ac78fcf 100644 --- a/SpotifyAPI.Docs/src/pages/index.js +++ b/SpotifyAPI.Docs/src/pages/index.js @@ -93,7 +93,25 @@ function Home() {

{siteConfig.title} - Star + Star +
+ + Nuget + {' '} + + + Nuget + +

{siteConfig.tagline}

diff --git a/SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs b/SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs index 60c3d664..d2d3550a 100644 --- a/SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs +++ b/SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs @@ -36,7 +36,7 @@ namespace SpotifyAPI.Web Assert.AreEqual(2, retryCalled); Assert.AreEqual(setup.Response.Object, response); - setup.Sleep.Verify(s => s(50000), Times.Exactly(2)); + setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Exactly(2)); } [Test] @@ -67,7 +67,7 @@ namespace SpotifyAPI.Web Assert.AreEqual(1, retryCalled); Assert.AreEqual(successResponse.Object, response); - setup.Sleep.Verify(s => s(50000), Times.Once); + setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Once); } [Test] @@ -97,7 +97,7 @@ namespace SpotifyAPI.Web Assert.AreEqual(1, retryCalled); Assert.AreEqual(successResponse.Object, response); - setup.Sleep.Verify(s => s(50000), Times.Once); + setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Once); } [Test] @@ -117,13 +117,13 @@ namespace SpotifyAPI.Web { TooManyRequestsConsumesARetry = true, RetryTimes = 10, - RetryAfter = 50 + RetryAfter = TimeSpan.FromMilliseconds(50) }; var response = await handler.HandleRetry(setup.Request.Object, setup.Response.Object, setup.Retry); Assert.AreEqual(10, retryCalled); Assert.AreEqual(setup.Response.Object, response); - setup.Sleep.Verify(s => s(50), Times.Exactly(10)); + setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Exactly(10)); } [Test] @@ -143,18 +143,18 @@ namespace SpotifyAPI.Web { TooManyRequestsConsumesARetry = true, RetryTimes = 10, - RetryAfter = 50 + RetryAfter = TimeSpan.FromMilliseconds(50) }; var response = await handler.HandleRetry(setup.Request.Object, setup.Response.Object, setup.Retry); Assert.AreEqual(0, retryCalled); Assert.AreEqual(setup.Response.Object, response); - setup.Sleep.Verify(s => s(50), Times.Exactly(0)); + setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Exactly(0)); } private class Setup { - public Mock> Sleep { get; set; } = new Mock>(); + public Mock> Sleep { get; set; } = new Mock>(); public Mock Response { get; set; } = new Mock(); public Mock Request { get; set; } = new Mock(); public IRetryHandler.RetryFunc Retry { get; set; } diff --git a/SpotifyAPI.Web/RetryHandlers/SimpleRetryHandler.cs b/SpotifyAPI.Web/RetryHandlers/SimpleRetryHandler.cs index a1d5a9e1..bd18ac38 100644 --- a/SpotifyAPI.Web/RetryHandlers/SimpleRetryHandler.cs +++ b/SpotifyAPI.Web/RetryHandlers/SimpleRetryHandler.cs @@ -9,12 +9,12 @@ namespace SpotifyAPI.Web { public class SimpleRetryHandler : IRetryHandler { - private readonly Func _sleep; + private readonly Func _sleep; /// /// Specifies after how many miliseconds should a failed request be retried. /// - public int RetryAfter { get; set; } + public TimeSpan RetryAfter { get; set; } /// /// Maximum number of tries for one failed request. @@ -38,10 +38,10 @@ namespace SpotifyAPI.Web /// /// public SimpleRetryHandler() : this(Task.Delay) { } - public SimpleRetryHandler(Func sleep) + public SimpleRetryHandler(Func sleep) { _sleep = sleep; - RetryAfter = 50; + RetryAfter = TimeSpan.FromMilliseconds(50); RetryTimes = 10; TooManyRequestsConsumesARetry = false; RetryErrorCodes = new[] { @@ -51,7 +51,7 @@ namespace SpotifyAPI.Web }; } - private static int? ParseTooManyRetriesToMs(IResponse response) + private static TimeSpan? ParseTooManyRetries(IResponse response) { if (response.StatusCode != (HttpStatusCode)429) { @@ -59,7 +59,7 @@ namespace SpotifyAPI.Web } if (int.TryParse(response.Headers["Retry-After"], out int secondsToWait)) { - return secondsToWait * 1000; + return TimeSpan.FromSeconds(secondsToWait); } throw new APIException("429 received, but unable to parse Retry-After Header. This should not happen!"); @@ -78,7 +78,7 @@ namespace SpotifyAPI.Web IRetryHandler.RetryFunc retry, int triesLeft) { - var secondsToWait = ParseTooManyRetriesToMs(response); + var secondsToWait = ParseTooManyRetries(response); if (secondsToWait != null && (!TooManyRequestsConsumesARetry || triesLeft > 0)) { await _sleep(secondsToWait.Value).ConfigureAwait(false);