From 9bd2457dbdb8bcb396142f1d528c23b730833976 Mon Sep 17 00:00:00 2001 From: Vladyslav Zaiets Date: Thu, 21 May 2026 15:44:55 +0000 Subject: [PATCH] Replace lock-based stats tracking with Interlocked in MemoryPluginCache Using Interlocked.Increment/Read instead of lock(_statsLock) removes unnecessary contention on the hot path. Cache hit/miss counters are simple atomic increments that don't need mutual exclusion with other state. --- src/PluginEngine/Caching/MemoryPluginCache.cs | 36 ++++++------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/PluginEngine/Caching/MemoryPluginCache.cs b/src/PluginEngine/Caching/MemoryPluginCache.cs index c929f3f..c63c9a7 100644 --- a/src/PluginEngine/Caching/MemoryPluginCache.cs +++ b/src/PluginEngine/Caching/MemoryPluginCache.cs @@ -17,7 +17,6 @@ public sealed class MemoryPluginCache : IPluginCache private readonly ILogger _logger; private long _hits; private long _misses; - private readonly object _statsLock = new(); public MemoryPluginCache(IMemoryCache cache, ILogger logger) { @@ -31,19 +30,12 @@ public MemoryPluginCache(IMemoryCache cache, ILogger logger) { if (_cache.TryGetValue(key, out T? value)) { - lock (_statsLock) - { - _hits++; - } - + Interlocked.Increment(ref _hits); _logger.LogDebug("Cache hit: {Key}", key); return Task.FromResult(value); } - lock (_statsLock) - { - _misses++; - } + Interlocked.Increment(ref _misses); _logger.LogDebug("Cache miss: {Key}", key); return Task.FromResult(default); @@ -104,11 +96,8 @@ public Task ClearAsync() { try { - lock (_statsLock) - { - _hits = 0; - _misses = 0; - } + Interlocked.Exchange(ref _hits, 0); + Interlocked.Exchange(ref _misses, 0); // MemoryCache doesn't have a built-in clear method // In production, would use a wrapper or distributed cache @@ -124,17 +113,14 @@ public Task ClearAsync() public Task GetStatisticsAsync() { - lock (_statsLock) + var stats = new CacheStatistics { - var stats = new CacheStatistics - { - TotalHits = _hits, - TotalMisses = _misses, - CurrentEntries = 0, // Can't easily get this from MemoryCache - TotalMemoryBytes = GC.GetTotalMemory(false) - }; + TotalHits = Interlocked.Read(ref _hits), + TotalMisses = Interlocked.Read(ref _misses), + CurrentEntries = 0, // Can't easily get this from MemoryCache + TotalMemoryBytes = GC.GetTotalMemory(false) + }; - return Task.FromResult(stats); - } + return Task.FromResult(stats); } }