/// <summary> /// This will execute a DocumentDB client method for you while handling retriable errors such as "too many requests". /// /// The caller must explicitly wrap the call they want to make in a lambda. This is so that WithRetry can /// execute the lambda in order to ask for the task multiple times instead of getting an instance created at /// WithRetry method invocation time. /// /// Example: "ExecuteResultWithRetry(() => YourCallHere(arguments, will, be, closured));" /// </summary> /// <param name="action"></param> /// <param name="resourceResponseHandler"></param> /// <param name="maxRetries"></param> /// <param name="maxTime"></param> /// <param name="shouldRetry"></param> /// <returns></returns> public static async Task ExecuteResultWithRetry(Func <Task> action, ResourceResponseHandler resourceResponseHandler, int maxRetries, TimeSpan maxTime, ShouldRetry shouldRetry) { // just wrap it in a task and call the main WithRetry method await ExecuteResultWithRetry <int>(async() => { await action(); return(0); }, resourceResponseHandler, maxRetries, maxTime, shouldRetry); }
/// <summary> /// This will execute a DocumentDB client method for you while handling retriable errors such as "too many requests". /// /// The caller must explicitly wrap the async call they want to make in a lambda. This is so that WithRetry can /// execute the lambda in order to ask for the task multiple times instead of getting an instance created at /// WithRetry method invocation time. /// /// Example: "ExecuteResultWithRetry(() => YourCallHere(arguments, will, be, closured));" /// </summary> /// <typeparam name="R"></typeparam> /// <param name="action"></param> /// <param name="resourceResponseHandler"></param> /// <param name="maxRetries"></param> /// <param name="maxTime"></param> /// <param name="shouldRetry"></param> /// <returns></returns> public static async Task <R> ExecuteResultWithRetry <R>(Func <Task <R> > action, ResourceResponseHandler resourceResponseHandler, int maxRetries, TimeSpan maxTime, ShouldRetry shouldRetry) { // time the execution Stopwatch sw = new Stopwatch(); sw.Start(); // count the retries int retries = 0; // remember the last exception Exception lastEx; while (true) { TimeSpan retryDelay = TimeSpan.Zero; try { var result = await action(); if (result is IResourceResponseBase) { resourceResponseHandler(result as IResourceResponseBase); } return(result); } catch (Exception clientException) { lastEx = clientException; try { retryDelay = shouldRetry(null, clientException); } catch (DocumentDbConflictResponse) { // conflict response was already handled properly internally to the ShouldRetry handler throw; } catch (DocumentDbUnexpectedResponse) { // unexpected response was already handled properly internally to the ShouldRetry handler throw; } catch (DocumentDbNonRetriableResponse) { // non-retriable response was already handled properly internally to the ShouldRetry handler throw; } catch (Exception ex) { // someone gave us a bad ShouldRetry handler throw new DocumentDbRetryHandlerError("The ShouldRetry handler threw an unexpected exception.", ex); } if (null == retryDelay) { // someone gave us a bad ShouldRetry handler throw new DocumentDbRetryHandlerError("The ShouldRetry handler returned a null delay.", clientException); } } if (sw.Elapsed > maxTime || retries > maxRetries) { throw new DocumentDbRetriesExceeded("Exceeded retry count (" + retries + " of " + maxRetries + ") or time (" + sw.Elapsed + " of " + maxTime + ") limit while retrying operation.", lastEx); } await Task.Delay(retryDelay); retries++; } }
/// <summary> /// This will execute a DocumentDB client method for you while handling retriable errors such as "too many requests". /// /// The caller must explicitly wrap the call they want to make in a lambda. This is so that WithRetry can /// execute the lambda in order to ask for the task multiple times instead of getting an instance created at /// WithRetry method invocation time. /// /// Example: "ExecuteResultWithRetry(() => YourCallHere(arguments, will, be, closured));" /// </summary> /// <typeparam name="R"></typeparam> /// <param name="action"></param> /// <param name="resourceResponseHandler"></param> /// <param name="maxRetries"></param> /// <param name="maxTime"></param> /// <param name="shouldRetry"></param> /// <returns></returns> public static async Task <R> ExecuteResultWithRetry <R>(Func <R> action, ResourceResponseHandler resourceResponseHandler, int maxRetries, TimeSpan maxTime, ShouldRetry shouldRetry) { // just wrap it in a task and call the main WithRetry method // the call to Task.Run must itself be closured because it takes a param return(await ExecuteResultWithRetry <R>(() => Task <R> .Run(action), resourceResponseHandler, maxRetries, maxTime, shouldRetry)); }