private Exception CreateException(IList <Exception> exceptions, SqlRetryLogicBase retryLogic, bool manualCancellation = false) { AggregateException result = SqlReliabilityUtil.ConfigurableRetryFail(exceptions, retryLogic, manualCancellation); if (!manualCancellation) { SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|ERR|THROW> Exiting retry scope (exceeded the max allowed attempts = {2}).", TypeName, MethodBase.GetCurrentMethod().Name, retryLogic.NumberOfTries); } _retryLogicPool.Add(retryLogic); return(result); }
private static SqlRetryLogicBaseProvider CreateRetryLogicProvider(string sectionName, ISqlConfigurableRetryConnectionSection configSection) { string methodName = MethodBase.GetCurrentMethod().Name; SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Entry point.", TypeName, methodName); try { if (configSection == null) { throw SqlReliabilityUtil.ArgumentNull(nameof(configSection)); } // Create a SqlRetryLogicOption object from the discovered retry logic values var retryOption = new SqlRetryLogicOption() { NumberOfTries = configSection.NumberOfTries, DeltaTime = configSection.DeltaTime, MinTimeInterval = configSection.MinTimeInterval, MaxTimeInterval = configSection.MaxTimeInterval }; // Prepare the transient error lists if (!string.IsNullOrEmpty(configSection.TransientErrors)) { retryOption.TransientErrors = configSection.TransientErrors.Split(',').Select(x => Convert.ToInt32(x)).ToList(); } // Prepare the authorized SQL statement just for SqlCommands if (configSection is ISqlConfigurableRetryCommandSection cmdSection && !string.IsNullOrEmpty(cmdSection.AuthorizedSqlCondition)) { retryOption.AuthorizedSqlCondition = (x) => Regex.IsMatch(x, cmdSection.AuthorizedSqlCondition); } SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Successfully created a {2} object to use on creating a retry logic provider from the section '{3}'.", TypeName, methodName, nameof(SqlRetryLogicOption), sectionName); // Extract the SqlRetryLogicBaseProvider object from the given information var provider = ResolveRetryLogicProvider(configSection.RetryLogicType, configSection.RetryMethod, retryOption); SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Successfully created a {2} object from the section '{3}'.", TypeName, methodName, nameof(SqlRetryLogicBaseProvider), sectionName); return(provider); } catch (Exception e) { SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> {2}", TypeName, methodName, e); } SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.{1}|INFO> Due to an exception, the default non-retriable logic will be applied.", TypeName, methodName); // Return default provider if an exception occured. return(SqlConfigurableRetryFactory.CreateNoneRetryProvider()); }
/// <summary>Executes a function and applies retry logic, if enabled.</summary> public override async Task <TResult> ExecuteAsync <TResult>(object sender, Func <Task <TResult> > function, CancellationToken cancellationToken = default) { if (function == null) { throw SqlReliabilityUtil.ArgumentNull(nameof(function)); } SqlRetryLogicBase retryLogic = null; var exceptions = new List <Exception>(); retry: try { TResult result = await function.Invoke(); RetryLogicPoolAdd(retryLogic); return(result); } catch (Exception e) { if (RetryLogic.RetryCondition(sender) && RetryLogic.TransientPredicate(e)) { retryLogic = retryLogic ?? GetRetryLogic(); SqlClientEventSource.Log.TryTraceEvent("<sc.{0}.ExecuteAsync<TResult>|INFO> Found an action eligible for the retry policy (retried attempts = {1}).", TypeName, retryLogic.Current); exceptions.Add(e); if (retryLogic.TryNextInterval(out TimeSpan intervalTime)) { // The retrying event raises on each retry. ApplyRetryingEvent(sender, retryLogic, intervalTime, exceptions, e); await Task.Delay(intervalTime, cancellationToken); goto retry; } else { throw CreateException(exceptions, retryLogic); } } else { RetryLogicPoolAdd(retryLogic); throw; } } }
public SqlRetryLogic(int numberOfTries, SqlRetryIntervalBaseEnumerator enumerator, Predicate <Exception> transientPredicate, Predicate <string> preCondition) { Debug.Assert(enumerator != null, $"The '{nameof(enumerator)}' mustn't be null."); Debug.Assert(transientPredicate != null, $"The '{nameof(transientPredicate)}' mustn't be null."); if (!(numberOfTries > counterDefaultValue && numberOfTries <= maxAttempts)) { // The 'numberOfTries' should be between 1 and 60. throw SqlReliabilityUtil.ArgumentOutOfRange(nameof(numberOfTries), numberOfTries, counterDefaultValue + 1, maxAttempts); } NumberOfTries = numberOfTries; RetryIntervalEnumerator = enumerator; TransientPredicate = transientPredicate; PreCondition = preCondition; Current = counterDefaultValue; }
/// <include file='../../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlRetryIntervalBaseEnumerator.xml' path='docs/members[@name="SqlRetryIntervalBaseEnumerator"]/Validate/*' /> protected virtual void Validate(TimeSpan timeInterval, TimeSpan maxTimeInterval, TimeSpan minTimeInterval) { if (minTimeInterval < _minValue || minTimeInterval > _maxValue) { throw SqlReliabilityUtil.ArgumentOutOfRange(nameof(minTimeInterval), minTimeInterval, _minValue, _maxValue); } if (maxTimeInterval < _minValue || maxTimeInterval > _maxValue) { throw SqlReliabilityUtil.ArgumentOutOfRange(nameof(maxTimeInterval), maxTimeInterval, _minValue, _maxValue); } if (timeInterval < _minValue || timeInterval > _maxValue) { throw SqlReliabilityUtil.ArgumentOutOfRange(nameof(timeInterval), timeInterval, _minValue, _maxValue); } if (maxTimeInterval < minTimeInterval) { throw SqlReliabilityUtil.InvalidMinAndMaxPair(nameof(minTimeInterval), minTimeInterval, nameof(maxTimeInterval), maxTimeInterval); } }