Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DSharpPlus.Rest/DiscordRestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ public DiscordRestClient(RestClientOptions options, string token, TokenType toke
options.Timeout,
logger ?? NullLogger.Instance,
options.MaximumRatelimitRetries,
options.RatelimitRetryDelayFallback,
options.InitialRequestTimeout,
(int)options.RatelimitRetryDelayFallback.TotalMilliseconds,
(int)options.InitialRequestTimeout.TotalMilliseconds,
options.MaximumConcurrentRestRequests
));

Expand Down
24 changes: 24 additions & 0 deletions DSharpPlus/Net/Rest/RateLimitStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using DSharpPlus.Exceptions;
using Microsoft.Extensions.Logging;
using Polly;

Expand Down Expand Up @@ -199,6 +201,28 @@ TState state
UpdateRateLimitBuckets(outcome.Result, hash, route, traceId);
}

if (outcome.Result?.StatusCode == HttpStatusCode.TooManyRequests)
{
string resetAfterRaw = outcome.Result.Headers.GetValues("X-RateLimit-Reset-After").Single();
TimeSpan resetAfter = TimeSpan.FromSeconds(double.Parse(resetAfterRaw));

string traceIdString = "";
if (this.logger.IsEnabled(LogLevel.Trace))
{
traceIdString = $"Request ID:{traceId}: ";
}

this.logger.LogWarning
(
"{TraceId}Hit Discord ratelimit on route {Route}, waiting for {ResetAfter}",
traceIdString,
route,
resetAfter
);

return Outcome.FromException<HttpResponseMessage>(new RetryableRatelimitException(resetAfter));
}

return outcome;
}
}
Expand Down
16 changes: 11 additions & 5 deletions DSharpPlus/Net/Rest/RestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ IOptions<TokenContainer> tokenContainer
options.Value.Timeout,
logger,
options.Value.MaximumRatelimitRetries,
options.Value.RatelimitRetryDelayFallback,
options.Value.InitialRequestTimeout,
(int)options.Value.RatelimitRetryDelayFallback.TotalMilliseconds,
(int)options.Value.InitialRequestTimeout.TotalMilliseconds,
options.Value.MaximumConcurrentRestRequests
)
{
Expand All @@ -64,7 +64,7 @@ internal RestClient
TimeSpan timeout,
ILogger logger,
int maxRetries = int.MaxValue,
double retryDelayFallback = 2.5,
int retryDelayFallback = 2500,
int waitingForHashMilliseconds = 200,
int maximumRequestsPerSecond = 15
)
Expand All @@ -88,8 +88,14 @@ internal RestClient
new()
{
DelayGenerator = result =>
ValueTask.FromResult<TimeSpan?>((result.Outcome.Exception as PreemptiveRatelimitException)?.ResetAfter
?? TimeSpan.FromSeconds(retryDelayFallback)),
{
return ValueTask.FromResult<TimeSpan?>(result.Outcome.Exception switch
{
PreemptiveRatelimitException preemptive => preemptive.ResetAfter,
RetryableRatelimitException real => real.ResetAfter,
_ => TimeSpan.FromMilliseconds(retryDelayFallback)
});
},
MaxRetryAttempts = maxRetries
}
)
Expand Down
6 changes: 3 additions & 3 deletions DSharpPlus/Net/Rest/RestClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ public sealed class RestClientOptions
/// <summary>
/// Specifies the delay to use when there was no delay information passed to the rest client. Defaults to 2.5 seconds.
/// </summary>
public double RatelimitRetryDelayFallback { get; set; } = 2.5;
public TimeSpan RatelimitRetryDelayFallback { get; set; } = TimeSpan.FromMilliseconds(2500);

/// <summary>
/// Specifies the amount of milliseconds we should be waiting for a ratelimit bucket hash to initialize.
/// Specifies the time we should be waiting for a ratelimit bucket hash to initialize.
/// </summary>
public int InitialRequestTimeout { get; set; } = 200;
public TimeSpan InitialRequestTimeout { get; set; } = TimeSpan.FromMilliseconds(200);

/// <summary>
/// Specifies the maximum rest requests to attempt concurrently. Defaults to 15.
Expand Down
13 changes: 13 additions & 0 deletions DSharpPlus/Net/Rest/RetryableRatelimitException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Diagnostics.CodeAnalysis;

namespace DSharpPlus.Net;

internal sealed class RetryableRatelimitException : Exception
{
public required TimeSpan ResetAfter { get; set; }

[SetsRequiredMembers]
public RetryableRatelimitException(TimeSpan resetAfter)
=> this.ResetAfter = resetAfter;
}