Round #246
07 May 2026, 20:52· csharp· meta/llama-3.3-70b-instruct· r13-clean-hints
- Complexity
- band 0
- ZP / UO / OBS
- 1 / 1 / 2
- Journey weight
- 257
- Lightshift ΔL
- +1.00
Prompt
Add cache stampede protection using lazy loading and locks
Zero-point index (what the engine surfaced)
ZPI-ZPI-0014 | 2026-05-07T20:52:37Z
Add cache stampede protection using lazy loading and locks
ZERO POINT — 1 entries (structural invariants)
● hsl(145.55, 100%, 21%) IMemoryCache [contract]
UNOBSERVED — 1 entries (implied truths)
○ hsl(27.69, 100%, 50%) async [feature]
OBSERVED — 2 entries (explicit in request)
◉ hsl(85.13, 100%, 36%) using [feature]
◉ hsl(234.87, 100%, 36%) using [feature]
COMPLEXITY │ 4 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 the same data from a cache, resulting in a large number of requests to the underlying data source. To prevent this, we can use a combination of lazy loading and locks.
### Implementation
Here's an example implementation of a cache with stampede protection using lazy loading and locks in C#:
```csharp
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
public class Cache<T>
{
private readonly ConcurrentDictionary<string, Lazy<T>> _cache;
private readonly ReaderWriterLockSlim _lock;
public Cache()
{
_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;
}
// If the value is not in the cache, acquire a write lock
_lock.EnterWriteLock();
try
{
// Check again if the value is in the cache (another thread might have added it while we were waiting for the lock)
if (_cache.TryGetValue(key, out lazyValue))
{
// If the value is already in the cache, return it
return lazyValue.Value;
}
// If the value is not in the cache, create a new lazy value and add it to the cache
lazyValue = new Lazy<T>(valueFactory);
_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
_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 cache:
```csharp
public class Program
{
public static void Main()
{
var cache = new Cache<string>();
// Get or add a value to the cache
var value = cache.GetOrAdd("key", () =>
{
// Simulate an expensive operation
Thread.Sleep(1000);
return "value";
});
Console.WriteLine(value); // Output: value
// Remove the value from the cache
cache.Remove("key");
}
}
```
### Benefits
The benefits of using this cache implementation are:
* **Cache stampede protection**: The cache uses lazy loading and locks to prevent multiple threads or requests from trying to retrieve the same data from the underlying data source.
* **Thread safety**: The cache is thread-safe, meaning that it can be safely accessed and modified by multiple threads concurrently.
* **Efficient**: The cache uses a `ConcurrentDictionary` to store values, which provides efficient lookup and insertion operations.
### Advice
When using this cache implementation, keep in mind the following:
* **Use a reasonable lock timeout**: If the lock is held for too long, it can cause other threads to wait indefinitely. Consider using a reasonable lock timeout to prevent this.
* **Avoid using the cache for large values**: The cache stores values in memory, so it's not suitable for large values. Consider using a disk-based cache or a distributed cache for large values.
* **Monitor cache performance**: Monitor the cache's performance and adjust its configuration as needed to ensure optimal performance.
Enhanced response (with ZPI)
```csharp
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Threading;
using System.Threading.Tasks;
public class CacheStampedeProtection
{
private readonly IMemoryCache _cache;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
public CacheStampedeProtection(IMemoryCache cache)
{
_cache = cache;
}
public async Task<T> GetOrAddAsync<T>(string key, Func<Task<T>> factory)
{
if (_cache.TryGetValue(key, out T value))
{
return value;
}
_lock.EnterUpgradeableReadLock();
try
{
if (_cache.TryGetValue(key, out value))
{
return value;
}
_lock.EnterWriteLock();
try
{
if (_cache.TryGetValue(key, out value))
{
return value;
}
value = await factory();
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromHours(1)); // adjust expiration as needed
_cache.Set(key, value, cacheEntryOptions);
return value;
}
finally
{
_lock.ExitWriteLock();
}
}
finally
{
_lock.ExitUpgradeableReadLock();
}
}
}
public class Program
{
public static async Task Main()
{
var services = new ServiceCollection();
services.AddMemoryCache();
var serviceProvider = services.BuildServiceProvider();
var cache = serviceProvider.GetService<IMemoryCache>();
var cacheStampedeProtection = new CacheStampedeProtection(cache);
var result = await cacheStampedeProtection.GetOrAddAsync("key", async () =>
{
// simulate an expensive operation
await Task.Delay(1000);
return (object)"value";
});
Console.WriteLine(result);
}
}
```
This implementation uses the `IMemoryCache` interface from the `Microsoft.Extensions.Caching.Memory` namespace to interact with the cache. The `CacheStampedeProtection` class provides a `GetOrAddAsync` method that checks if a value is already in the cache. If it is, the method returns the cached value. If not, it uses a `ReaderWriterLockSlim` to synchronize access to the cache and prevent multiple threads from executing the factory method simultaneously.
The `GetOrAddAsync` method first checks if the value is in the cache without acquiring a lock. If it is, the method returns the cached value. If not, it acquires an upgradeable read lock and checks again if the value is in the cache. If it is, the method returns the cached value. If not, it acquires a write lock, executes the factory method, caches the result, and returns the value.
The `Main` method demonstrates how to use the `CacheStampedeProtection` class. It creates a `ServiceCollection`, adds the memory cache, builds the service provider, and gets an instance of the `IMemoryCache` interface. It then creates an instance of the `CacheStampedeProtection` class and uses it to get or add a value to the cache. The factory method simulates an expensive operation by delaying for 1 second. The result is then printed to the console.
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.