From 7b650a56ede9a76b323092bc35c4fa88306c1dfc Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Sun, 19 May 2024 14:56:11 +0200 Subject: [PATCH 1/2] 2nd call to MemoryCache.Set() with the same key same size erases entry if cache is full --- .../src/MemoryCache.cs | 16 ++++++++++++++ .../tests/CapacityTests.cs | 22 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs b/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs index ed711c0ffa0e9f..b87399f27b1318 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs @@ -200,6 +200,22 @@ internal void SetEntry(CacheEntry entry) priorEntry?.InvokeEvictionCallbacks(); } + else if (_options.HasSizeLimit && priorEntry != null && priorEntry.Size == entry.Size) + { + bool entryAdded = coherentState._entries.TryUpdate(entry.Key, entry, priorEntry); + + if (entryAdded) + { + entry.AttachTokens(); + } + else + { + entry.SetExpired(EvictionReason.Replaced); + entry.InvokeEvictionCallbacks(); + } + + priorEntry.InvokeEvictionCallbacks(); + } else { entry.SetExpired(EvictionReason.Capacity); diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/tests/CapacityTests.cs b/src/libraries/Microsoft.Extensions.Caching.Memory/tests/CapacityTests.cs index 2be2a66bd60e6b..d72039611ce4f2 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/tests/CapacityTests.cs +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/tests/CapacityTests.cs @@ -293,6 +293,28 @@ public void AddingReplacementExceedsCapacityRemovesOldEntry() AssertCacheSize(0, cache); // addition was rejected due to size, and previous item with the same key removed } + [Fact] + public void ReplaceOldEntryWithSameSizeNewEntryAtSizeLimitCapacity() + { + var cache = new MemoryCache(new MemoryCacheOptions + { + SizeLimit = 6 + }); + + AssertCacheSize(0, cache); + + cache.Set("key", "oldValue", new MemoryCacheEntryOptions { Size = 6 }); + + Assert.Equal("oldValue", cache.Get("key")); + + AssertCacheSize(6, cache); + + cache.Set("key", "newValue", new MemoryCacheEntryOptions { Size = 6 }); + + Assert.Equal("newValue", cache.Get("key")); + AssertCacheSize(6, cache); + } + [Fact] public void RemovingEntryDecreasesCacheSize() { From 3a9d0479e27753940e111de1e1dd2ce46cb29394 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Sat, 25 May 2024 22:39:07 +0200 Subject: [PATCH 2/2] handle the case when new entry has less size than older one --- .../src/MemoryCache.cs | 9 ++++++++- .../tests/CapacityTests.cs | 11 +++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs b/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs index b87399f27b1318..6a798ec5e1d7bf 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCache.cs @@ -200,12 +200,19 @@ internal void SetEntry(CacheEntry entry) priorEntry?.InvokeEvictionCallbacks(); } - else if (_options.HasSizeLimit && priorEntry != null && priorEntry.Size == entry.Size) + else if (_options.HasSizeLimit && priorEntry != null && priorEntry.Size >= entry.Size) { bool entryAdded = coherentState._entries.TryUpdate(entry.Key, entry, priorEntry); if (entryAdded) { + long newSizeIncrement = entry.Size - priorEntry.Size; + + if (newSizeIncrement < 0) + { + Interlocked.Add(ref coherentState._cacheSize, newSizeIncrement); + } + entry.AttachTokens(); } else diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/tests/CapacityTests.cs b/src/libraries/Microsoft.Extensions.Caching.Memory/tests/CapacityTests.cs index d72039611ce4f2..ef6a43fa4190f0 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/tests/CapacityTests.cs +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/tests/CapacityTests.cs @@ -293,8 +293,11 @@ public void AddingReplacementExceedsCapacityRemovesOldEntry() AssertCacheSize(0, cache); // addition was rejected due to size, and previous item with the same key removed } - [Fact] - public void ReplaceOldEntryWithSameSizeNewEntryAtSizeLimitCapacity() + [Theory] + [InlineData(6)] + [InlineData(5)] + [InlineData(2)] + public void ReplaceOldEntryWithSameSizeOrLessNewEntryAtSizeLimitCapacity(int newValueSize) { var cache = new MemoryCache(new MemoryCacheOptions { @@ -309,10 +312,10 @@ public void ReplaceOldEntryWithSameSizeNewEntryAtSizeLimitCapacity() AssertCacheSize(6, cache); - cache.Set("key", "newValue", new MemoryCacheEntryOptions { Size = 6 }); + cache.Set("key", "newValue", new MemoryCacheEntryOptions { Size = newValueSize }); Assert.Equal("newValue", cache.Get("key")); - AssertCacheSize(6, cache); + AssertCacheSize(newValueSize, cache); } [Fact]