using System;
namespace ResilienceDemo.Core
{
public abstract class ResilienceScenario
{
public abstract string Name { get; }
public void Log(string message, ConsoleColor color)
{
lock (typeof(Console))
{
var originalColor = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {message}");
Console.ForegroundColor = originalColor;
}
}
public void Info(string message) => Log(message, ConsoleColor.Cyan);
public void Success(string message) => Log(message, ConsoleColor.Green);
public void Error(string message) => Log(message, ConsoleColor.Red);
public abstract void Execute();
}
}
Retry Pattern
using Polly;
using System;
namespace ResilienceDemo.Scenarios
{
public class RetryScenario : ResilienceScenario
{
public override string Name => "Retry Policy";
private int tokenRefreshCount = 0;
public override void Execute()
{
var retryPolicy = Policy
.Handle<AuthorizationException>()
.Or<RateLimitException>()
.Or<Exception>(ex => ex.Message == "Transient network glitch")
.Retry(3, (exception, retryCount) =>
{
Error($"Attempt {retryCount} failed: {exception.Message}. Retrying...");
RefreshCredentials();
});
try
{
retryPolicy.Execute(CallSecureApi);
Success("API request successful.");
}
catch (Exception ex)
{
Error($"Request ultimately failed: {ex.Message}");
}
}
private void RefreshCredentials()
{
tokenRefreshCount++;
Info("Refreshed authentication token.");
}
private void CallSecureApi()
{
if (tokenRefreshCount < 1) throw new AuthorizationException("Invalid token");
if (tokenRefreshCount < 2) throw new RateLimitException("Too many requests");
if (tokenRefreshCount < 3 && DateTime.Now.Millisecond % 2 == 0) throw new Exception("Transient network glitch");
Info("Fetching data from API...");
}
}
public class AuthorizationException : Exception { public AuthorizationException(string msg) : base(msg) {} }
public class RateLimitException : Exception { public RateLimitException(string msg) : base(msg) {} }
}
Timeout Pattern
using Polly;
using Polly.Timeout;
using System;
using System.Threading;
namespace ResilienceDemo.Scenarios
{
public class TimeoutScenario : ResilienceScenario
{
public override string Name => "Timeout Policy";
public override void Execute()
{
var timeoutPolicy = Policy.Timeout(2, TimeoutStrategy.Pessimistic);
for (int i = 1; i <= 5; i++)
{
try
{
Info($"Executing request {i}...");
timeoutPolicy.Execute(FetchRemoteConfig);
Success($"Request {i} completed.");
}
catch (TimeoutRejectedException ex)
{
Error($"Request {i} timed out: {ex.Message}");
}
}
}
private void FetchRemoteConfig()
{
var latency = new Random().Next(0, 4000);
Thread.Sleep(latency);
if (latency > 2000) throw new OperationCanceledException();
}
}
}
Fallback Pattern
using Polly;
using System;
using System.Threading;
namespace ResilienceDemo.Scenarios
{
public class FallbackScenario : ResilienceScenario
{
public override string Name => "Fallback Policy";
private int networkLatency = 1;
public override void Execute()
{
var fallbackPolicy = Policy.Handle<GatewayTimeoutException>()
.Fallback(() =>
{
networkLatency++;
Error($"Service overloaded. Adjusting latency tolerance to {networkLatency}s.");
});
try
{
for (int i = 0; i < 5; i++)
{
fallbackPolicy.Execute(DownloadAsset);
}
}
catch (Exception ex)
{
Error($"Critical failure: {ex.Message}");
}
}
private void DownloadAsset()
{
if (networkLatency < 3)
{
Thread.Sleep(networkLatency * 1000);
throw new GatewayTimeoutException("Download exceeded current tolerance");
}
Thread.Sleep(networkLatency * 1000);
Success("Asset downloaded successfully.");
networkLatency = 2; // Simulate fluctuating network
}
}
public class GatewayTimeoutException : Exception { public GatewayTimeoutException(string msg) : base(msg) {} }
}
Circuit Breaker Patttern
using Polly;
using Polly.CircuitBreaker;
using System;
namespace ResilienceDemo.Scenarios
{
public class CircuitBreakerScenario : ResilienceScenario
{
public override string Name => "Circuit Breaker Policy";
public override void Execute()
{
var breakerPolicy = Policy.Handle<DatabaseUnavailableException>()
.CircuitBreaker(3, TimeSpan.FromSeconds(5));
for (int i = 1; i <= 6; i++)
{
try
{
Info($"Processing transaction {i}...");
breakerPolicy.Execute(WriteToDatabase);
}
catch (BrokenCircuitException)
{
Error("Circuit is open. Request blocked.");
}
catch (Exception ex)
{
Error($"Transaction failed: {ex.Message}");
}
}
}
private void WriteToDatabase() => throw new DatabaseUnavailableException("Primary replica offline");
}
public class DatabaseUnavailableException : Exception { public DatabaseUnavailableException(string msg) : base(msg) {} }
}
Advanced Circuit Breaker Pattern
using Polly;
using Polly.CircuitBreaker;
using System;
using System.Threading;
namespace ResilienceDemo.Scenarios
{
public class AdvancedCircuitBreakerScenario : ResilienceScenario
{
public override string Name => "Advanced Circuit Breaker Policy";
public override void Execute()
{
var advancedBreaker = Policy.Handle<SmtpException>()
.AdvancedCircuitBreaker(
failureThreshold: 0.5,
minimumThroughput: 4,
samplingDuration: TimeSpan.FromSeconds(5),
durationOfBreak: TimeSpan.FromSeconds(10),
onBreak: (ex, delay) => Error($"Circuit tripped for {delay.TotalSeconds}s. Reason: {ex.Message}"),
onHalfOpen: () => Info("Circuit is half-open. Testing connection..."),
onReset: () => Success("Circuit closed. Service restored.")
);
for (int i = 1; i <= 10; i++)
{
try
{
advancedBreaker.Execute(SendNotification);
}
catch (BrokenCircuitException)
{
Error("Blocked by open circuit.");
}
catch (Exception ex)
{
Error($"Notification failed: {ex.Message}");
}
finally
{
Thread.Sleep(300);
}
}
}
private void SendNotification()
{
if (new Random().Next(2) == 0) throw new SmtpException("Connection refused");
Success("Notification sent.");
}
}
public class SmtpException : Exception { public SmtpException(string msg) : base(msg) {} }
}
Bulkhead Isolasion Pattern
using Polly;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ResilienceDemo.Scenarios
{
public class BulkheadScenario : ResilienceScenario
{
public override string Name => "Bulkhead Isolation Policy";
public override void Execute()
{
var bulkhead = Policy.Bulkhead(3);
for (int i = 1; i <= 10; i++)
{
var taskId = i;
Task.Run(() =>
{
try
{
bulkhead.Execute(() => ProcessVideo(taskId));
}
catch (BulkheadRejectedException)
{
Error($"Task {taskId} rejected: Queue capacity reached.");
}
});
}
Thread.Sleep(5000); // Wait for tasks to complete
}
private void ProcessVideo(int id)
{
Info($"Processing video {id}...");
Thread.Sleep(1000);
Success($"Video {id} processed.");
}
}
}
Policy Wrap
using Polly;
using Polly.Timeout;
using System;
using System.Threading;
namespace ResilienceDemo.Scenarios
{
public class PolicyWrapScenario : ResilienceScenario
{
public override string Name => "Policy Wrap";
public override void Execute()
{
var fallbackPolicy = Policy.Handle<TimeoutRejectedException>()
.Fallback(() =>
{
Error("Fallback: Serving cached response due to timeout.");
});
var timeoutPolicy = Policy.Timeout(1, TimeoutStrategy.Pessimistic);
var combinedPolicy = fallbackPolicy.Wrap(timeoutPolicy);
for (int i = 1; i <= 3; i++)
{
try
{
combinedPolicy.Execute(CallExternalApi);
}
catch (Exception ex)
{
Error($"Unhandled exception: {ex.Message}");
}
}
}
private void CallExternalApi()
{
var delay = new Random().Next(0, 2000);
Thread.Sleep(delay);
if (delay > 1000) throw new OperationCanceledException();
Success($"API responded in {delay}ms.");
}
}
}
Cache Pattern
using Polly;
using Polly.Caching;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Threading;
namespace ResilienceDemo.Scenarios
{
public class CacheScenario : ResilienceScenario
{
public override string Name => "Cache Policy";
public override void Execute()
{
var memoryCache = new MemoryCache(new MemoryCacheOptions());
var cacheProvider = new MemoryCacheProvider(memoryCache);
var cachePolicy = Policy.Cache(cacheProvider, TimeSpan.FromSeconds(5));
for (int i = 0; i < 4; i++)
{
var context = new Context($"Product-99");
var result = cachePolicy.Execute(ctx => FetchProductDetails(), context);
Info($"Result: {result}");
Thread.Sleep(2000);
}
}
private string FetchProductDetails()
{
Info("Querying database for product details...");
return "Product: Laptop, Price: $1200";
}
}
}