Round #506
09 May 2026, 20:00· csharp· qwen2.5-coder-7b-instruct· r13-clean-hints
- Complexity
- band 0
- ZP / UO / OBS
- 3 / 7 / 0
- Journey weight
- 601
- Lightshift ΔL
- +0.83
Prompt
Write a circuit breaker for cache reads that falls back to the database
Zero-point index (what the engine surfaced)
ZPI-ZPI-0015 | 2026-05-09T20:00:26Z
Write a circuit breaker for cache reads that falls back to the database
ZERO POINT — 3 entries (structural invariants)
● hsl(145.55, 100%, 22%) IMemoryCache [contract]
● hsl(296.42, 100%, 46%) ResiliencePipeline [contract]
● hsl(211.57, 100%, 46%) ResiliencePipelineBuilder [contract]
UNOBSERVED — 7 entries (implied truths)
○ hsl(348.72, 100%, 30%) IDisposable [feature]
○ hsl(27.69, 100%, 50%) async [feature]
○ hsl(9.23, 100%, 50%) interface [feature]
○ hsl(180.92, 100%, 22%) Database health check [function]
○ hsl(144.00, 100%, 42%) Connection resilience [function]
○ hsl(83.08, 100%, 42%) ILogger of T injection [function]
○ hsl(49.23, 100%, 50%) try [feature]
OBSERVED — 0 entries (explicit in request)
COMPLEXITY │ 10 entries │ spread: 339° │ band: LOW
Baseline response (no ZPI)
Certainly! A circuit breaker pattern can be useful in scenarios where you want to prevent your application from making too many requests to a potentially unreliable service (in this case, the database) when it's experiencing issues. Below is an example of how you might implement a circuit breaker for cache reads that falls back to the database using C#.
First, let's define some interfaces and classes:
1. **ICacheService**: Represents the cache service.
2. **IDatabaseService**: Represents the database service.
3. **CircuitBreakerCacheService**: Combines the cache and database services with a circuit breaker.
Here's the implementation:
```csharp
using System;
using System.Threading.Tasks;
public interface ICacheService
{
Task<T> GetAsync<T>(string key);
Task SetAsync<T>(string key, T value, TimeSpan expiration);
}
public interface IDatabaseService
{
Task<T> GetAsync<T>(string key);
}
public class CircuitBreakerCacheService : ICacheService
{
private readonly ICacheService _cacheService;
private readonly IDatabaseService _databaseService;
private readonly ICircuitBreaker _circuitBreaker;
public CircuitBreakerCacheService(ICacheService cacheService, IDatabaseService databaseService, ICircuitBreaker circuitBreaker)
{
_cacheService = cacheService;
_databaseService = databaseService;
_circuitBreaker = circuitBreaker;
}
public async Task<T> GetAsync<T>(string key)
{
if (_circuitBreaker.IsOpen)
{
throw new CircuitBreakerOpenException("Circuit breaker is open. Unable to access the cache or database.");
}
try
{
// Try to get from cache first
var cachedValue = await _cacheService.GetAsync<T>(key);
if (cachedValue != null)
{
return cachedValue;
}
// If not in cache, fall back to database
var dbValue = await _databaseService.GetAsync<T>(key);
if (dbValue != null)
{
// Optionally update the cache with the value from the database
await _cacheService.SetAsync(key, dbValue, TimeSpan.FromMinutes(10));
}
return dbValue;
}
catch (Exception ex)
{
_circuitBreaker.OnFailure();
throw new CacheReadException("Failed to read from cache or database.", ex);
}
}
public async Task SetAsync<T>(string key, T value, TimeSpan expiration)
{
try
{
await _cacheService.SetAsync(key, value, expiration);
}
catch (Exception ex)
{
_circuitBreaker.OnFailure();
throw new CacheWriteException("Failed to write to cache.", ex);
}
}
}
public interface ICircuitBreaker
{
bool IsOpen { get; }
void OnSuccess();
void OnFailure();
}
public class SimpleCircuitBreaker : ICircuitBreaker
{
private const int MaxFailures = 3;
private const int ResetTimeout = 5000; // 5 seconds
private int _failureCount = 0;
private DateTime? _lastFailureTime;
public bool IsOpen => _failureCount >= MaxFailures && _lastFailureTime.HasValue && DateTime.Now < _lastFailureTime.Value.AddMilliseconds(ResetTimeout);
public void OnSuccess()
{
_failureCount = 0;
}
public void OnFailure()
{
_failureCount++;
_lastFailureTime = DateTime.Now;
}
}
public class CircuitBreakerOpenException : Exception
{
public CircuitBreakerOpenException(string message) : base(message)
{
}
}
public class CacheReadException : Exception
{
public CacheReadException(string message, Exception innerException) : base(message, innerException)
{
}
}
public class CacheWriteException : Exception
{
public CacheWriteException(string message, Exception innerException) : base(message, innerException)
{
}
}
```
### Usage Example
```csharp
class Program
{
static async Task Main(string[] args)
{
var cacheService = new InMemoryCacheService();
var databaseService = new DatabaseService();
var circuitBreaker = new SimpleCircuitBreaker();
var circuitBreakerCacheService = new CircuitBreakerCacheService(cacheService, databaseService, circuitBreaker);
try
{
var value = await circuitBreakerCacheService.GetAsync<string>("myKey");
Console.WriteLine($"Value: {value}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
public class InMemoryCacheService : ICacheService
{
private readonly Dictionary<string, object> _cache = new Dictionary<string, object>();
public async Task<T> GetAsync<T>(string key)
{
if (_cache.ContainsKey(key))
{
return (T)_cache[key];
}
return default;
}
public async Task SetAsync<T>(string key, T value, TimeSpan expiration)
{
_cache[key] = value;
}
}
public class DatabaseService : IDatabaseService
{
public async Task<T> GetAsync<T>(string key)
{
// Simulate database access
await Task.Delay(100);
return default(T);
}
}
```
### Explanation
1. **ICacheService and IDatabaseService**: These interfaces define the contract for cache and database operations.
2. **CircuitBreakerCacheService**: This class combines the cache and database services, using a circuit breaker to control access.
3. **SimpleCircuitBreaker**: A simple implementation of the circuit breaker pattern that opens after a certain number of failures and resets after a timeout.
4. **Exception Classes**: Custom exceptions for different error scenarios.
This example demonstrates how you can implement a circuit breaker for cache reads that falls back to the database in C#. You can customize the behavior, such as changing the failure threshold or reset timeout, according to your specific requirements.
Enhanced response (with ZPI)
To implement a circuit breaker for cache reads that falls back to the database, we can leverage the `ResiliencePipeline` and `IMemoryCache` contracts. We'll also use `ILogger<T>` for logging purposes.
Here's a complete implementation:
1. **Install Required Packages**:
- `Polly`: For implementing the circuit breaker pattern.
- `Microsoft.Extensions.Caching.Memory`: For in-memory caching.
- `Microsoft.Extensions.Logging.Abstractions`: For logging.
2. **Create the Circuit Breaker Service**:
```csharp
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Polly;
using Polly.CircuitBreaker;
using System.Threading.Tasks;
public class CacheWithCircuitBreakerService<T>
{
private readonly IMemoryCache _cache;
private readonly ILogger<CacheWithCircuitBreakerService<T>> _logger;
private readonly ResiliencePipeline _resiliencePipeline;
public CacheWithCircuitBreakerService(IMemoryCache cache, ILogger<CacheWithCircuitBreakerService<T>> logger, ResiliencePipeline resiliencePipeline)
{
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_resiliencePipeline = resiliencePipeline ?? throw new ArgumentNullException(nameof(resiliencePipeline));
}
public async Task<T> GetAsync(string key, Func<Task<T>> databaseReadFunc)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key cannot be null or empty", nameof(key));
if (databaseReadFunc == null)
throw new ArgumentNullException(nameof(databaseReadFunc));
try
{
// Try to get the value from cache first
if (_cache.TryGetValue(key, out T cachedValue))
{
_logger.LogInformation($"Cache hit for key: {key}");
return cachedValue;
}
// If not in cache, use the resilience pipeline to execute the database read function
var result = await _resiliencePipeline.ExecuteAsync(databaseReadFunc);
// Store the result in cache
_cache.Set(key, result, new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) // Example expiration time
});
_logger.LogInformation($"Database read and cached for key: {key}");
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error fetching data from cache or database for key: {key}");
throw;
}
}
}
```
3. **Register the Service in DI**:
```csharp
using Microsoft.Extensions.DependencyInjection;
public static class CacheWithCircuitBreakerServiceExtensions
{
public static IServiceCollection AddCacheWithCircuitBreakerService<T>(this IServiceCollection services)
{
services.AddMemoryCache();
services.AddSingleton<ResiliencePipelineBuilder>();
services.AddTransient<ResiliencePipeline>();
services.AddScoped<CacheWithCircuitBreakerService<T>>();
return services;
}
}
```
4. **Configure the Resilience Pipeline**:
```csharp
using Polly;
using Polly.CircuitBreaker;
public static class ResiliencePipelineBuilderExtensions
{
public static void ConfigureCircuitBreaker(this ResiliencePipelineBuilder builder)
{
builder.AddCircuitBreaker(
onBreak: (exception, breakDuration, context) =>
{
Console.WriteLine($"Circuit breaker opened for {breakDuration}");
},
onReset: () =>
{
Console.WriteLine("Circuit breaker reset");
},
onHalfOpen: () =>
{
Console.WriteLine("Circuit breaker half-open");
},
shouldHandle: (exception) => exception is CircuitBreakerException
);
}
}
```
5. **Usage Example**:
```csharp
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
var services = new ServiceCollection();
services.AddCacheWithCircuitBreakerService<string>();
var serviceProvider = services.BuildServiceProvider();
var cacheService = serviceProvider.GetService<CacheWithCircuitBreakerService<string>>();
var result = await cacheService.GetAsync("exampleKey", async () =>
{
// Simulate a database read
return await Task.FromResult("Database Result");
});
Console.WriteLine(result);
}
}
```
### Explanation:
1. **CacheWithCircuitBreakerService<T>**:
- This service is responsible for fetching data from the cache or, if not available, falling back to the database.
- It uses `IMemoryCache` to store and retrieve cached data.
- The `ResiliencePipeline` is used to execute the database read function with circuit breaker semantics.
2. **Registering Services**:
- We register `IMemoryCache`, `ResiliencePipelineBuilder`, and `ResiliencePipeline` in the DI container.
- We also register our custom service `CacheWithCircuitBreakerService<T>` as a scoped service.
3. **Configuring Resilience Pipeline**:
- The resilience pipeline is configured to use a circuit breaker with specific callbacks for when the circuit breaker opens, resets, and transitions to half-open state.
- It handles exceptions of type `CircuitBreakerException`.
4. **Usage Example**:
- We demonstrate how to use the `CacheWithCircuitBreakerService<T>` to fetch data from the cache or database with circuit breaker protection.
This implementation ensures that the application can handle transient failures gracefully by falling back to the database when the cache is unavailable, while also providing circuit breaker semantics to prevent overloading the database during high failure rates.
Grader chain
Each grader reads the same baseline + enhanced pair and casts an independent verdict. Disagreement is the framework's own signal — not noise to be averaged away.
V1samba/Meta-Llama-3.3-70B-Instruct
BetterV4qwen2.5-coder-3b-instruct
BetterV5openrouter/qwen/qwen3-235b-a22b-2507
BetterV7qwen2.5-coder-7b-instruct
BetterV8openai/gpt-4.1
BetterV9anthropic/claude-opus-4-7
WorseV12openai/gpt-4o
BetterComments
Our grader said what it said. What do you say? Comment as a guest below.
No comments yet. Be the first to say what you make of this round.