mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-02-14 07:43:31 -07:00
Upgrade to Polly 8 usage
This commit is contained in:
parent
fbfff4e51f
commit
a58509fda8
|
|
@ -33,7 +33,7 @@ public class DiscordClient
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return await Http.ResponseResiliencePolicy.ExecuteAsync(
|
return await Http.ResponseResiliencePipeline.ExecuteAsync(
|
||||||
async innerCancellationToken =>
|
async innerCancellationToken =>
|
||||||
{
|
{
|
||||||
using var request = new HttpRequestMessage(HttpMethod.Get, new Uri(_baseUri, url));
|
using var request = new HttpRequestMessage(HttpMethod.Get, new Uri(_baseUri, url));
|
||||||
|
|
|
||||||
|
|
@ -53,44 +53,47 @@ internal partial class ExportAssetDownloader
|
||||||
|
|
||||||
Directory.CreateDirectory(_workingDirPath);
|
Directory.CreateDirectory(_workingDirPath);
|
||||||
|
|
||||||
await Http.ResiliencePolicy.ExecuteAsync(async () =>
|
await Http.ResiliencePipeline.ExecuteAsync(
|
||||||
{
|
async innerCancellationToken =>
|
||||||
// Download the file
|
|
||||||
using var response = await Http.Client.GetAsync(url, cancellationToken);
|
|
||||||
await using (var output = File.Create(filePath))
|
|
||||||
await response.Content.CopyToAsync(output, cancellationToken);
|
|
||||||
|
|
||||||
// Try to set the file date according to the last-modified header
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var lastModified = response.Content.Headers
|
// Download the file
|
||||||
.TryGetValue("Last-Modified")
|
using var response = await Http.Client.GetAsync(url, innerCancellationToken);
|
||||||
?.Pipe(
|
await using (var output = File.Create(filePath))
|
||||||
s =>
|
await response.Content.CopyToAsync(output, innerCancellationToken);
|
||||||
DateTimeOffset.TryParse(
|
|
||||||
s,
|
|
||||||
CultureInfo.InvariantCulture,
|
|
||||||
DateTimeStyles.None,
|
|
||||||
out var instant
|
|
||||||
)
|
|
||||||
? instant
|
|
||||||
: (DateTimeOffset?)null
|
|
||||||
);
|
|
||||||
|
|
||||||
if (lastModified is not null)
|
// Try to set the file date according to the last-modified header
|
||||||
|
try
|
||||||
{
|
{
|
||||||
File.SetCreationTimeUtc(filePath, lastModified.Value.UtcDateTime);
|
var lastModified = response.Content.Headers
|
||||||
File.SetLastWriteTimeUtc(filePath, lastModified.Value.UtcDateTime);
|
.TryGetValue("Last-Modified")
|
||||||
File.SetLastAccessTimeUtc(filePath, lastModified.Value.UtcDateTime);
|
?.Pipe(
|
||||||
|
s =>
|
||||||
|
DateTimeOffset.TryParse(
|
||||||
|
s,
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
DateTimeStyles.None,
|
||||||
|
out var instant
|
||||||
|
)
|
||||||
|
? instant
|
||||||
|
: (DateTimeOffset?)null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (lastModified is not null)
|
||||||
|
{
|
||||||
|
File.SetCreationTimeUtc(filePath, lastModified.Value.UtcDateTime);
|
||||||
|
File.SetLastWriteTimeUtc(filePath, lastModified.Value.UtcDateTime);
|
||||||
|
File.SetLastAccessTimeUtc(filePath, lastModified.Value.UtcDateTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
catch
|
||||||
catch
|
{
|
||||||
{
|
// This can apparently fail for some reason.
|
||||||
// This can apparently fail for some reason.
|
// Updating the file date is not a critical task, so we'll just ignore exceptions thrown here.
|
||||||
// Updating the file date is not a critical task, so we'll just ignore exceptions thrown here.
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/585
|
||||||
// https://github.com/Tyrrrz/DiscordChatExporter/issues/585
|
}
|
||||||
}
|
},
|
||||||
});
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
return _previousPathsByUrl[url] = filePath;
|
return _previousPathsByUrl[url] = filePath;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Security.Authentication;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
using Polly;
|
using Polly;
|
||||||
|
using Polly.Retry;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Utils;
|
namespace DiscordChatExporter.Core.Utils;
|
||||||
|
|
||||||
|
|
@ -31,29 +32,45 @@ public static class Http
|
||||||
&& IsRetryableStatusCode(hrex.StatusCode ?? HttpStatusCode.OK)
|
&& IsRetryableStatusCode(hrex.StatusCode ?? HttpStatusCode.OK)
|
||||||
);
|
);
|
||||||
|
|
||||||
public static IAsyncPolicy ResiliencePolicy { get; } =
|
public static ResiliencePipeline ResiliencePipeline { get; } =
|
||||||
Policy
|
new ResiliencePipelineBuilder()
|
||||||
.Handle<Exception>(IsRetryableException)
|
.AddRetry(
|
||||||
.WaitAndRetryAsync(4, i => TimeSpan.FromSeconds(Math.Pow(2, i) + 1));
|
new RetryStrategyOptions
|
||||||
|
|
||||||
public static IAsyncPolicy<HttpResponseMessage> ResponseResiliencePolicy { get; } =
|
|
||||||
Policy
|
|
||||||
.Handle<Exception>(IsRetryableException)
|
|
||||||
.OrResult<HttpResponseMessage>(m => IsRetryableStatusCode(m.StatusCode))
|
|
||||||
.WaitAndRetryAsync(
|
|
||||||
8,
|
|
||||||
(i, result, _) =>
|
|
||||||
{
|
{
|
||||||
// If rate-limited, use retry-after header as the guide.
|
ShouldHandle = new PredicateBuilder().Handle<Exception>(IsRetryableException),
|
||||||
// The response can be null here if an exception was thrown.
|
MaxRetryAttempts = 4,
|
||||||
if (result.Result?.Headers.RetryAfter?.Delta is { } retryAfter)
|
BackoffType = DelayBackoffType.Exponential,
|
||||||
{
|
Delay = TimeSpan.FromSeconds(1)
|
||||||
// Add some buffer just in case
|
}
|
||||||
return retryAfter + TimeSpan.FromSeconds(1);
|
)
|
||||||
}
|
.Build();
|
||||||
|
|
||||||
return TimeSpan.FromSeconds(Math.Pow(2, i) + 1);
|
public static ResiliencePipeline<HttpResponseMessage> ResponseResiliencePipeline { get; } =
|
||||||
},
|
new ResiliencePipelineBuilder<HttpResponseMessage>()
|
||||||
(_, _, _, _) => Task.CompletedTask
|
.AddRetry(
|
||||||
);
|
new RetryStrategyOptions<HttpResponseMessage>
|
||||||
|
{
|
||||||
|
ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
|
||||||
|
.Handle<Exception>(IsRetryableException)
|
||||||
|
.HandleResult(m => IsRetryableStatusCode(m.StatusCode)),
|
||||||
|
MaxRetryAttempts = 8,
|
||||||
|
DelayGenerator = args =>
|
||||||
|
{
|
||||||
|
// If rate-limited, use retry-after header as the guide.
|
||||||
|
// The response can be null here if an exception was thrown.
|
||||||
|
if (args.Outcome.Result?.Headers.RetryAfter?.Delta is { } retryAfter)
|
||||||
|
{
|
||||||
|
// Add some buffer just in case
|
||||||
|
return ValueTask.FromResult<TimeSpan?>(
|
||||||
|
retryAfter + TimeSpan.FromSeconds(1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ValueTask.FromResult<TimeSpan?>(
|
||||||
|
TimeSpan.FromSeconds(Math.Pow(2, args.AttemptNumber) + 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.Build();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue