private async Task RunAsync() { var reader = _requests.Reader; await foreach (var request in reader.ReadAllAsync()) { bool retry; do { retry = false; try { if (_isUnlimited) { var bucket = _rateLimiter.GetBucket(request.Route, false); if (bucket != this) { Logger.LogDebug("Route {0} is moving the request to the limited bucket.", request.Route); bucket.Post(request); break; } } var now = DateTimeOffset.UtcNow; var globalResetsAt = _rateLimiter._globalResetsAt; var isGloballyRateLimited = globalResetsAt > now; if (Remaining == 0 || isGloballyRateLimited) { var delay = isGloballyRateLimited ? globalResetsAt.Value - now : ResetsAt - now; if (delay > TimeSpan.Zero) { var maximumDelayDuration = (request.Options as DefaultRestRequestOptions)?.MaximumDelayDuration ?? _rateLimiter.MaximumDelayDuration; if (maximumDelayDuration != Timeout.InfiniteTimeSpan && delay > maximumDelayDuration) { Logger.LogDebug("Route {0} is rate-limited - throwing as the delay {1} exceeds the maximum delay duration.", request.Route, delay); request.Complete(new MaximumRateLimitDelayExceededException(delay, isGloballyRateLimited)); break; } var level = request.Route.BaseRoute.Equals(Route.Channel.CreateReaction) ? LogLevel.Debug : LogLevel.Information; Logger.Log(level, "Route {0} is rate-limited - delaying for {1}.", request.Route, delay); await Task.Delay(delay).ConfigureAwait(false); } } var response = await _rateLimiter.ApiClient.Requester.ExecuteAsync(request).ConfigureAwait(false); if (_rateLimiter.UpdateBucket(request.Route, response.HttpResponse)) { Logger.LogInformation("Route {0} is retrying the last request due to a hit rate-limit.", request.Route); retry = true; } else { request.Complete(response); request.Dispose(); } } catch (Exception ex) { Logger.LogError(ex, "Route {0} encountered an exception while processing a request.", request.Route); request.Dispose(); } }while (retry); } }
private async Task RunAsync() { //Logger.LogTrace("Bucket {0} is running {1} requests.", Id, _requests.Count); LinkedListNode <IRestRequest> requestNode; while ((requestNode = _requests.First) != null) { var request = requestNode.Value; _requests.RemoveFirst(); if (Remaining == 0) { var delay = ResetsAt - DateTimeOffset.UtcNow; if (delay > TimeSpan.Zero) { if (_rateLimiter.MaximumDelayDuration != Timeout.InfiniteTimeSpan && delay > _rateLimiter.MaximumDelayDuration) { Logger.LogWarning("Bucket {0} is pre-emptively rate-limiting, throwing as the delay {1} exceeds the maximum delay duration.", Id, delay); // TODO: MaximumDelayDurationExceededException request.Complete(new TimeoutException()); continue; } var level = request.Route.BaseRoute == Route.Channel.CreateReaction ? LogLevel.Debug : LogLevel.Information; Logger.Log(level, "Bucket {0} is pre-emptively rate-limiting, delaying for {1}.", Id, delay); await Task.Delay(delay).ConfigureAwait(false); } } // If this bucket is unlimited we check if there's a proper bucket created and move the requests accordingly. if (IsUnlimited) { var bucket = _rateLimiter.GetBucket(request.Route, false); if (bucket != this) { Logger.LogDebug("Bucket {0} is moving the request to the limited bucket {1}.", Id, bucket); bucket.Enqueue(request); continue; } } try { var response = await _rateLimiter.ApiClient.Requester.ExecuteAsync(request).ConfigureAwait(false); if (_rateLimiter.UpdateBucket(request.Route, response.HttpResponse)) { Logger.LogInformation("Bucket {0} is re-enqueuing the last request due to a hit rate-limit.", Id); _requests.AddFirst(request); } else { request.Complete(response); request.Dispose(); } } catch (Exception ex) { Logger.LogError(ex, "Bucket {0} encountered an exception when executing a request.", Id); } } }