private static AsyncPolicyWrap <HttpResponseMessage> CreatePollyPolicy(PollyOptions pollyOptions) { var retries = Backoff.ConstantBackoff( TimeSpan.FromMilliseconds(pollyOptions.RetryBackOffMs), pollyOptions.RetryCount, true); // Timeout policy var timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromMilliseconds(pollyOptions.TimeoutMs)); // Wrap timeout policy with retry var retryWithTimeout = Policy .Handle <TimeoutRejectedException>() .Or <TimeoutException>() .OrResult <HttpResponseMessage>(IsTransientError) .WaitAndRetryAsync(retries) .WrapAsync(timeoutPolicy); // Wrap retry with circuit breaker var circuitBreaker = Policy .Handle <HttpRequestException>() .Or <TimeoutRejectedException>() .Or <TimeoutException>() .OrResult <HttpResponseMessage>(IsTransientError) .CircuitBreakerAsync( handledEventsAllowedBeforeBreaking: pollyOptions.ExceptionsBeforeBreak, durationOfBreak: TimeSpan.FromMilliseconds(pollyOptions.BreakDurationMs)) .WrapAsync(retryWithTimeout); return(circuitBreaker); }
public void Backoff_WithFastFirstEqualToTrue_ResultIsZero() { // Arrange var delay = TimeSpan.FromMilliseconds(1); const int retryCount = 3; const bool fastFirst = true; // Act IEnumerable <TimeSpan> result = Backoff.ConstantBackoff(delay, retryCount, fastFirst); // Assert result.Should().NotBeNull(); result.Should().HaveCount(retryCount); bool first = true; foreach (TimeSpan timeSpan in result) { if (first) { timeSpan.Should().Be(TimeSpan.Zero); first = false; } else { timeSpan.Should().Be(delay); } } }
public ConferenceRegistrationDbContext(DbContextOptions options) : base(options) { var delay = Backoff.ConstantBackoff(TimeSpan.FromMilliseconds(200), retryCount: 5, fastFirst: true); this.retryPolicy = Policy .Handle <Exception>() .WaitAndRetryAsync(delay, (exception, i, span) => { Trace.TraceWarning( $"An error occurred in attempt number {i} to access the database in ConferenceService: {exception.Message}"); }); }
public static SleepDurationProvider Constant(int retryCount, TimeSpan initialDelay) { if (retryCount < 0) { throw new ArgumentOutOfRangeException(nameof(retryCount), retryCount, "should be >= 0"); } if (initialDelay < TimeSpan.Zero) { throw new ArgumentOutOfRangeException(nameof(initialDelay), initialDelay, "should be >= 0ms"); } return(new SleepDurationProvider(retryCount, Backoff.ConstantBackoff(initialDelay, retryCount))); }
public OrderEventHandler(Func <ConferenceContext> contextFactory) { this.contextFactory = contextFactory; var delay = Backoff.ConstantBackoff(TimeSpan.FromMilliseconds(200), retryCount: 5, fastFirst: true); this.retryPolicy = Policy .Handle <Exception>() .WaitAndRetryAsync(delay, (exception, i, span) => { Trace.TraceWarning( $"An error occurred in attempt number {i} to access the database in ConferenceService: {exception.Message}"); }); }
internal static IEnumerable <TimeSpan> Constant(int retryCount, TimeSpan delay) { if (retryCount < 0) { throw new ArgumentOutOfRangeException(nameof(retryCount), retryCount, "should be >= 0"); } if (delay < TimeSpan.Zero) { throw new ArgumentOutOfRangeException(nameof(delay), delay, "should be >= 0ms"); } return(Backoff.ConstantBackoff(delay, retryCount)); }
public void Backoff_WithRetryEqualToZero_ResultIsEmpty() { // Arrange var delay = TimeSpan.FromMilliseconds(1); const int retryCount = 0; const bool fastFirst = false; // Act IEnumerable <TimeSpan> result = Backoff.ConstantBackoff(delay, retryCount, fastFirst); // Assert result.Should().NotBeNull(); result.Should().BeEmpty(); }
public void Backoff_WithRetryCountLessThanZero_ThrowsException() { // Arrange var delay = TimeSpan.FromMilliseconds(1); const int retryCount = -1; const bool fastFirst = false; // Act Action act = () => Backoff.ConstantBackoff(delay, retryCount, fastFirst); // Assert act.Should().Throw <ArgumentOutOfRangeException>() .And.ParamName.Should().Be("retryCount"); }
public void Backoff_WithDelayLessThanZero_ThrowsException() { // Arrange var delay = new TimeSpan(-1); const int retryCount = 3; const bool fastFirst = false; // Act Action act = () => Backoff.ConstantBackoff(delay, retryCount, fastFirst); // Assert act.Should().Throw <ArgumentOutOfRangeException>() .And.ParamName.Should().Be("delay"); }
/// <summary> /// Initializes a new instance of the <see cref="UserVoteController"/> class. /// </summary> /// <param name="logger">Logger implementation to send logs to the logger service.</param> /// <param name="telemetryClient">The Application Insights telemetry client.</param> /// <param name="userVoteStorageProvider">Instance of user vote storage provider to add and delete user vote.</param> /// <param name="teamIdeaStorageProvider">Instance of team post storage provider to update post and get information of posts.</param> /// <param name="teamIdeaSearchService">The team post search service dependency injection.</param> public UserVoteController( ILogger <UserVoteController> logger, TelemetryClient telemetryClient, IUserVoteStorageProvider userVoteStorageProvider, IIdeaStorageProvider teamIdeaStorageProvider, IIdeaSearchService teamIdeaSearchService) : base(telemetryClient) { this.logger = logger; this.userVoteStorageProvider = userVoteStorageProvider; this.teamIdeaStorageProvider = teamIdeaStorageProvider; this.teamIdeaSearchService = teamIdeaSearchService; this.retryPolicy = Policy.Handle <StorageException>(ex => ex.RequestInformation.HttpStatusCode == StatusCodes.Status412PreconditionFailed) .WaitAndRetryAsync(Backoff.ConstantBackoff(TimeSpan.FromMilliseconds(250), 25)); }
/// <summary> /// Create an constant backoff retry delay of 1, 1, 1, 1s. /// </summary> /// <typeparam name="T">The type of response.</typeparam> /// <param name="option">The retry option.</param> /// <param name="context">Request context.</param> /// <param name="action">Action to execute.</param> /// <param name="predicate">Handle result predicate.</param> /// <param name="onRetry">Handle custom on retries.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> public static Task <T> ConstantBackoff <T>( RetryOption option, Context context, Func <Context, Task <T> > action, Func <T, bool> predicate, Action <DelegateResult <T>, TimeSpan, Context> onRetry) { if (option is null) { throw new ArgumentNullException(nameof(option)); } var delay = Backoff.ConstantBackoff(TimeSpan.FromMilliseconds(option.MinDelayIsMs), retryCount: option.MaxRetry); return(HandleRetry(context, action, predicate, onRetry, delay)); }
public void Backoff_ResultIsConstant() { // Arrange var delay = TimeSpan.FromMilliseconds(10); const int retryCount = 3; const bool fastFirst = false; // Act IEnumerable <TimeSpan> result = Backoff.ConstantBackoff(delay, retryCount, fastFirst); // Assert result.Should().NotBeNull(); result.Should().HaveCount(retryCount); foreach (TimeSpan timeSpan in result) { timeSpan.Should().Be(delay); } }
public ConferenceService(IBus bus, ConferenceContext conferenceContext) { // NOTE: the database storage cannot be transactional consistent with the // event bus, so there is a chance that the conference state is saved // to the database but the events are not published. The recommended // mechanism to solve this lack of transaction support is to persist // failed events to a table in the same database as the conference, in a // queue that is retried until successful delivery of events is // guaranteed. This mechanism has been implemented for the AzureEventSourcedRepository // and that implementation can be used as a guide to implement it here too. context = conferenceContext; this.bus = bus; var delay = Backoff.ConstantBackoff(TimeSpan.FromMilliseconds(200), retryCount: 5, fastFirst: true); this.retryPolicy = Policy .Handle <Exception>() .WaitAndRetryAsync(delay, (exception, i, span) => { Trace.TraceWarning( $"An error occurred in attempt number {i} to access the database in ConferenceService: {exception.Message}"); }); }