Skip to content

Commit f14ee06

Browse files
committed
Retry on IO exception
1 parent 35bb26d commit f14ee06

File tree

1 file changed

+38
-6
lines changed

1 file changed

+38
-6
lines changed

src/NexusMods.Networking.HttpDownloader/HttpDownloadJob.cs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using NexusMods.MnemonicDB.Abstractions;
1414
using NexusMods.Paths;
1515
using Polly;
16+
using Polly.Retry;
1617

1718
namespace NexusMods.Networking.HttpDownloader;
1819

@@ -26,6 +27,8 @@ public record HttpDownloadJob : IJobDefinitionWithStart<HttpDownloadJob, Absolut
2627
private static readonly HttpClient Client = BuildClient();
2728
#pragma warning restore EXTEXP0001
2829

30+
private static readonly ResiliencePipeline<AbsolutePath> ResiliencePipeline = BuildResiliencePipeline();
31+
2932
/// <summary>
3033
/// Logger.
3134
/// </summary>
@@ -81,10 +84,23 @@ public static IJobTask<HttpDownloadJob, AbsolutePath> Create(
8184
return monitor.Begin<HttpDownloadJob, AbsolutePath>(job);
8285
}
8386

84-
/// <summary>
85-
/// Execute the job
86-
/// </summary>
87+
/// <inheritdoc/>
8788
public async ValueTask<AbsolutePath> StartAsync(IJobContext<HttpDownloadJob> context)
89+
{
90+
var result = await ResiliencePipeline.ExecuteAsync(
91+
callback: static (tuple, _) =>
92+
{
93+
var (self, context) = tuple;
94+
return self.StartAsyncImpl(context);
95+
},
96+
state: (this, context),
97+
cancellationToken: context.CancellationToken
98+
);
99+
100+
return result;
101+
}
102+
103+
private async ValueTask<AbsolutePath> StartAsyncImpl(IJobContext<HttpDownloadJob> context)
88104
{
89105
await context.YieldAsync();
90106
await FetchMetadata(context);
@@ -168,9 +184,9 @@ public async ValueTask<AbsolutePath> StartAsync(IJobContext<HttpDownloadJob> con
168184
{
169185
await response.Content.CopyToAsync(outputStream, context.CancellationToken);
170186
}
171-
catch (HttpRequestException e)
187+
catch (HttpIOException e)
172188
{
173-
Logger.LogError(e, "Http error while downloading from `{PageUri}`: Server=`{Server}`,Http Version=`{Version}`", DownloadPageUri, response.Headers.Server.ToString(), response.Version);
189+
Logger.LogWarning(e, "Exception while downloading from `{PageUri}`, downloaded `{DownloadedBytes}` from `{TotalBytes}` bytes", DownloadPageUri, outputStream.Position, outputStream.Length);
174190
throw;
175191
}
176192
finally
@@ -249,7 +265,23 @@ private async ValueTask FetchMetadata(IJobContext context)
249265
var contentLength = response.Content.Headers.ContentLength;
250266
ContentLength = contentLength is not null ? Size.FromLong(contentLength.Value) : Optional<Size>.None;
251267
}
252-
268+
269+
private static ResiliencePipeline<AbsolutePath> BuildResiliencePipeline()
270+
{
271+
var pipeline = new ResiliencePipelineBuilder<AbsolutePath>()
272+
.AddRetry(new RetryStrategyOptions<AbsolutePath>
273+
{
274+
ShouldHandle = new PredicateBuilder<AbsolutePath>().Handle<HttpIOException>(),
275+
BackoffType = DelayBackoffType.Exponential,
276+
UseJitter = true,
277+
MaxRetryAttempts = 3,
278+
Delay = TimeSpan.FromSeconds(3),
279+
})
280+
.Build();
281+
282+
return pipeline;
283+
}
284+
253285
[Experimental("EXTEXP0001")]
254286
private static HttpClient BuildClient()
255287
{

0 commit comments

Comments
 (0)