/// <summary> /// Initializes a new instance of the <see cref="IntuitRetryingEventArgs"/> class. /// </summary> /// <param name="currentRetryCount">The current retry attempt count.</param> /// <param name="delay">The delay indicating how long the current thread will be suspended for before the next iteration will be invoked.</param> /// <param name="lastException">The exception which caused the retry conditions to occur.</param> public IntuitRetryingEventArgs(int currentRetryCount, TimeSpan delay, System.Exception lastException) { IntuitRetryHelper.IsArgumentNull(lastException, "lastException"); this.CurrentRetryCount = currentRetryCount; this.Delay = delay; this.LastException = lastException; }
/// <summary> /// Initializes a new instance of the <see cref="IntuitRetryPolicy"/> class. /// </summary> /// <param name="retryCount">The number of retry attempts.</param> /// <param name="retryInterval">The time interval between retries.</param> public IntuitRetryPolicy(int retryCount, TimeSpan retryInterval) { IntuitRetryHelper.ArgumentNotNegativeValue(retryCount, "retryCount"); IntuitRetryHelper.ArgumentNotNegativeValue(retryInterval.Ticks, "retryInterval"); this.retryCount = retryCount; this.retryInterval = retryInterval; this.shouldRetry = this.GetShouldFixedRetry(); }
/// <summary> /// Initializes a new instance of the <see cref="IntuitRetryPolicy"/> class. /// </summary> /// <param name="retryCount">The number of retry attempts.</param> /// <param name="initialInterval">The initial interval that will apply for the first retry.</param> /// <param name="increment">The incremental time value that will be used for calculating the progressive delay between retries.</param> public IntuitRetryPolicy(int retryCount, TimeSpan initialInterval, TimeSpan increment) { IntuitRetryHelper.ArgumentNotNegativeValue(retryCount, "retryCount"); IntuitRetryHelper.ArgumentNotNegativeValue(initialInterval.Ticks, "initialInterval"); IntuitRetryHelper.ArgumentNotNegativeValue(increment.Ticks, "increment"); this.retryCount = retryCount; this.initialInterval = initialInterval; this.increment = increment; this.shouldRetry = this.GetShouldIncrementalRetry(); }
/// <summary> /// Repetitively executes the specified asynchronous action while it satisfies the current retry policy. /// </summary> /// <param name="beginAction">The begin method of the async pattern.</param> /// <param name="endAction">The end method of the async pattern.</param> /// <param name="successHandler">The action to perform when the async operation is done.</param> /// <param name="faultHandler">The fault handler delegate that will be triggered if the operation cannot be successfully invoked despite retry attempts.</param> public void ExecuteAction(Action <AsyncCallback> beginAction, Action <IAsyncResult> endAction, Action successHandler, Action <Exception> faultHandler) { IntuitRetryHelper.IsArgumentNull(beginAction, "beginAction"); IntuitRetryHelper.IsArgumentNull(endAction, "endAction"); IntuitRetryHelper.IsArgumentNull(successHandler, "successHandler"); IntuitRetryHelper.IsArgumentNull(faultHandler, "faultHandler"); this.ExecuteAction <object>( beginAction, ar => { endAction(ar); return(null); }, _ => successHandler(), faultHandler); }
/// <summary> /// Initializes a new instance of the <see cref="IntuitRetryPolicy"/> class. /// </summary> /// <param name="retryCount">The maximum number of retry attempts.</param> /// <param name="minBackoff">The minimum back-off time</param> /// <param name="maxBackoff">The maximum back-off time.</param> /// <param name="deltaBackoff">The value which will be used to calculate a random delta in the exponential delay between retries.</param> public IntuitRetryPolicy(int retryCount, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff) { IntuitRetryHelper.ArgumentNotNegativeValue(retryCount, "retryCount"); IntuitRetryHelper.ArgumentNotNegativeValue(minBackoff.Ticks, "minBackoff"); IntuitRetryHelper.ArgumentNotNegativeValue(maxBackoff.Ticks, "maxBackoff"); IntuitRetryHelper.ArgumentNotNegativeValue(deltaBackoff.Ticks, "deltaBackoff"); IntuitRetryHelper.ArgumentNotGreaterThan(minBackoff.TotalMilliseconds, maxBackoff.TotalMilliseconds, "minBackoff"); this.retryCount = retryCount; this.minBackOff = minBackoff; this.maxBackOff = maxBackoff; this.deltaBackOff = deltaBackoff; this.shouldRetry = this.GetShouldExponentialBackOffRetry(); }
/// <summary> /// Repetitively executes the specified action while it satisfies the current retry policy. /// </summary> /// <typeparam name="TResult">The type of result expected from the executable action.</typeparam> /// <param name="func">A delegate representing the executable action which returns the result of type R.</param> /// <returns>The result from the action.</returns> private TResult ExecuteAction <TResult>(Func <TResult> func) { IntuitRetryHelper.IsArgumentNull(func, "func"); int retryCount = 1; TimeSpan delay = TimeSpan.Zero; Exception lastError; while (true) { lastError = null; try { return(func()); } catch (RetryExceededException retryExceededException) { // The user code can throw a RetryLimitExceededException to force the exit from the retry loop. // The RetryLimitExceeded exception can have an inner exception attached to it. This is the exception // which we will have to throw up the stack so that callers can handle it. if (retryExceededException.InnerException != null) { throw retryExceededException.InnerException; } else { return(default(TResult)); } } catch (Exception ex) { lastError = ex; if (!IsTransient(lastError) || ((this.ExtendedRetryException != null) && this.ExtendedRetryException.IsRetryException(ex))) { throw; } if (!this.shouldRetry(retryCount++, lastError, out delay)) { WebException webException = ex as WebException; string errorString = string.Empty; if (webException != null) { // If not null then check the response property of the webException object. if (webException.Response != null) { // There is a response from the Ids server. Cast it to HttpWebResponse. HttpWebResponse errorResponse = (HttpWebResponse)webException.Response; // Get the status code description of the error response. string statusCodeDescription = errorResponse.StatusCode.ToString(); // Get the status code of the error response. int statusCode = (int)errorResponse.StatusCode; ICompressor responseCompressor = CoreHelper.GetCompressor(this.context, false); if (!string.IsNullOrWhiteSpace(errorResponse.ContentEncoding) && responseCompressor != null) { using (var responseStream = errorResponse.GetResponseStream()) //Check for decompressing { using (var decompressedStream = responseCompressor.Decompress(responseStream)) { // Get the response stream. StreamReader reader = new StreamReader(decompressedStream); //StreamReader reader = new StreamReader(responseStream); // Read the Stream errorString = reader.ReadToEnd(); // Close reader reader.Close(); } } } else { using (Stream responseStream = errorResponse.GetResponseStream()) { // Get the response stream. StreamReader reader = new StreamReader(responseStream); // Read the Stream errorString = reader.ReadToEnd(); // Close reader reader.Close(); } } // Log the error string to disk. CoreHelper.GetRequestLogging(this.context).LogPlatformRequests(errorString, false); } } Core.Rest.FaultHandler fault = new Core.Rest.FaultHandler(this.context); IdsException idsException = fault.ParseErrorResponseAndPrepareException(errorString); if (idsException != null) { throw new RetryExceededException(webException.Message, webException.Status.ToString(), webException.Source, idsException); } else if (webException != null) { throw new RetryExceededException(webException.Message, webException.Status.ToString(), webException.Source, webException); } throw new RetryExceededException(ex.Message, ex); } } // Perform an extra check in the delay interval. Should prevent from accidentally ending up with the value of -1 that will block a thread indefinitely. // In addition, any other negative numbers will cause an ArgumentOutOfRangeException fault that will be thrown by Thread.Sleep. if (delay.TotalMilliseconds < 0) { delay = TimeSpan.Zero; } this.OnRetrying(retryCount - 1, lastError, delay); if (retryCount > 2 && delay > TimeSpan.Zero) { Thread.Sleep(delay); } } }
public void ExecuteAction <TResult>(Action <AsyncCallback> beginAction, Func <IAsyncResult, TResult> endAction, Action <TResult> successHandler, Action <Exception> faultHandler) { IntuitRetryHelper.IsArgumentNull(beginAction, "beginAction"); IntuitRetryHelper.IsArgumentNull(endAction, "endAction"); IntuitRetryHelper.IsArgumentNull(successHandler, "successHandler"); IntuitRetryHelper.IsArgumentNull(faultHandler, "faultHandler"); int retryCount = 0; AsyncCallback endInvoke = null; Func <Action, bool> executeWithRetry = null; // Configure a custom callback delegate that invokes the end operation and the success handler if the operation succeedes endInvoke = ar => { var result = default(TResult); if (executeWithRetry(() => result = endAction(ar))) { successHandler(result); } }; // Utility delegate to invoke an action and implement the core retry logic // If the action succeeds (i.e. does not throw an exception) it returns true. // If the action throws, it analizes it for retries. If a retry is required, it restarts the async operation; otherwise, it invokes the fault handler. executeWithRetry = a => { try { // Invoke the callback delegate which can throw an exception if the main async operation has completed with a fault. a(); return(true); } catch (Exception ex) { // Capture the original exception for analysis. var lastError = ex; // Handling of RetryLimitExceededException needs to be done separately. This exception type indicates the application's intent to exit from the retry loop. if (lastError is RetryExceededException) { if (lastError.InnerException != null) { faultHandler(lastError.InnerException); return(false); } else { faultHandler(lastError); return(false); } } else { var delay = TimeSpan.Zero; // Check if we should continue retrying on this exception. If not, invoke the fault handler so that user code can take control. if (!IsTransient(lastError) || ((this.ExtendedRetryException != null) && this.ExtendedRetryException.IsRetryException(ex))) { faultHandler(lastError); return(false); } else { if (delay.TotalMilliseconds < 0) { delay = TimeSpan.Zero; } retryCount = retryCount + 1; if (!this.shouldRetry(retryCount, lastError, out delay)) { WebException webException = ex as WebException; string errorString = string.Empty; if (webException != null) { // If not null then check the response property of the webException object. if (webException.Response != null) { // There is a response from the Ids server. Cast it to HttpWebResponse. HttpWebResponse errorResponse = (HttpWebResponse)webException.Response; // Get the status code description of the error response. string statusCodeDescription = errorResponse.StatusCode.ToString(); // Get the status code of the error response. int statusCode = (int)errorResponse.StatusCode; ICompressor responseCompressor = CoreHelper.GetCompressor(this.context, false); if (!string.IsNullOrWhiteSpace(errorResponse.ContentEncoding) && responseCompressor != null) { using (var responseStream = errorResponse.GetResponseStream()) //Check for decompressing { using (var decompressedStream = responseCompressor.Decompress(responseStream)) { // Get the response stream. StreamReader reader = new StreamReader(decompressedStream); //StreamReader reader = new StreamReader(responseStream); // Read the Stream errorString = reader.ReadToEnd(); // Close reader reader.Close(); } } } else { using (Stream responseStream = errorResponse.GetResponseStream()) { // Get the response stream. StreamReader reader = new StreamReader(responseStream); // Read the Stream errorString = reader.ReadToEnd(); // Close reader reader.Close(); } } // Log the error string to disk. CoreHelper.GetRequestLogging(this.context).LogPlatformRequests(errorString, false); } } Core.Rest.FaultHandler fault = new Core.Rest.FaultHandler(this.context); IdsException idsException = fault.ParseErrorResponseAndPrepareException(errorString); if (idsException != null) { faultHandler(new RetryExceededException(webException.Message, webException.Status.ToString(), webException.Source, idsException)); return(false); } else if (webException != null) { faultHandler(new RetryExceededException(webException.Message, webException.Status.ToString(), webException.Source, webException)); return(false); } faultHandler(new RetryExceededException(ex.Message, ex)); return(false); } // Notify the respective subscribers about this exception. this.OnRetrying(retryCount, lastError, delay); // Sleep for the defined interval before repetitively executing the main async operation. if (retryCount > 2 && delay > TimeSpan.Zero) { Thread.Sleep(delay); } executeWithRetry(() => beginAction(endInvoke)); } } return(false); } }; // Invoke the the main async operation for the first time which should return control to the caller immediately. executeWithRetry(() => beginAction(endInvoke)); }
/// <summary> /// Repetitively executes the specified action while it satisfies the current retry policy. /// </summary> /// <param name="action">A delegate representing the executable action which doesn't return any results.</param> public void ExecuteAction(Action action) { IntuitRetryHelper.IsArgumentNull(action, "action"); this.ExecuteAction(() => { action(); return(default(object)); }); }
/// <summary> /// Repetitively executes the specified action while it satisfies the current retry policy. /// </summary> /// <typeparam name="TResult">The type of result expected from the executable action.</typeparam> /// <param name="func">A delegate representing the executable action which returns the result of type R.</param> /// <returns>The result from the action.</returns> private TResult ExecuteAction <TResult>(Func <TResult> func) { IntuitRetryHelper.IsArgumentNull(func, "func"); int retryCount = 1; TimeSpan delay = TimeSpan.Zero; Exception lastError; while (true) { lastError = null; try { return(func()); } catch (RetryExceededException retryExceededException) { // The user code can throw a RetryLimitExceededException to force the exit from the retry loop. // The RetryLimitExceeded exception can have an inner exception attached to it. This is the exception // which we will have to throw up the stack so that callers can handle it. if (retryExceededException.InnerException != null) { throw retryExceededException.InnerException; } else { return(default(TResult)); } } catch (Exception ex) { lastError = ex; if (!IsTransient(lastError) || ((this.ExtendedRetryException != null) && this.ExtendedRetryException.IsRetryException(ex))) { throw; } if (!this.shouldRetry(retryCount++, lastError, out delay)) { WebException webException = ex as WebException; if (webException != null) { throw new RetryExceededException(webException.Message, webException.Status.ToString(), webException.Source, webException); } throw new RetryExceededException(ex.Message, ex); } } // Perform an extra check in the delay interval. Should prevent from accidentally ending up with the value of -1 that will block a thread indefinitely. // In addition, any other negative numbers will cause an ArgumentOutOfRangeException fault that will be thrown by Thread.Sleep. if (delay.TotalMilliseconds < 0) { delay = TimeSpan.Zero; } this.OnRetrying(retryCount - 1, lastError, delay); if (retryCount > 2 && delay > TimeSpan.Zero) { Thread.Sleep(delay); } } }
public void ExecuteAction <TResult>(Action <AsyncCallback> beginAction, Func <IAsyncResult, TResult> endAction, Action <TResult> successHandler, Action <Exception> faultHandler) { IntuitRetryHelper.IsArgumentNull(beginAction, "beginAction"); IntuitRetryHelper.IsArgumentNull(endAction, "endAction"); IntuitRetryHelper.IsArgumentNull(successHandler, "successHandler"); IntuitRetryHelper.IsArgumentNull(faultHandler, "faultHandler"); int retryCount = 0; AsyncCallback endInvoke = null; Func <Action, bool> executeWithRetry = null; // Configure a custom callback delegate that invokes the end operation and the success handler if the operation succeedes endInvoke = ar => { var result = default(TResult); if (executeWithRetry(() => result = endAction(ar))) { successHandler(result); } }; // Utility delegate to invoke an action and implement the core retry logic // If the action succeeds (i.e. does not throw an exception) it returns true. // If the action throws, it analizes it for retries. If a retry is required, it restarts the async operation; otherwise, it invokes the fault handler. executeWithRetry = a => { try { // Invoke the callback delegate which can throw an exception if the main async operation has completed with a fault. a(); return(true); } catch (Exception ex) { // Capture the original exception for analysis. var lastError = ex; // Handling of RetryLimitExceededException needs to be done separately. This exception type indicates the application's intent to exit from the retry loop. if (lastError is RetryExceededException) { if (lastError.InnerException != null) { faultHandler(lastError.InnerException); return(false); } else { faultHandler(lastError); return(false); } } else { var delay = TimeSpan.Zero; // Check if we should continue retrying on this exception. If not, invoke the fault handler so that user code can take control. if (!IsTransient(lastError) || ((this.ExtendedRetryException != null) && this.ExtendedRetryException.IsRetryException(ex))) { faultHandler(lastError); return(false); } else { if (delay.TotalMilliseconds < 0) { delay = TimeSpan.Zero; } retryCount = retryCount + 1; if (!this.shouldRetry(retryCount, lastError, out delay)) { WebException webException = ex as WebException; if (webException != null) { faultHandler(new RetryExceededException(webException.Message, webException.Status.ToString(), webException.Source, webException)); return(false); } faultHandler(new RetryExceededException(ex.Message, ex)); return(false); } // Notify the respective subscribers about this exception. this.OnRetrying(retryCount, lastError, delay); // Sleep for the defined interval before repetitively executing the main async operation. if (retryCount > 2 && delay > TimeSpan.Zero) { Thread.Sleep(delay); } executeWithRetry(() => beginAction(endInvoke)); } } return(false); } }; // Invoke the the main async operation for the first time which should return control to the caller immediately. executeWithRetry(() => beginAction(endInvoke)); }