Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Threading;
Expand Down Expand Up @@ -153,7 +154,7 @@ internal void SetEntry(CacheEntry entry)
coherentState.RemoveEntry(priorEntry, _options);
}
}
else if (!UpdateCacheSizeExceedsCapacity(entry, coherentState))
else if (!UpdateCacheSizeExceedsCapacity(entry, priorEntry, coherentState))
{
bool entryAdded;
if (priorEntry == null)
Expand All @@ -166,15 +167,7 @@ internal void SetEntry(CacheEntry entry)
// Try to update with the new entry if a previous entries exist.
entryAdded = coherentState._entries.TryUpdate(entry.Key, entry, priorEntry);

if (entryAdded)
{
if (_options.HasSizeLimit)
{
// The prior entry was removed, decrease the by the prior entry's size
Interlocked.Add(ref coherentState._cacheSize, -priorEntry.Size);
}
}
else
if (!entryAdded)
{
// The update will fail if the previous entry was removed after retrieval.
// Adding the new entry will succeed only if no entry has been added since.
Expand All @@ -192,7 +185,7 @@ internal void SetEntry(CacheEntry entry)
if (_options.HasSizeLimit)
{
// Entry could not be added, reset cache size
Interlocked.Add(ref coherentState._cacheSize, -entry.Size);
Interlocked.Add(ref coherentState._cacheSize, -entry.Size + (priorEntry?.Size).GetValueOrDefault());
}
entry.SetExpired(EvictionReason.Replaced);
entry.InvokeEvictionCallbacks();
Expand Down Expand Up @@ -444,7 +437,7 @@ private void ScanForExpiredItems()
/// Returns true if increasing the cache size by the size of entry would
/// cause it to exceed any size limit on the cache, otherwise, returns false.
/// </summary>
private bool UpdateCacheSizeExceedsCapacity(CacheEntry entry, CoherentState coherentState)
private bool UpdateCacheSizeExceedsCapacity(CacheEntry entry, CacheEntry? priorEntry, CoherentState coherentState)
{
long sizeLimit = _options.SizeLimitValue;
if (sizeLimit < 0)
Expand All @@ -456,6 +449,11 @@ private bool UpdateCacheSizeExceedsCapacity(CacheEntry entry, CoherentState cohe
for (int i = 0; i < 100; i++)
{
long newSize = sizeRead + entry.Size;
if (priorEntry != null)
{
Debug.Assert(entry.Key == priorEntry.Key);
newSize -= priorEntry.Size;
}

if ((ulong)newSize > (ulong)sizeLimit)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ public void AddingReplacementWhenTotalSizeExceedsCapacityDoesNotUpdateAndRemoves
{
var cache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = 10,
SizeLimit = 5,
CompactionPercentage = 0.5
});

Expand All @@ -236,6 +236,31 @@ public void AddingReplacementWhenTotalSizeExceedsCapacityDoesNotUpdateAndRemoves
AssertCacheSize(0, cache);
}

[Theory]
[InlineData(6)]
[InlineData(5)]
[InlineData(2)]
public void ReplaceOldEntryWithSameSizeOrLessNewEntryAtSizeLimitCapacity(int newValueSize)
{
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 = newValueSize });

Assert.Equal("newValue", cache.Get("key"));
AssertCacheSize(newValueSize, cache);
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/72912")]
public async Task AddingReplacementWhenTotalSizeExceedsCapacityDoesNotUpdateRemovesOldEntryAndTriggersCompaction()
Expand Down