Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing Resilience Patterns in .NET with Polly: Practical Scenarios

Tech 1
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";
        }
    }
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.