1010using System . Runtime . CompilerServices ;
1111using System . Threading ;
1212using System . Threading . Tasks ;
13+ using Lucene . Net . Util . Cache ;
1314using Microsoft . Extensions . Caching . Memory ;
1415using Microsoft . Extensions . Internal ;
1516using Microsoft . Extensions . Logging ;
@@ -24,7 +25,7 @@ public class MemoryCache : IMemoryCache
2425 internal readonly ILogger _logger ;
2526
2627 private readonly MemoryCacheOptions _options ;
27- private readonly ConcurrentDictionary < object , CacheEntry > _entries ;
28+ private ConcurrentDictionary < object , CacheEntry > _entries ;
2829
2930 private long _cacheSize ;
3031 private bool _disposed ;
@@ -85,7 +86,18 @@ public MemoryCache(IOptions<MemoryCacheOptions> optionsAccessor, ILoggerFactory
8586
8687 private ICollection < KeyValuePair < object , CacheEntry > > EntriesCollection => _entries ;
8788
88- public void Clear ( ) => _entries . Clear ( ) ;
89+ public void Clear ( )
90+ {
91+ var oldEntries = _entries ;
92+ Interlocked . Exchange ( ref _entries , new ConcurrentDictionary < object , CacheEntry > ( ) ) ;
93+ Interlocked . Exchange ( ref _cacheSize , 0 ) ;
94+
95+ foreach ( var entry in oldEntries )
96+ {
97+ entry . Value . SetExpired ( EvictionReason . Removed ) ;
98+ entry . Value . InvokeEvictionCallbacks ( ) ;
99+ }
100+ }
89101
90102 public IEnumerable < KeyValuePair < object , object > > EntriesForDebug => _entries . Select ( kvp => new KeyValuePair < object , object > ( kvp . Key , kvp . Value . Value ) ) ;
91103
@@ -386,9 +398,10 @@ public void Compact(double percentage)
386398 private void Compact ( long removalSizeTarget , Func < CacheEntry , long > computeEntrySize )
387399 {
388400 var entriesToRemove = new List < CacheEntry > ( ) ;
389- var lowPriEntries = new List < CacheEntry > ( ) ;
390- var normalPriEntries = new List < CacheEntry > ( ) ;
391- var highPriEntries = new List < CacheEntry > ( ) ;
401+ // cache LastAccessed outside of the CacheEntry so it is stable during compaction
402+ var lowPriEntries = new List < ( CacheEntry entry , DateTimeOffset lastAccessed ) > ( ) ;
403+ var normalPriEntries = new List < ( CacheEntry entry , DateTimeOffset lastAccessed ) > ( ) ;
404+ var highPriEntries = new List < ( CacheEntry entry , DateTimeOffset lastAccessed ) > ( ) ;
392405 long removedSize = 0 ;
393406
394407 // Sort items by expired & priority status
@@ -406,13 +419,13 @@ private void Compact(long removalSizeTarget, Func<CacheEntry, long> computeEntry
406419 switch ( entry . Priority )
407420 {
408421 case CacheItemPriority . Low :
409- lowPriEntries . Add ( entry ) ;
422+ lowPriEntries . Add ( ( entry , entry . LastAccessed ) ) ;
410423 break ;
411424 case CacheItemPriority . Normal :
412- normalPriEntries . Add ( entry ) ;
425+ normalPriEntries . Add ( ( entry , entry . LastAccessed ) ) ;
413426 break ;
414427 case CacheItemPriority . High :
415- highPriEntries . Add ( entry ) ;
428+ highPriEntries . Add ( ( entry , entry . LastAccessed ) ) ;
416429 break ;
417430 case CacheItemPriority . NeverRemove :
418431 break ;
@@ -437,7 +450,7 @@ private void Compact(long removalSizeTarget, Func<CacheEntry, long> computeEntry
437450 // ?. Items with the soonest sliding expiration.
438451 // ?. Larger objects - estimated by object graph size, inaccurate.
439452 static void ExpirePriorityBucket ( ref long removedSize , long removalSizeTarget , Func < CacheEntry , long > computeEntrySize , List < CacheEntry > entriesToRemove ,
440- List < CacheEntry > priorityEntries )
453+ List < ( CacheEntry Entry , DateTimeOffset LastAccessed ) > priorityEntries )
441454 {
442455 // Do we meet our quota by just removing expired entries?
443456 if ( removalSizeTarget <= removedSize )
@@ -450,8 +463,8 @@ static void ExpirePriorityBucket(ref long removedSize, long removalSizeTarget, F
450463 // TODO: Refine policy
451464
452465 // LRU
453- priorityEntries . Sort ( ( e1 , e2 ) => e1 . LastAccessed . CompareTo ( e2 . LastAccessed ) ) ;
454- foreach ( CacheEntry entry in priorityEntries )
466+ priorityEntries . Sort ( ( e1 , e2 ) => e1 . LastAccessed . CompareTo ( e2 . LastAccessed ) ) ;
467+ foreach ( ( CacheEntry entry , _ ) in priorityEntries )
455468 {
456469 entry . SetExpired ( EvictionReason . Capacity ) ;
457470 entriesToRemove . Add ( entry ) ;
0 commit comments