Round #221
07 May 2026, 15:01· csharp· meta/llama-3.3-70b-instruct· r12-real-contracts
- Complexity
- band 0
- ZP / UO / OBS
- 1 / 5 / 2
- Journey weight
- 560
- Lightshift ΔL
- —
Prompt
Add cache stampede protection using lazy loading and locks
Zero-point index (what the engine surfaced)
ZPI-ZPI-0014 | 2026-05-07T15:01:31Z
Add cache stampede protection using lazy loading and locks
ZERO POINT — 1 entries (structural invariants)
● hsl(145.55, 100%, 21%) IMemoryCache [contract]
UNOBSERVED — 5 entries (implied truths)
○ hsl(27.69, 100%, 50%) async [feature]
○ hsl(199.38, 100%, 16%) Redis distributed cache [function]
○ hsl(188.31, 100%, 16%) Distributed cache (IDistributedCache) [function]
○ hsl(31.38, 100%, 40%) Scoped registration [function]
○ hsl(44.31, 100%, 25%) IOptions of T binding [function]
OBSERVED — 2 entries (explicit in request)
◉ hsl(85.13, 100%, 36%) using [feature]
◉ hsl(234.87, 100%, 36%) using [feature]
COMPLEXITY │ 8 entries │ spread: 207° │ band: LOW
Baseline response (no ZPI)
**Cache Stampede Protection using Lazy Loading and Locks in C#**
Cache stampede is a phenomenon where multiple threads or requests try to retrieve a cache value simultaneously, resulting in a large number of requests to the underlying data source. To prevent this, we can implement cache stampede protection using lazy loading and locks.
### Implementation
Here's an example implementation in C#:
```csharp
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
public class CacheStampedeProtection<T>
{
private readonly ConcurrentDictionary<string, Lazy<T>> _cache;
private readonly ReaderWriterLockSlim _lock;
public CacheStampedeProtection()
{
_cache = new ConcurrentDictionary<string, Lazy<T>>();
_lock = new ReaderWriterLockSlim();
}
public T GetOrAdd(string key, Func<T> valueFactory)
{
// Try to get the value from the cache
if (_cache.TryGetValue(key, out var lazyValue))
{
// If the value is already in the cache, return it
return lazyValue.Value;
}
// Acquire a write lock to prevent other threads from adding the value
_lock.EnterWriteLock();
try
{
// Check again if the value is already in the cache
if (_cache.TryGetValue(key, out lazyValue))
{
// If the value is already in the cache, return it
return lazyValue.Value;
}
// Create a new lazy value and add it to the cache
lazyValue = new Lazy<T>(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication);
_cache.TryAdd(key, lazyValue);
// Return the value
return lazyValue.Value;
}
finally
{
// Release the write lock
_lock.ExitWriteLock();
}
}
public void Remove(string key)
{
// Acquire a write lock to prevent other threads from accessing the cache
_lock.EnterWriteLock();
try
{
// Remove the value from the cache
_cache.TryRemove(key, out _);
}
finally
{
// Release the write lock
_lock.ExitWriteLock();
}
}
}
```
### Example Usage
Here's an example usage of the `CacheStampedeProtection` class:
```csharp
public class Program
{
public static void Main()
{
var cache = new CacheStampedeProtection<string>();
// Add a value to the cache
var value = cache.GetOrAdd("key", () =>
{
Console.WriteLine("Loading value from data source...");
return "Hello, World!";
});
Console.WriteLine(value);
// Remove the value from the cache
cache.Remove("key");
}
}
```
### Explanation
The `CacheStampedeProtection` class uses a `ConcurrentDictionary` to store the cache values and a `ReaderWriterLockSlim` to synchronize access to the cache.
The `GetOrAdd` method tries to retrieve the value from the cache. If the value is not in the cache, it acquires a write lock, checks again if the value is in the cache, and if not, creates a new lazy value and adds it to the cache.
The `Remove` method acquires a write lock and removes the value from the cache.
The `LazyThreadSafetyMode.ExecutionAndPublication` mode ensures that the value is only loaded once, even if multiple threads try to retrieve it simultaneously.
This implementation provides cache stampede protection by preventing multiple threads from loading the same value simultaneously.
Enhanced response (with ZPI)
```csharp
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using System;
using System.Threading;
using System.Threading.Tasks;
public class CacheStampedeProtection
{
private readonly IDistributedCache _distributedCache;
private readonly IMemoryCache _memoryCache;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
public CacheStampedeProtection(IDistributedCache distributedCache, IMemoryCache memoryCache)
{
_distributedCache = distributedCache;
_memoryCache = memoryCache;
}
public async Task<string> GetOrAddAsync(string key, Func<Task<string>> factory, DistributedCacheEntryOptions options)
{
// Check if the value is already in the memory cache
if (_memoryCache.TryGetValue(key, out string value))
{
return value;
}
// Acquire a write lock to prevent concurrent execution of the factory
_lock.EnterWriteLock();
try
{
// Re-check the memory cache after acquiring the lock
if (_memoryCache.TryGetValue(key, out value))
{
return value;
}
// Execute the factory and store the result in the memory cache
value = await factory();
var cacheEntryOptions = new MemoryCacheEntryOptions().AddExpirationToken(new CancellationChangeToken(new CancellationTokenSource(options.AbsoluteExpirationRelativeToNow).Token));
_memoryCache.Set(key, value, cacheEntryOptions);
// Store the result in the distributed cache
await _distributedCache.SetStringAsync(key, value, options);
}
finally
{
_lock.ExitWriteLock();
}
return value;
}
}
public class CacheStampedeProtectionOptions
{
public TimeSpan AbsoluteExpirationRelativeToNow { get; set; }
}
public class CacheStampedeProtectionFactory
{
private readonly IOptions<CacheStampedeProtectionOptions> _options;
public CacheStampedeProtectionFactory(IOptions<CacheStampedeProtectionOptions> options)
{
_options = options;
}
public CacheStampedeProtection Create(IDistributedCache distributedCache, IMemoryCache memoryCache)
{
return new CacheStampedeProtection(distributedCache, memoryCache);
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedMemoryCache();
services.AddMemoryCache();
services.Configure<CacheStampedeProtectionOptions>(options =>
{
options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);
});
services.AddSingleton<CacheStampedeProtectionFactory>();
services.AddSingleton<CacheStampedeProtection>(provider =>
{
var factory = provider.GetService<CacheStampedeProtectionFactory>();
var distributedCache = provider.GetService<IDistributedCache>();
var memoryCache = provider.GetService<IMemoryCache>();
return factory.Create(distributedCache, memoryCache);
});
}
}
```
This implementation provides a `CacheStampedeProtection` class that uses lazy loading and locks to prevent cache stampede. The `GetOrAddAsync` method checks if the value is already in the memory cache, and if not, acquires a write lock to prevent concurrent execution of the factory. The factory is then executed, and the result is stored in both the memory cache and the distributed cache.
The `CacheStampedeProtectionOptions` class is used to configure the absolute expiration time for the cache entries. The `CacheStampedeProtectionFactory` class is used to create instances of the `CacheStampedeProtection` class.
In the `Startup` class, the `ConfigureServices` method is used to register the services and configure the options. The `AddDistributedMemoryCache` and `AddMemoryCache` methods are used to add the distributed memory cache and memory cache services, respectively. The `Configure` method is used to configure the `CacheStampedeProtectionOptions`. The `AddSingleton` method is used to register the `CacheStampedeProtectionFactory` and `CacheStampedeProtection` classes as singletons.
This implementation provides a thread-safe and efficient way to prevent cache stampede using lazy loading and locks. The use of the `ReaderWriterLockSlim` class ensures that only one thread can execute the factory at a time, while allowing multiple threads to read the cache simultaneously. The use of the `IDistributedCache` and `IMemoryCache` interfaces ensures that the implementation is decoupled from the specific cache implementation.
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.
V1claude-haiku-4-5-20251001
WorseV4qwen2.5-coder-7b-instruct
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.