private async Task <TResult> HandleExceptionAsync <TResult>(Exception e, Func <Task <TResult> > func, int retries) { if (_retryStrategy.ShouldRetry(e, retries)) { return(await TryAsync(func, retries + 1)); } throw ApiException.CreateFromException(e); }
/// <summary> /// Gets a TimeSpan value which defines how long to wait before trying again after an unsuccessful attempt to /// dequeue a message. /// </summary> /// <param name="attempt"> /// The number of attempts carried out so far. That is, after the first attempt (for the first retry), attempt /// will be set to 1, after the second attempt it is set to 2, and so on. /// </param> /// <returns> /// A TimeSpan value which defines how long to wait before the next attempt. /// </returns> public TimeSpan GetWaitTime(int attempt) { if (inner.ShouldRetry(attempt)) { return(inner.GetWaitTime(attempt)); } // We don't know the last wait time used by the inner strategy yet, so let's go and discover it. int lastSupportedAttempt = 1; while (inner.ShouldRetry(lastSupportedAttempt + 1)) { lastSupportedAttempt++; } return(inner.GetWaitTime(lastSupportedAttempt)); }
private TResult HandleException <TResult>(Exception e, Func <TResult> func, int retries) { if (_retryStrategy.ShouldRetry(e, retries)) { return(Try(func, retries + 1)); } throw e; }
public override async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken token) { if (!_retryStrategy.ShouldRetry(context, jobException)) { return; } _logger.Information("job {job} will be retried", context.JobDetail.Key); var trigger = _retryStrategy.GetTrigger(context); await context.Scheduler.UnscheduleJob(context.Trigger.Key, token); await context.Scheduler.ScheduleJob(context.JobDetail, trigger, token); return; }
public static T Run <T>(Func <T> func, IRetryStrategy strategy) { var failCount = 0; while (true) { try { return(func.Invoke()); } catch (Exception ex) { failCount++; if (!strategy.ShouldRetry(ex is AggregateException ? ex.InnerException : ex, failCount)) { throw; } } Task.Delay(strategy.NextWait(failCount)); } }
public static async Task <T> RunAsync <T>(Func <Task <T> > func, IRetryStrategy strategy) { var failCount = 0; while (true) { try { return(await func.Invoke().ConfigureAwait(false)); } catch (Exception ex) { failCount++; if (!strategy.ShouldRetry(ex is AggregateException ? ex.InnerException : ex, failCount)) { throw; } } await Task.Delay(strategy.NextWait(failCount)).ConfigureAwait(false); } }
private async Task <HttpResponseMessage> RequestWithRetriesAsync(Methods method, string endpoint, string content, CancellationToken cancellationToken = default(CancellationToken)) { var attempts = 0; var response = await RequestAsync(method, endpoint, content, cancellationToken).ConfigureAwait(false); attempts++; while (_retryStrategy.ShouldRetry(attempts, response)) { var timespan = _retryStrategy.GetNextDelay(attempts, response); if (timespan > TimeSpan.Zero) { await Task.Delay(timespan).ConfigureAwait(false); } response = await RequestAsync(method, endpoint, content, cancellationToken).ConfigureAwait(false); attempts++; } return(response); }
/// <summary> /// Consumes one message from the queue, applying the given IRetryStrategy to wait for a message if the queue /// is empty. /// </summary> /// <param name="dequeueStrategy"> /// An instance of IRetryStrategy which defines how long and how often to query the queue for a single message. /// </param> /// <param name="cancellationToken"> /// A CancellationToken to use to check if the operation should be cancelled. /// </param> /// <returns> /// True if a message was successfully consumed, false otherwise. /// </returns> public bool One(IRetryStrategy dequeueStrategy, CancellationToken cancellationToken) { if (null == dequeueStrategy) { throw new ArgumentNullException("dequeueStrategy"); } for (int i = 1; ; i++) { if (cancellationToken.IsCancellationRequested) { return(false); } using (JobExecutionContext context = JobExecutionContext.Dequeue(this)) { if (context.Empty) { if (dequeueStrategy.ShouldRetry(i)) { Task.Delay(dequeueStrategy.GetWaitTime(i), cancellationToken) .ContinueWith(NoopTaskContinuation) .Wait(); continue; } break; } using (new JobPerfContext(this, context)) { return(context.Execute()); } } } return(false); }
/// <summary> /// Sends a HTTP request to MercadoPago's APIs. /// </summary> /// <param name="request">Request data.</param> /// <param name="retryStrategy">Strategy to be used when it is necessary to retry the request.</param> /// <param name="cancellationToken">Cancellation token to cancel operation.</param> /// <returns>A Task with response data.</returns> public async Task <MercadoPagoResponse> SendAsync( MercadoPagoRequest request, IRetryStrategy retryStrategy, CancellationToken cancellationToken) { HttpResponseMessage httpResponse = null; int numberRetries = 0; Exception exception; HttpRequestMessage httpRequest; while (true) { httpRequest = CreateHttpRequest(request); try { httpResponse = await HttpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); exception = null; } catch (Exception ex) { exception = ex; } RetryResponse retryResponse = retryStrategy.ShouldRetry( httpRequest, httpResponse, IsRetryableError(exception, cancellationToken), numberRetries); if (!retryResponse.Retry) { break; } // Dispose HTTP response if if will retry if (httpResponse != null) { httpResponse.Dispose(); } numberRetries++; await Task.Delay(retryResponse.Delay).ConfigureAwait(false); } // Dispose HTTP request if (httpRequest != null) { httpRequest.Dispose(); } if (exception != null) { throw exception; } return(await MapResponse(httpResponse)); }
/// <summary> /// Consumes one message from the queue, applying the given IRetryStrategy to wait for a message if the queue /// is empty. /// </summary> /// <param name="dequeueStrategy"> /// An instance of IRetryStrategy which defines how long and how often to query the queue for a single message. /// </param> /// <param name="cancellationToken"> /// A CancellationToken to use to check if the operation should be cancelled. /// </param> /// <returns> /// True if a message was successfully consumed, false otherwise. /// </returns> public bool One(IRetryStrategy dequeueStrategy, CancellationToken cancellationToken) { if (null == dequeueStrategy) { throw new ArgumentNullException("dequeueStrategy"); } for (int i = 1; ; i++) { if (cancellationToken.IsCancellationRequested) { return false; } using (JobExecutionContext context = JobExecutionContext.Dequeue(this)) { if (context.Empty) { if (dequeueStrategy.ShouldRetry(i)) { Task.Delay(dequeueStrategy.GetWaitTime(i), cancellationToken) .ContinueWith(NoopTaskContinuation) .Wait(); continue; } break; } return context.Execute(); } } return false; }
/// <summary> /// Checks if we should retry after an unsuccessful attempt to dequeue a message. /// </summary> /// <param name="attempt"> /// The number of attempts carried out so far. That is, after the first attempt (for the first retry), attempt /// will be set to 1, after the second attempt it is set to 2, and so on. /// </param> /// <returns> /// True if another attempt to dequeue a message should be made, false otherwise. /// </returns> public bool ShouldRetry(int attempt) { return(inner.ShouldRetry(attempt)); }
bool WillRetry(Exception e, IEnumerator <TimeSpan> delays) => delays.MoveNext() && _retryStrategy.ShouldRetry(e);