Skip to content

Commit

Permalink
Merge pull request #47 from dodopizza/sleep_duration_provider
Browse files Browse the repository at this point in the history
refactor: make sleep duration provider variation
  • Loading branch information
Ceridan authored Nov 1, 2020
2 parents 9853390 + 3edf65c commit f96bbab
Show file tree
Hide file tree
Showing 13 changed files with 144 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ public void Should_break_after_4_concurrent_calls()
{
const int retryCount = 5;
const int minimumThroughput = 2;
var retrySettings = new SimpleRetryPolicySettings(retryCount)
{
SleepDurationProvider = i => TimeSpan.FromMilliseconds(50)
};
var retrySettings = new RetryPolicySettings(
SleepDurationProvider.Constant(retryCount, TimeSpan.FromMilliseconds(50)));

var wrapper = Create.HttpClientWrapperWrapperBuilder
.WithStatusCode(HttpStatusCode.ServiceUnavailable)
.WithTimeoutOverall(TimeSpan.FromSeconds(5))
Expand All @@ -40,10 +39,9 @@ public async Task Should_Open_Circuit_Breaker_for_RU_and_do_not_affect_EE()
{
const int retryCount = 5;
const int minimumThroughput = 2;
var retrySettings = new SimpleRetryPolicySettings(retryCount)
{
SleepDurationProvider = i => TimeSpan.FromMilliseconds(50)
};
var retrySettings = new RetryPolicySettings(
SleepDurationProvider.Constant(retryCount, TimeSpan.FromMilliseconds(50)));

var circuitBreakerSettings = BuildCircuitBreakerSettings(minimumThroughput);
circuitBreakerSettings.IsHostSpecificOn = true;
var wrapper = Create.HttpClientWrapperWrapperBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private ResiliencePoliciesSettings BuildClientSettings()
{
OverallTimeoutPolicySettings = new OverallTimeoutPolicySettings { Timeout = _timeoutOverall },
TimeoutPerTryPolicySettings = new TimeoutPerTryPolicySettings { Timeout = _timeoutPerTry },
RetrySettings = _retrySettings ?? new JitterRetryPolicySettings(),
RetrySettings = _retrySettings ?? new RetryPolicySettings(),
CircuitBreakerSettings = defaultCircuitBreakerSettings
};
}
Expand Down
17 changes: 10 additions & 7 deletions src/Dodo.HttpClient.ResiliencePolicies.Tests/RetryPolicyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public class RetryPolicyTests
public async Task Should_retry_3_times_when_client_returns_503()
{
const int retryCount = 3;
var retrySettings = new SimpleRetryPolicySettings(retryCount);
var retrySettings = new RetryPolicySettings(
SleepDurationProvider.Constant(retryCount, TimeSpan.FromMilliseconds(1)));

var wrapper = Create.HttpClientWrapperWrapperBuilder
.WithStatusCode(HttpStatusCode.ServiceUnavailable)
Expand All @@ -35,9 +36,9 @@ public async Task Should_retry_3_times_when_client_returns_503()
public async Task Should_retry_6_times_for_two_threads_when_client_returns_503()
{
const int retryCount = 3;
var retrySettings =
new JitterRetryPolicySettings(retryCount,
medianFirstRetryDelay: TimeSpan.FromMilliseconds(50));
var retrySettings = new RetryPolicySettings(
SleepDurationProvider.Jitter(retryCount,
medianFirstRetryDelay: TimeSpan.FromMilliseconds(50)));
var wrapper = Create.HttpClientWrapperWrapperBuilder
.WithStatusCode(HttpStatusCode.ServiceUnavailable)
.WithRetrySettings(retrySettings)
Expand All @@ -54,8 +55,8 @@ public async Task Should_separately_distribute_retry_attempts_for_multiple_tasks
{
const int retryCount = 3;
var retryAttempts = new Dictionary<string, List<TimeSpan>>();
var retrySettings = new JitterRetryPolicySettings(retryCount,
medianFirstRetryDelay: TimeSpan.FromMilliseconds(50) )
var retrySettings = new RetryPolicySettings(SleepDurationProvider.Jitter(retryCount,
medianFirstRetryDelay: TimeSpan.FromMilliseconds(50) ))
{
OnRetry = BuildOnRetryAction(retryAttempts)
};
Expand All @@ -74,7 +75,9 @@ public async Task Should_separately_distribute_retry_attempts_for_multiple_tasks
public async Task Should_retry_when_client_returns_500()
{
const int retryCount = 3;
var retrySettings = new SimpleRetryPolicySettings(retryCount);
var retrySettings = new RetryPolicySettings(
SleepDurationProvider.Constant(retryCount, TimeSpan.FromMilliseconds(1)));

var wrapper = Create.HttpClientWrapperWrapperBuilder
.WithStatusCode(HttpStatusCode.InternalServerError)
.WithRetrySettings(retrySettings)
Expand Down
28 changes: 12 additions & 16 deletions src/Dodo.HttpClient.ResiliencePolicies.Tests/TimeoutPolicyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ public class TimeoutPolicyTests
public void Should_retry_5_times_200_status_code_because_of_per_try_timeout()
{
const int retryCount = 5;
var retrySettings = new SimpleRetryPolicySettings(retryCount)
{
SleepDurationProvider = i => TimeSpan.FromMilliseconds(200)
};
var retrySettings = new RetryPolicySettings(
SleepDurationProvider.Constant(retryCount, TimeSpan.FromMilliseconds(200)));

var wrapper = Create.HttpClientWrapperWrapperBuilder
.WithStatusCode(HttpStatusCode.OK)
.WithResponseLatency(TimeSpan.FromMilliseconds(200))
Expand Down Expand Up @@ -51,10 +50,9 @@ public void Should_fail_on_HttpClient_timeout()
public void Should_fail_on_HttpClient_timeout_with_retry()
{
const int retryCount = 5;
var retrySettings = new SimpleRetryPolicySettings(retryCount)
{
SleepDurationProvider = i => TimeSpan.FromMilliseconds(1)
};
var retrySettings = new RetryPolicySettings(
SleepDurationProvider.Constant(retryCount, TimeSpan.FromMilliseconds(1)));

var wrapper = Create.HttpClientWrapperWrapperBuilder
.WithStatusCode(HttpStatusCode.ServiceUnavailable)
.WithResponseLatency(TimeSpan.FromMilliseconds(50))
Expand Down Expand Up @@ -88,10 +86,9 @@ public void Should_catchTimeout_1_times_because_of_overall_timeout_less_than_per
var overallTimeout = TimeSpan.FromMilliseconds(100);
var perTryTimeout = TimeSpan.FromMilliseconds(200);

var retrySettings = new SimpleRetryPolicySettings(retryCount)
{
SleepDurationProvider = i => TimeSpan.FromMilliseconds(200)
};
var retrySettings =new RetryPolicySettings(
SleepDurationProvider.Constant(retryCount, TimeSpan.FromMilliseconds(200)));

var wrapper = Create.HttpClientWrapperWrapperBuilder
.WithStatusCode(HttpStatusCode.OK)
.WithResponseLatency(TimeSpan.FromMilliseconds(300))
Expand All @@ -113,10 +110,9 @@ public void When_overall_timeout_greated_than_summ_perTrials_Should_retry_5_time
var perTryTimeout = TimeSpan.FromMilliseconds(100);
var overallTimeout = TimeSpan.FromSeconds(2);

var retrySettings = new SimpleRetryPolicySettings(retryCount)
{
SleepDurationProvider = i => TimeSpan.FromMilliseconds(200)
};
var retrySettings = new RetryPolicySettings(
SleepDurationProvider.Constant(retryCount, TimeSpan.FromMilliseconds(200)));

var wrapper = Create.HttpClientWrapperWrapperBuilder
.WithStatusCode(HttpStatusCode.OK)
.WithResponseLatency(TimeSpan.FromMilliseconds(200))
Expand Down
1 change: 1 addition & 0 deletions src/Dodo.HttpClient.ResiliencePolicies/Defaults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public static class Timeout
public static class Retry
{
public const int RetryCount = 2;
public const int InitialDelayMilliseconds = 20;
public const int MedianFirstRetryDelayInMilliseconds = 2000;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,14 @@ public static IHttpClientBuilder AddJsonClient<TClientInterface, TClientImplemen

private static IHttpClientBuilder AddRetryPolicy(
this IHttpClientBuilder clientBuilder,
RetrySettings.IRetryPolicySettings settings)
IRetryPolicySettings settings)
{
return clientBuilder
.AddPolicyHandler(HttpPolicyExtensions
.HandleTransientHttpError()
.Or<TimeoutRejectedException>()
.WaitAndRetryAsync(
settings.RetryCount,
settings.SleepDurationProvider,
settings.SleepDurationProvider.SleepFunction,
settings.OnRetry));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ public ResiliencePoliciesSettings()
{
OverallTimeoutPolicySettings = new OverallTimeoutPolicySettings();
TimeoutPerTryPolicySettings = new TimeoutPerTryPolicySettings();
RetrySettings = new SimpleRetryPolicySettings();
CircuitBreakerSettings = new CircuitBreakerSettings.CircuitBreakerPolicySettings();
RetrySettings = new RetryPolicySettings();
CircuitBreakerSettings = new CircuitBreakerPolicySettings();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ namespace Dodo.HttpClientResiliencePolicies.RetrySettings
{
public interface IRetryPolicySettings
{
public int RetryCount { get; }
public Func<int, TimeSpan> SleepDurationProvider { get; }
public ISleepDurationProvider SleepDurationProvider { get; }
public Action<DelegateResult<HttpResponseMessage>, TimeSpan> OnRetry { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;

namespace Dodo.HttpClientResiliencePolicies.RetrySettings
{
public interface ISleepDurationProvider
{
IEnumerable<TimeSpan> SleepFunction { get; }
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Net.Http;
using Polly;

namespace Dodo.HttpClientResiliencePolicies.RetrySettings
{
public class RetryPolicySettings : IRetryPolicySettings
{
public ISleepDurationProvider SleepDurationProvider { get; }
public Action<DelegateResult<HttpResponseMessage>, TimeSpan> OnRetry { get; set; }

public RetryPolicySettings()
: this(RetrySettings.SleepDurationProvider.Jitter(
Defaults.Retry.RetryCount,
TimeSpan.FromMilliseconds(Defaults.Retry.MedianFirstRetryDelayInMilliseconds)))
{
}

public RetryPolicySettings(
ISleepDurationProvider sleepDurationProvider)
{
SleepDurationProvider = sleepDurationProvider;
OnRetry = _doNothingOnRetry;
}

private static readonly Action<DelegateResult<HttpResponseMessage>, TimeSpan> _doNothingOnRetry = (_, __) => { };
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using Polly.Contrib.WaitAndRetry;

namespace Dodo.HttpClientResiliencePolicies.RetrySettings
{
public sealed class SleepDurationProvider : ISleepDurationProvider
{
public IEnumerable<TimeSpan> SleepFunction { get; }

private SleepDurationProvider(IEnumerable<TimeSpan> sleepFunction)
{
if (sleepFunction == null)
throw new ArgumentNullException(nameof(sleepFunction));

SleepFunction = sleepFunction;
}

public static ISleepDurationProvider Exponential(int retryCount)
{
return Exponential(retryCount, TimeSpan.FromMilliseconds(Defaults.Retry.InitialDelayMilliseconds));
}

public static ISleepDurationProvider Exponential(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(Backoff.ExponentialBackoff(initialDelay, retryCount));
}

public static ISleepDurationProvider Linear(int retryCount)
{
return Linear(retryCount, TimeSpan.FromMilliseconds(Defaults.Retry.InitialDelayMilliseconds));
}

public static ISleepDurationProvider Linear(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(Backoff.LinearBackoff(initialDelay, retryCount));
}

public static ISleepDurationProvider Jitter(int retryCount)
{
return Jitter(retryCount, TimeSpan.FromMilliseconds(Defaults.Retry.MedianFirstRetryDelayInMilliseconds));
}

public static ISleepDurationProvider Jitter(int retryCount, TimeSpan medianFirstRetryDelay)
{
if (retryCount < 0) throw new ArgumentOutOfRangeException(nameof(retryCount), retryCount, "should be >= 0");
if (medianFirstRetryDelay < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(medianFirstRetryDelay), medianFirstRetryDelay, "should be >= 0ms");

return new SleepDurationProvider(Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay, retryCount));
}

public static ISleepDurationProvider Constant(int retryCount)
{
return Constant(retryCount, TimeSpan.FromMilliseconds(Defaults.Retry.InitialDelayMilliseconds));
}

public static ISleepDurationProvider 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 new SleepDurationProvider(Backoff.ConstantBackoff(delay, retryCount));
}
}
}

0 comments on commit f96bbab

Please sign in to comment.