/// <summary>Pause for a length of time equal to 'exp(backOffContext.expSeed)'</summary> /// <param name="backOffContext">The back off context.</param> public void BackOff(IBackOffContext backOffContext) { var context = (ExponentialBackOffContext)backOffContext; try { long sleepTime = context.GetSleepAndIncrement(); Logger.Debug(m => m("Sleeping for {0}", sleepTime)); this.sleeper.Sleep(sleepTime); } catch (ThreadInterruptedException e) { throw new BackOffInterruptedException("Thread interrupted while sleeping", e); } }
/// <summary>Delegates directly to the <see cref="DoBackOff"/> method without passing on the <see cref="IBackOffContext"/> argument which is not needed for stateless implementations.</summary> /// <param name="backOffContext">The back off context.</param> public void BackOff(IBackOffContext backOffContext) { this.DoBackOff(); }
protected T DoExecute <T>(object retryCallback, object recoveryCallback, IRetryState state) { if (retryCallback == null || (!(retryCallback is IRetryCallback <T>) & !(retryCallback is Func <IRetryContext, T>))) { throw new ArgumentException("retryCallback must be an IRetryCallback<T> or a Func<IRetryContext, T>"); } if (recoveryCallback != null && (!(recoveryCallback is IRetryCallback <T>) & !(recoveryCallback is Func <IRetryContext, T>))) { throw new ArgumentException("recoveryCallback must be an IRecoveryCallback<T> or a Func<IRetryContext, T>"); } var retryPolicy = this.retryPolicy; var backOffPolicy = this.backOffPolicy; // Allow the retry policy to initialise itself... var context = this.Open(retryPolicy, state); Logger.Trace(m => m("RetryContext retrieved: {0}", context)); // Make sure the context is available globally for clients who need // it... RetrySynchronizationManager.Register(context); Exception lastException = null; try { // Give clients a chance to enhance the context... var running = retryCallback is IRetryCallback <T>?this.DoOpenInterceptors((IRetryCallback <T>) retryCallback, context) : this.DoOpenInterceptors((Func <IRetryContext, T>)retryCallback, context); if (!running) { throw new TerminatedRetryException("Retry terminated abnormally by interceptor before first attempt"); } // Get or Start the backoff context... IBackOffContext backOffContext = null; IAttributeAccessor attributeAccessor = null; if (context is IAttributeAccessor) { attributeAccessor = context; var resource = attributeAccessor.GetAttribute("backOffContext"); if (resource is IBackOffContext) { backOffContext = (IBackOffContext)resource; } } if (backOffContext == null) { backOffContext = backOffPolicy.Start(context); if (attributeAccessor != null && backOffContext != null) { attributeAccessor.SetAttribute("backOffContext", backOffContext); } } // We allow the whole loop to be skipped if the policy or context // already forbid the first try. This is used in the case of // external retry to allow a recovery in handleRetryExhausted // without the callback processing (which would throw an exception). while (this.CanRetry(retryPolicy, context) && !context.ExhaustedOnly) { try { Logger.Debug(m => m("Retry: count={0}", context.RetryCount)); // Reset the last exception, so if we are successful // the close interceptors will not think we failed... lastException = null; return(retryCallback is IRetryCallback <T>?((IRetryCallback <T>)retryCallback).DoWithRetry(context) : ((Func <IRetryContext, T>)retryCallback).Invoke(context)); } catch (Exception e) { lastException = e; if (retryCallback is IRetryCallback <T> ) { this.DoOnErrorInterceptors((IRetryCallback <T>)retryCallback, context, e); } else { this.DoOnErrorInterceptors((Func <IRetryContext, T>)retryCallback, context, e); } try { this.RegisterThrowable(retryPolicy, state, context, e); } catch (Exception ex) { throw new TerminatedRetryException("Could not register exception", ex); } if (this.CanRetry(retryPolicy, context) && !context.ExhaustedOnly) { try { backOffPolicy.BackOff(backOffContext); } catch (BackOffInterruptedException ex) { lastException = e; // back off was prevented by another thread - fail // the retry Logger.Debug(m => m("Abort retry because interrupted: count={0}", context.RetryCount)); throw; } } Logger.Debug(m => m("Checking for rethrow: count={0}", context.RetryCount)); if (this.ShouldRethrow(retryPolicy, context, state)) { Logger.Debug(m => m("Rethrow in retry for policy: count={0}", context.RetryCount)); throw WrapIfNecessary(e); } } // A stateful attempt that can retry should have rethrown the // exception by now - i.e. we shouldn't get this far for a // stateful attempt if it can retry. } Logger.Debug(m => m("Retry failed last attempt: count={0}" + context.RetryCount)); if (context.ExhaustedOnly) { throw new ExhaustedRetryException("Retry exhausted after last attempt with no recovery path.", context.LastException); } if (recoveryCallback == null) { return(this.HandleRetryExhausted(default(IRecoveryCallback <T>), context, state)); } else if (recoveryCallback is IRecoveryCallback <T> ) { return(this.HandleRetryExhausted((IRecoveryCallback <T>)recoveryCallback, context, state)); } else { return(this.HandleRetryExhausted((Func <IRetryContext, T>)recoveryCallback, context, state)); } } finally { this.Close(retryPolicy, context, state, lastException == null); if (retryCallback is IRetryCallback <T> ) { this.DoCloseInterceptors((IRetryCallback <T>)retryCallback, context, lastException); } else { this.DoCloseInterceptors((Func <IRetryContext, T>)retryCallback, context, lastException); } RetrySynchronizationManager.Clear(); } }