Exemple #1
0
 /// <summary>Clean up the cache if necessary and close the context provided (if the flag indicates that processing was successful).</summary>
 /// <param name="retryPolicy">The retry policy.</param>
 /// <param name="context">The context.</param>
 /// <param name="state">The state.</param>
 /// <param name="succeeded">The succeeded.</param>
 protected void Close(IRetryPolicy retryPolicy, IRetryContext context, IRetryState state, bool succeeded)
 {
     if (state != null)
     {
         if (succeeded)
         {
             this.retryContextCache.Remove(state.GetKey());
             retryPolicy.Close(context);
         }
     }
     else
     {
         retryPolicy.Close(context);
     }
 }
Exemple #2
0
        /// <summary>The register exception.</summary>
        /// <param name="retryPolicy">The retry policy.</param>
        /// <param name="state">The state.</param>
        /// <param name="context">The context.</param>
        /// <param name="e">The e.</param>
        /// <exception cref="RetryException"></exception>
        protected void RegisterThrowable(IRetryPolicy retryPolicy, IRetryState state, IRetryContext context, Exception e)
        {
            if (state != null)
            {
                var key = state.GetKey();
                if (context.RetryCount > 0 && !this.retryContextCache.ContainsKey(key))
                {
                    throw new RetryException(
                              "Inconsistent state for failed item key: cache key has changed. "
                              + "Consider whether equals() or hashCode() for the key might be inconsistent, "
                              + "or if you need to supply a better key");
                }

                this.retryContextCache.Put(key, context);
            }

            retryPolicy.RegisterException(context, e);
        }
Exemple #3
0
        /// <summary>Delegate to the <see cref="IRetryPolicy"/> having checked in the cache for an existing value if the state is not null.</summary>
        /// <param name="retryPolicy">The retry policy.</param>
        /// <param name="state">The state.</param>
        /// <returns>The Spring.Retry.Retry.IRetryContext, either a new one or the one used last time the same state was encountered.</returns>
        protected IRetryContext Open(IRetryPolicy retryPolicy, IRetryState state)
        {
            if (state == null)
            {
                return(this.DoOpenInternal(retryPolicy));
            }

            var key = state.GetKey();

            if (state.IsForceRefresh())
            {
                return(this.DoOpenInternal(retryPolicy));
            }

            // If there is no cache hit we can avoid the possible expense of the
            // cache re-hydration.
            if (!this.retryContextCache.ContainsKey(key))
            {
                // The cache is only used if there is a failure.
                return(this.DoOpenInternal(retryPolicy));
            }

            var context = this.retryContextCache.Get(key);

            if (context == null)
            {
                if (this.retryContextCache.ContainsKey(key))
                {
                    throw new RetryException(
                              "Inconsistent state for failed item: no history found. "
                              + "Consider whether equals() or hashCode() for the item might be inconsistent, "
                              + "or if you need to supply a better ItemKeyGenerator");
                }

                // The cache could have been expired in between calls to
                // containsKey(), so we have to live with this:
                return(this.DoOpenInternal(retryPolicy));
            }

            return(context);
        }
Exemple #4
0
 /// <summary>Extension point for subclasses to decide on behaviour after catching an
 /// exception in a <see cref="IRetryCallback{T}"/>. Normal stateless behaviour is not
 /// to rethrow, and if there is state we rethrow.</summary>
 /// <param name="retryPolicy">The retry policy.</param>
 /// <param name="context">The context.</param>
 /// <param name="state">The state.</param>
 /// <returns>The System.Boolean.</returns>
 protected bool ShouldRethrow(IRetryPolicy retryPolicy, IRetryContext context, IRetryState state)
 {
     if (state == null)
     {
         return(false);
     }
     else
     {
         return(state.RollbackFor(context.LastException));
     }
 }
Exemple #5
0
        /// <summary>Actions to take after final attempt has failed. If there is state clean
        /// up the cache. If there is a recovery callback, execute that and return
        /// its result. Otherwise throw an exception.</summary>
        /// <param name="recoveryCallback">The callback for recovery (might be null).</param>
        /// <param name="context">The current retry context.</param>
        /// <param name="state">The state.</param>
        /// <returns>The T.</returns>
        /// <typeparam name="T">Type T.</typeparam>
        protected T HandleRetryExhausted <T>(Func <IRetryContext, T> recoveryCallback, IRetryContext context, IRetryState state)
        {
            if (state != null)
            {
                this.retryContextCache.Remove(state.GetKey());
            }

            if (recoveryCallback != null)
            {
                return(recoveryCallback.Invoke(context));
            }

            if (state != null)
            {
                Logger.Debug(m => m("Retry exhausted after last attempt with no recovery path."));
                throw new ExhaustedRetryException("Retry exhausted after last attempt with no recovery path", context.LastException);
            }

            throw WrapIfNecessary(context.LastException);
        }
Exemple #6
0
        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();
            }
        }
Exemple #7
0
 /// <summary>The execute.</summary>
 /// <param name="retryCallback">The retry callback.</param>
 /// <param name="recoveryCallback">The recovery callback.</param>
 /// <param name="retryState">The retry state.</param>
 /// <returns>The T.</returns>
 /// <typeparam name="T">Type T.</typeparam>
 public T Execute <T>(Func <IRetryContext, T> retryCallback, Func <IRetryContext, T> recoveryCallback, IRetryState retryState)
 {
     return(this.DoExecute <T>(retryCallback, recoveryCallback, retryState));
 }
Exemple #8
0
 /// <summary>The execute.</summary>
 /// <param name="retryCallback">The retry callback.</param>
 /// <param name="recoveryCallback">The recovery callback.</param>
 /// <param name="retryState">The retry state.</param>
 /// <returns>The T.</returns>
 /// <typeparam name="T">Type T.</typeparam>
 public T Execute <T>(IRetryCallback <T> retryCallback, IRecoveryCallback <T> recoveryCallback, IRetryState retryState)
 {
     return(this.DoExecute <T>(retryCallback, recoveryCallback, retryState));
 }