diff --git a/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCache.cs b/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCache.cs index 7c41611d..72c05bb5 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCache.cs @@ -30,5 +30,11 @@ public interface IMemoryCache : IDisposable /// /// An object identifying the entry. void Remove(object key); + + /// + /// Attempt to remove the given percentage of the total entries in the cache. + /// + /// The percentage of cache entries to remove. + void Compact(double percentage); } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Abstractions/exceptions.net45.json b/src/Microsoft.Extensions.Caching.Abstractions/exceptions.net45.json new file mode 100644 index 00000000..2ece3d0c --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Abstractions/exceptions.net45.json @@ -0,0 +1,8 @@ +[ + { + "OldTypeId": "public interface Microsoft.Extensions.Caching.Memory.IMemoryCache : System.IDisposable", + "NewTypeId": "public interface Microsoft.Extensions.Caching.Memory.IMemoryCache : System.IDisposable", + "NewMemberId": "System.Void Compact(System.Double percentage)", + "Kind": "Addition" + } +] \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Abstractions/exceptions.netcore.json b/src/Microsoft.Extensions.Caching.Abstractions/exceptions.netcore.json new file mode 100644 index 00000000..2ece3d0c --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Abstractions/exceptions.netcore.json @@ -0,0 +1,8 @@ +[ + { + "OldTypeId": "public interface Microsoft.Extensions.Caching.Memory.IMemoryCache : System.IDisposable", + "NewTypeId": "public interface Microsoft.Extensions.Caching.Memory.IMemoryCache : System.IDisposable", + "NewMemberId": "System.Void Compact(System.Double percentage)", + "Kind": "Addition" + } +] \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Memory/Infrastructure/GcNotification.cs b/src/Microsoft.Extensions.Caching.Memory/Infrastructure/GcNotification.cs deleted file mode 100644 index 0448cf3c..00000000 --- a/src/Microsoft.Extensions.Caching.Memory/Infrastructure/GcNotification.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.Extensions.Internal -{ - /// - /// Registers a callback that fires each time a Gen2 garbage collection occurs, - /// presumably due to memory pressure. - /// For this to work no components can have a reference to the instance. - /// - public class GcNotification - { - private readonly Func _callback; - private readonly object _state; - private readonly int _initialCollectionCount; - - private GcNotification(Func callback, object state) - { - _callback = callback; - _state = state; - _initialCollectionCount = GC.CollectionCount(2); - } - - public static void Register(Func callback, object state) - { - var notification = new GcNotification(callback, state); - } - - ~GcNotification() - { - bool reRegister = true; - try - { - // Only invoke the callback after this instance has made it into gen2. - if (_initialCollectionCount < GC.CollectionCount(2)) - { - reRegister = _callback(_state); - } - } - catch (Exception) - { - // Never throw from the finalizer thread - } - - if (reRegister && !Environment.HasShutdownStarted) - { - GC.ReRegisterForFinalize(this); - } - } - } -} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index e9097dc2..85c2829d 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -48,10 +48,6 @@ public MemoryCache(IOptions optionsAccessor) _entryExpirationNotification = EntryExpired; _clock = options.Clock ?? new SystemClock(); - if (options.CompactOnMemoryPressure) - { - GcNotification.Register(DoMemoryPreassureCollection, state: null); - } _expirationScanFrequency = options.ExpirationScanFrequency; _lastExpirationScan = _clock.UtcNow; } @@ -276,20 +272,6 @@ private static void ScanForExpiredItems(MemoryCache cache) } } - /// This is called after a Gen2 garbage collection. We assume this means there was memory pressure. - /// Remove at least 10% of the total entries (or estimated memory?). - private bool DoMemoryPreassureCollection(object state) - { - if (_disposed) - { - return false; - } - - Compact(0.10); - - return true; - } - /// Remove at least the given percentage (0.10 for 10%) of the total entries (or estimated memory?), according to the following policy: /// 1. Remove all expired items. /// 2. Bucket by CacheItemPriority. diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs index 6d79032e..4e591b77 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs @@ -11,8 +11,6 @@ public class MemoryCacheOptions : IOptions { public ISystemClock Clock { get; set; } - public bool CompactOnMemoryPressure { get; set; } = true; - public TimeSpan ExpirationScanFrequency { get; set; } = TimeSpan.FromMinutes(1); MemoryCacheOptions IOptions.Value diff --git a/src/Microsoft.Extensions.Caching.Memory/exceptions.net45.json b/src/Microsoft.Extensions.Caching.Memory/exceptions.net45.json new file mode 100644 index 00000000..7351b76c --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Memory/exceptions.net45.json @@ -0,0 +1,12 @@ +[ + { + "OldTypeId": "public class Microsoft.Extensions.Caching.Memory.MemoryCacheOptions : Microsoft.Extensions.Options.IOptions", + "OldMemberId": "public System.Boolean get_CompactOnMemoryPressure()", + "Kind": "Removal" + }, + { + "OldTypeId": "public class Microsoft.Extensions.Caching.Memory.MemoryCacheOptions : Microsoft.Extensions.Options.IOptions", + "OldMemberId": "public System.Void set_CompactOnMemoryPressure(System.Boolean value)", + "Kind": "Removal" + } +] \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Memory/exceptions.netcore.json b/src/Microsoft.Extensions.Caching.Memory/exceptions.netcore.json new file mode 100644 index 00000000..7351b76c --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Memory/exceptions.netcore.json @@ -0,0 +1,12 @@ +[ + { + "OldTypeId": "public class Microsoft.Extensions.Caching.Memory.MemoryCacheOptions : Microsoft.Extensions.Options.IOptions", + "OldMemberId": "public System.Boolean get_CompactOnMemoryPressure()", + "Kind": "Removal" + }, + { + "OldTypeId": "public class Microsoft.Extensions.Caching.Memory.MemoryCacheOptions : Microsoft.Extensions.Options.IOptions", + "OldMemberId": "public System.Void set_CompactOnMemoryPressure(System.Boolean value)", + "Kind": "Removal" + } +] \ No newline at end of file diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/CacheEntryScopeExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/CacheEntryScopeExpirationTests.cs index 9980ab7d..3ca069ad 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/CacheEntryScopeExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/CacheEntryScopeExpirationTests.cs @@ -22,7 +22,6 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - CompactOnMemoryPressure = false, }); } diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/CacheServiceExtensionsTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/CacheServiceExtensionsTests.cs index 5c76d01f..853c2f1a 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/CacheServiceExtensionsTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/CacheServiceExtensionsTests.cs @@ -124,6 +124,11 @@ public ICacheEntry CreateEntry(object key) { throw new NotImplementedException(); } + + public void Compact(double percentage) + { + throw new NotImplementedException(); + } } private class TestDistributedCache : IDistributedCache diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/CompactTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/CompactTests.cs index 5934f175..0d863ffe 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/CompactTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/CompactTests.cs @@ -14,7 +14,6 @@ private MemoryCache CreateCache(ISystemClock clock = null) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - CompactOnMemoryPressure = false, }); } diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/GcNotificationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/GcNotificationTests.cs deleted file mode 100644 index 292d492d..00000000 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/GcNotificationTests.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading; -using Microsoft.AspNetCore.Testing.xunit; -using Microsoft.Extensions.Internal; -using Xunit; - -namespace Microsoft.Extensions.Caching.Memory -{ - public class GcNotificationTests - { - [ConditionalFact] - [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test fails on Mono (finalizer never called).")] - public void CallbackRegisteredAndInvoked() - { - var callbackInvoked = new ManualResetEvent(false); - GcNotification.Register(state => - { - callbackInvoked.Set(); - return false; - }, null); - - GcCollectAndWait(); - Assert.True(callbackInvoked.WaitOne(0)); - } - - [Fact] - public void CallbackInvokedMultipleTimes() - { - var reRegisterForFinalize = true; - var callbackInvoked = new ManualResetEvent(false); - GcNotification.Register(state => - { - callbackInvoked.Set(); - return reRegisterForFinalize; - }, null); - - GcCollectAndWait(); - Assert.True(callbackInvoked.WaitOne(0)); - - callbackInvoked.Reset(); - reRegisterForFinalize = false; - - GcCollectAndWait(); - Assert.True(callbackInvoked.WaitOne(0)); - - callbackInvoked.Reset(); - - // No callback expected the 3rd time - GcCollectAndWait(); - Assert.False(callbackInvoked.WaitOne(0)); - } - - private static void GcCollectAndWait() - { - // We need to collect twice for this test to work on Mono - GC.Collect(2, GCCollectionMode.Forced, blocking: true); - GC.Collect(2, GCCollectionMode.Forced, blocking: true); - GC.WaitForPendingFinalizers(); - } - } -} diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/MemoryCacheSetAndRemoveTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/MemoryCacheSetAndRemoveTests.cs index 18e18e93..a76afe2a 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/MemoryCacheSetAndRemoveTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/MemoryCacheSetAndRemoveTests.cs @@ -12,10 +12,7 @@ public class MemoryCacheSetAndRemoveTests { private static IMemoryCache CreateCache() { - return new MemoryCache(new MemoryCacheOptions() - { - CompactOnMemoryPressure = false, - }); + return new MemoryCache(new MemoryCacheOptions()); } [Fact] diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs index 0237ad6b..f4cbfd99 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs @@ -16,7 +16,6 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - CompactOnMemoryPressure = false, }); } diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs index 6b1c6484..9c36ab26 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs @@ -23,7 +23,6 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - CompactOnMemoryPressure = false, }); }