diff --git a/EonaCat.SmartRetry/EonaCat.SmartRetry.Testers/EonaCat.SmartRetry.Testers.csproj b/EonaCat.SmartRetry/EonaCat.SmartRetry.Testers/EonaCat.SmartRetry.Testers.csproj
new file mode 100644
index 0000000..39096e4
--- /dev/null
+++ b/EonaCat.SmartRetry/EonaCat.SmartRetry.Testers/EonaCat.SmartRetry.Testers.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/EonaCat.SmartRetry/EonaCat.SmartRetry.Testers/Program.cs b/EonaCat.SmartRetry/EonaCat.SmartRetry.Testers/Program.cs
new file mode 100644
index 0000000..8abb458
--- /dev/null
+++ b/EonaCat.SmartRetry/EonaCat.SmartRetry.Testers/Program.cs
@@ -0,0 +1,38 @@
+using EonaCat.SmartRetry;
+
+// This file is part of the EonaCat project(s) which is released under the Apache License.
+// See the LICENSE file or go to https://EonaCat.com/License for full license details.
+
+class Program
+{
+ static async Task Main()
+ {
+ var policy = SmartRetryPolicy.Policy()
+ .Handle()
+ .Retry(5)
+ .WithExponentialBackoff(1, 2)
+ .WithJitter(0.2)
+ .WithCircuitBreaker(failureThreshold: 3, openDuration: TimeSpan.FromSeconds(10))
+ .WithFallbackAsync(async () =>
+ {
+ Console.WriteLine("Executing fallback...");
+ await Task.Delay(50);
+
+ // fallback value
+ return 42;
+ })
+ .OnRetry((attempt, ex) => Console.WriteLine($"Retry {attempt}: {ex.Message}"))
+ .OnCircuitOpen(() => Console.WriteLine("Circuit breaker opened"))
+ .OnCircuitClose(() => Console.WriteLine("Circuit breaker closed"));
+
+ int result = await policy.ExecuteAsync(async (ct) =>
+ {
+ Console.WriteLine("Attempting operation...");
+ await Task.Delay(50, ct);
+ throw new TimeoutException("Simulated failure");
+ });
+
+ Console.WriteLine($"Result: {result}");
+
+ }
+}
diff --git a/EonaCat.SmartRetry/EonaCat.SmartRetry.slnx b/EonaCat.SmartRetry/EonaCat.SmartRetry.slnx
new file mode 100644
index 0000000..7e552eb
--- /dev/null
+++ b/EonaCat.SmartRetry/EonaCat.SmartRetry.slnx
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/EonaCat.SmartRetry/EonaCat.SmartRetry/BackoffStrategyType.cs b/EonaCat.SmartRetry/EonaCat.SmartRetry/BackoffStrategyType.cs
new file mode 100644
index 0000000..5fe5f47
--- /dev/null
+++ b/EonaCat.SmartRetry/EonaCat.SmartRetry/BackoffStrategyType.cs
@@ -0,0 +1,12 @@
+namespace EonaCat.SmartRetry
+{
+ // This file is part of the EonaCat project(s) which is released under the Apache License.
+ // See the LICENSE file or go to https://EonaCat.com/License for full license details.
+
+ public enum BackoffStrategyType
+ {
+ Exponential,
+ Linear,
+ Custom
+ }
+}
diff --git a/EonaCat.SmartRetry/EonaCat.SmartRetry/CircuitBreaker.cs b/EonaCat.SmartRetry/EonaCat.SmartRetry/CircuitBreaker.cs
new file mode 100644
index 0000000..f3d9f15
--- /dev/null
+++ b/EonaCat.SmartRetry/EonaCat.SmartRetry/CircuitBreaker.cs
@@ -0,0 +1,61 @@
+using System;
+
+namespace EonaCat.SmartRetry
+{
+ // This file is part of the EonaCat project(s) which is released under the Apache License.
+ // See the LICENSE file or go to https://EonaCat.com/License for full license details.
+
+ public class CircuitBreaker
+ {
+ private readonly int _failureThreshold;
+ private readonly TimeSpan _openDuration;
+ private int _failureCount = 0;
+ private DateTime _lastFailureTime = DateTime.MinValue;
+
+ public CircuitBreakerState State { get; private set; } = CircuitBreakerState.Closed;
+
+ public Action OnOpen { get; set; }
+ public Action OnClose { get; set; }
+
+ public CircuitBreaker(int failureThreshold = 5, TimeSpan? openDuration = null)
+ {
+ _failureThreshold = failureThreshold;
+ _openDuration = openDuration ?? TimeSpan.FromSeconds(30);
+ }
+
+ public bool CanExecute()
+ {
+ if (State == CircuitBreakerState.Open)
+ {
+ if ((DateTime.Now - _lastFailureTime) >= _openDuration)
+ {
+ State = CircuitBreakerState.HalfOpen;
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ public void OnSuccess()
+ {
+ _failureCount = 0;
+ if (State != CircuitBreakerState.Closed)
+ {
+ State = CircuitBreakerState.Closed;
+ OnClose?.Invoke();
+ }
+ }
+
+ public void OnFailure()
+ {
+ _failureCount++;
+ _lastFailureTime = DateTime.Now;
+ if (_failureCount >= _failureThreshold)
+ {
+ State = CircuitBreakerState.Open;
+ OnOpen?.Invoke();
+ }
+ }
+ }
+}
diff --git a/EonaCat.SmartRetry/EonaCat.SmartRetry/CircuitBreakerState.cs b/EonaCat.SmartRetry/EonaCat.SmartRetry/CircuitBreakerState.cs
new file mode 100644
index 0000000..0a834a1
--- /dev/null
+++ b/EonaCat.SmartRetry/EonaCat.SmartRetry/CircuitBreakerState.cs
@@ -0,0 +1,12 @@
+namespace EonaCat.SmartRetry
+{
+ // This file is part of the EonaCat project(s) which is released under the Apache License.
+ // See the LICENSE file or go to https://EonaCat.com/License for full license details.
+
+ public enum CircuitBreakerState
+ {
+ Closed,
+ Open,
+ HalfOpen
+ }
+}
diff --git a/EonaCat.SmartRetry/EonaCat.SmartRetry/EonaCat.SmartRetry.csproj b/EonaCat.SmartRetry/EonaCat.SmartRetry/EonaCat.SmartRetry.csproj
new file mode 100644
index 0000000..3abc64c
--- /dev/null
+++ b/EonaCat.SmartRetry/EonaCat.SmartRetry/EonaCat.SmartRetry.csproj
@@ -0,0 +1,38 @@
+
+
+
+ netstandard2.1
+ enable
+ latest
+ True
+ EonaCat.SmartRetry
+ EonaCat.SmartRetry
+ EonaCat (Jeroen Saey)
+ EonaCat
+ Powerful and flexible .NET library for building resilient applications.
+ EonaCat (Jeroen Saey)
+ https://git.saey.me/EonaCat/EonaCat.SmartRetry
+ icon.png
+ readme.md
+ https://git.saey.me/EonaCat/EonaCat.SmartRetry
+ git
+ retry;smart;resilient;EonaCat;Jeroen;Saey;power;circuitbreaker;circuit;breaker;auto;fallback
+ LICENSE
+
+
+
+
+ True
+ \
+
+
+ True
+ \
+
+
+ True
+ \
+
+
+
+
diff --git a/EonaCat.SmartRetry/EonaCat.SmartRetry/ISmartRetryPolicy.cs b/EonaCat.SmartRetry/EonaCat.SmartRetry/ISmartRetryPolicy.cs
new file mode 100644
index 0000000..604c69f
--- /dev/null
+++ b/EonaCat.SmartRetry/EonaCat.SmartRetry/ISmartRetryPolicy.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace EonaCat.SmartRetry
+{
+ // This file is part of the EonaCat project(s) which is released under the Apache License.
+ // See the LICENSE file or go to https://EonaCat.com/License for full license details.
+
+ public interface ISmartRetryPolicy
+ {
+ ISmartRetryPolicy Handle() where TException : Exception;
+ ISmartRetryPolicy Retry(int retryCount);
+ ISmartRetryPolicy WithExponentialBackoff(double initialDelaySeconds = 1.0, double multiplier = 2.0);
+ ISmartRetryPolicy WithLinearBackoff(double delaySeconds = 1.0);
+ ISmartRetryPolicy WithCustomBackoff(Func delayProvider);
+ ISmartRetryPolicy WithJitter(double maxPercentage = 0.3);
+ ISmartRetryPolicy WithCircuitBreaker(int failureThreshold = 5, TimeSpan? openDuration = null);
+ ISmartRetryPolicy WithFallback(Func fallbackAction);
+ ISmartRetryPolicy WithFallbackAsync(Func> fallbackAction);
+ ISmartRetryPolicy OnRetry(Action onRetry);
+ ISmartRetryPolicy OnRetryAsync(Func onRetryAsync);
+ ISmartRetryPolicy OnCircuitOpen(Action onOpen);
+ ISmartRetryPolicy OnCircuitClose(Action onClose);
+ ISmartRetryPolicy RetryIf(Func shouldRetry);
+ ISmartRetryPolicy WithMaxRetryDuration(TimeSpan maxDuration);
+
+ Task ExecuteAsync(Func> action, CancellationToken token = default);
+ Task ExecuteAsync(Func> action);
+ T Execute(Func action);
+ }
+}
diff --git a/EonaCat.SmartRetry/EonaCat.SmartRetry/RetryFailedException.cs b/EonaCat.SmartRetry/EonaCat.SmartRetry/RetryFailedException.cs
new file mode 100644
index 0000000..24afbef
--- /dev/null
+++ b/EonaCat.SmartRetry/EonaCat.SmartRetry/RetryFailedException.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace EonaCat.SmartRetry.Exceptions
+{
+ // This file is part of the EonaCat project(s) which is released under the Apache License.
+ // See the LICENSE file or go to https://EonaCat.com/License for full license details.
+
+ public class RetryFailedException : Exception
+ {
+ public RetryFailedException(string message, Exception innerException)
+ : base(message, innerException)
+ { }
+ }
+}
diff --git a/EonaCat.SmartRetry/EonaCat.SmartRetry/RetryStrategy.cs b/EonaCat.SmartRetry/EonaCat.SmartRetry/RetryStrategy.cs
new file mode 100644
index 0000000..e66c355
--- /dev/null
+++ b/EonaCat.SmartRetry/EonaCat.SmartRetry/RetryStrategy.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace EonaCat.SmartRetry
+{
+ // This file is part of the EonaCat project(s) which is released under the Apache License.
+ // See the LICENSE file or go to https://EonaCat.com/License for full license details.
+
+ public class RetryStrategy
+ {
+ public int MaxRetries { get; set; } = 3;
+ public double InitialDelaySeconds { get; set; } = 1.0;
+ public double BackoffMultiplier { get; set; } = 2.0;
+ public BackoffStrategyType BackoffType { get; set; } = BackoffStrategyType.Exponential;
+ public Func CustomDelayProvider { get; set; }
+ public double JitterMaxPercentage { get; set; } = 0.0;
+ public TimeSpan? MaxRetryDuration { get; set; }
+ }
+}
diff --git a/EonaCat.SmartRetry/EonaCat.SmartRetry/SmartRetryPolicy.cs b/EonaCat.SmartRetry/EonaCat.SmartRetry/SmartRetryPolicy.cs
new file mode 100644
index 0000000..2fb137d
--- /dev/null
+++ b/EonaCat.SmartRetry/EonaCat.SmartRetry/SmartRetryPolicy.cs
@@ -0,0 +1,206 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using EonaCat.SmartRetry.Exceptions;
+
+namespace EonaCat.SmartRetry
+{
+ // This file is part of the EonaCat project(s) which is released under the Apache License.
+ // See the LICENSE file or go to https://EonaCat.com/License for full license details.
+
+ public class SmartRetryPolicy : ISmartRetryPolicy
+ {
+ private readonly List _handledExceptions = new();
+ private readonly RetryStrategy _strategy = new();
+ private CircuitBreaker _circuitBreaker;
+ private Delegate _fallback;
+ private Delegate _fallbackAsync;
+ private Func _onRetryAsync;
+ private Action _onRetry;
+ private Func