All MicroEvals
.NET thread-safe in-memory cache
Create MicroEval
Header image for .NET thread-safe in-memory cache

.NET thread-safe in-memory cache

Prompt

You are a senior .NET engineer working on a performance-sensitive production service. Implement the following task in modern C# targeting .NET 10. ## Task Create a high-performance, thread-safe in-memory cache with the following API: ```csharp public sealed class ExpiringCache<TKey, TValue> where TKey : notnull { public ExpiringCache(TimeProvider timeProvider); public void Set(TKey key, TValue value, TimeSpan ttl); public bool TryGet(TKey key, out TValue value); public bool Remove(TKey key); public int Count { get; } } ``` ## Requirements 1. Entries expire after their TTL. 2. `TryGet` must never return an expired entry. 3. The implementation must be safe under concurrent `Set`, `TryGet`, and `Remove` calls. 4. Avoid a global lock on the hot read path. 5. Use `TimeProvider` rather than `DateTime.UtcNow` or `Stopwatch` directly. 6. Expired entries should eventually be removed even if they are never accessed again. 7. Do not create one `Timer` per cache entry. 8. Minimize allocations on the read path. 9. Define what happens when: * `ttl == TimeSpan.Zero` * `ttl < TimeSpan.Zero` * the same key is overwritten while an older expiration is already scheduled 10. The implementation must behave correctly when expiration cleanup races with an update to the same key. 11. Do not use `MemoryCache` or another prebuilt caching library. ## Deliverables Provide: 1. The complete implementation. 2. A concise explanation of the concurrency design. 3. The key invariants that make the implementation correct. 4. Any unavoidable trade-offs. 5. Big-O complexity for: * `Set` * `TryGet` * `Remove` * expiration cleanup 6. Unit tests using xUnit. 7. At least one deterministic expiration test using `FakeTimeProvider` or an equivalent controllable `TimeProvider`. 8. A BenchmarkDotNet benchmark comparing: * cache hit * cache miss * overwrite * concurrent reads 9. Benchmark configuration suitable for getting meaningful results rather than measuring startup/JIT noise. ## Constraints * Enable nullable reference types. * Do not suppress warnings without explaining why. * Prefer standard .NET BCL types. * Do not introduce abstractions unless they materially improve correctness or performance. * Avoid LINQ in hot paths. * Avoid `Task.Run` as a synchronization mechanism. * Do not use `lock` merely because concurrency is difficult; justify every synchronization primitive used. * Code must compile as presented. ## Review your own solution After producing the implementation, perform a critical review and identify at least three plausible failure modes or performance problems. For each one: * explain whether the implementation is actually vulnerable, * describe the race or cost involved, * and, if necessary, provide a corrected implementation. Pay particular attention to ABA-style races where an expiration record for an old value may accidentally remove a newer value stored under the same key. Do not assume your first implementation is correct.

Drag to resize
Drag to resize