From 5f7da8893fa235acc226ccb96e3519ea89c67fa1 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Wed, 2 May 2018 22:27:58 +0100 Subject: [PATCH 1/3] Add configuration by code for System.Runtime.Caching memory options #177 --- .../MemoryCacheHandle`1.cs | 18 ++++++-- .../RuntimeCachingBuilderExtensions.cs | 42 +++++++++++++++++- .../RuntimeMemoryCacheOptions.cs | 44 +++++++++++++++++++ test/CacheManager.Tests/MemoryCacheTests.cs | 23 ++++++++++ 4 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 src/CacheManager.SystemRuntimeCaching/RuntimeMemoryCacheOptions.cs diff --git a/src/CacheManager.SystemRuntimeCaching/MemoryCacheHandle`1.cs b/src/CacheManager.SystemRuntimeCaching/MemoryCacheHandle`1.cs index 9babda4d..da550c52 100644 --- a/src/CacheManager.SystemRuntimeCaching/MemoryCacheHandle`1.cs +++ b/src/CacheManager.SystemRuntimeCaching/MemoryCacheHandle`1.cs @@ -35,21 +35,33 @@ public class MemoryCacheHandle : BaseCacheHandle /// The cache handle configuration. /// The logger factory. public MemoryCacheHandle(ICacheManagerConfiguration managerConfiguration, CacheHandleConfiguration configuration, ILoggerFactory loggerFactory) + : this(managerConfiguration, configuration, loggerFactory, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The manager configuration. + /// The cache handle configuration. + /// The logger factory. + /// The vendor specific options. + public MemoryCacheHandle(ICacheManagerConfiguration managerConfiguration, CacheHandleConfiguration configuration, ILoggerFactory loggerFactory, RuntimeMemoryCacheOptions memoryCacheOptions) : base(managerConfiguration, configuration) { NotNull(configuration, nameof(configuration)); NotNull(loggerFactory, nameof(loggerFactory)); - Logger = loggerFactory.CreateLogger(this); _cacheName = configuration.Name; if (_cacheName.ToUpper(CultureInfo.InvariantCulture).Equals(DefaultName.ToUpper(CultureInfo.InvariantCulture))) { + //we can't change default cache configuration by code, can we? _cache = MemoryCache.Default; } else { - _cache = new MemoryCache(_cacheName); + _cache = new MemoryCache(_cacheName, memoryCacheOptions?.AsNameValueCollection()); } _instanceKey = Guid.NewGuid().ToString(); @@ -397,4 +409,4 @@ private static void ParseKeyParts(int instanceKeyLength, string fullKey, out boo } } } -} \ No newline at end of file +} diff --git a/src/CacheManager.SystemRuntimeCaching/RuntimeCachingBuilderExtensions.cs b/src/CacheManager.SystemRuntimeCaching/RuntimeCachingBuilderExtensions.cs index ce767ad5..ee8ee370 100644 --- a/src/CacheManager.SystemRuntimeCaching/RuntimeCachingBuilderExtensions.cs +++ b/src/CacheManager.SystemRuntimeCaching/RuntimeCachingBuilderExtensions.cs @@ -39,5 +39,45 @@ public static ConfigurationBuilderCacheHandlePart WithSystemRuntimeCacheHandle(t /// Thrown if is null. public static ConfigurationBuilderCacheHandlePart WithSystemRuntimeCacheHandle(this ConfigurationBuilderCachePart part, string instanceName, bool isBackplaneSource = false) => part?.WithHandle(typeof(MemoryCacheHandle<>), instanceName, isBackplaneSource); + + /// + /// Adds a using a instance with the given . + /// The named cache instance can be configured via . + /// + /// The builder part. + /// The name to be used for the cache instance. + /// + /// The which should be used to initiate this cache. + /// If Null, default options will be used. + /// + /// + /// The builder part. + /// + /// If part is null. + /// Thrown if is null. + public static ConfigurationBuilderCacheHandlePart WithSystemRuntimeCacheHandle( + this ConfigurationBuilderCachePart part, string instanceName, RuntimeMemoryCacheOptions options) + => WithSystemRuntimeCacheHandle(part, instanceName, false, options); + + /// + /// Adds a using a instance with the given . + /// The named cache instance can be configured via . + /// + /// The builder part. + /// The name to be used for the cache instance. + /// Set this to true if this cache handle should be the source of the backplane. + /// This setting will be ignored if no backplane is configured. + /// + /// The which should be used to initiate this cache. + /// If Null, default options will be used. + /// + /// + /// The builder part. + /// + /// If part is null. + /// Thrown if is null. + public static ConfigurationBuilderCacheHandlePart WithSystemRuntimeCacheHandle( + this ConfigurationBuilderCachePart part, string instanceName, bool isBackplaneSource, RuntimeMemoryCacheOptions options) + => part?.WithHandle(typeof(MemoryCacheHandle<>), instanceName, isBackplaneSource, options); } -} \ No newline at end of file +} diff --git a/src/CacheManager.SystemRuntimeCaching/RuntimeMemoryCacheOptions.cs b/src/CacheManager.SystemRuntimeCaching/RuntimeMemoryCacheOptions.cs new file mode 100644 index 00000000..9168d4ae --- /dev/null +++ b/src/CacheManager.SystemRuntimeCaching/RuntimeMemoryCacheOptions.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace CacheManager.SystemRuntimeCaching +{ + /// + /// configuration options + /// + public class RuntimeMemoryCacheOptions + { + /// + /// An integer value that specifies the maximum allowable size, in megabytes, that an instance of a MemoryCache can grow to. The default value is 0, which means that the autosizing heuristics of the MemoryCache class are used by default. + /// + public int CacheMemoryLimitMegabytes { get; set; } = 0; + + /// + /// An integer value between 0 and 100 that specifies the maximum percentage of physically installed computer memory that can be consumed by the cache. The default value is 0, which means that the autosizing heuristics of the MemoryCache class are used by default. + /// + public int PhysicalMemoryLimitPercentage { get; set; } = 0; + + /// + /// A value that indicates the time interval after which the cache implementation compares the current memory load against the absolute and percentage-based memory limits that are set for the cache instance. + /// + public TimeSpan PollingInterval { get; set; } = TimeSpan.FromMinutes(2); + + /// + /// Gets the configuration as a + /// + /// A with the current configuration. + public NameValueCollection AsNameValueCollection() + { + return new NameValueCollection(3) + { + { nameof(CacheMemoryLimitMegabytes), CacheMemoryLimitMegabytes.ToString(CultureInfo.InvariantCulture) }, + { nameof(PhysicalMemoryLimitPercentage), PhysicalMemoryLimitPercentage.ToString(CultureInfo.InvariantCulture) }, + { nameof(PollingInterval), PollingInterval.ToString("c") } + }; + } + } +} diff --git a/test/CacheManager.Tests/MemoryCacheTests.cs b/test/CacheManager.Tests/MemoryCacheTests.cs index 86397b71..1c776f4e 100644 --- a/test/CacheManager.Tests/MemoryCacheTests.cs +++ b/test/CacheManager.Tests/MemoryCacheTests.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Linq; using System.Threading.Tasks; using CacheManager.Core; @@ -186,6 +187,28 @@ public void SysRuntime_Extensions_NamedB() cache.CacheHandles.Count().Should().Be(1); } + [Fact] + public void SysRuntime_Extensions_NamedWithCfg() + { + var expectedCacheOptions = new CacheManager.SystemRuntimeCaching.RuntimeMemoryCacheOptions() + { + CacheMemoryLimitMegabytes = 13, + PhysicalMemoryLimitPercentage = 24, + PollingInterval = TimeSpan.FromMinutes(3) + }; + + using (var act = CacheFactory.Build(_ => _.WithSystemRuntimeCacheHandle("NamedTestWithCfg", expectedCacheOptions))) + { + // arrange + var settings = ((CacheManager.SystemRuntimeCaching.MemoryCacheHandle)act.CacheHandles.ElementAt(0)).CacheSettings; + + // act assert + settings["CacheMemoryLimitMegabytes"].Should().Be(expectedCacheOptions.CacheMemoryLimitMegabytes.ToString(CultureInfo.InvariantCulture)); + settings["PhysicalMemoryLimitPercentage"].Should().Be(expectedCacheOptions.PhysicalMemoryLimitPercentage.ToString(CultureInfo.InvariantCulture)); + settings["PollingInterval"].Should().Be(expectedCacheOptions.PollingInterval.ToString("c")); + } + } + // disabling for netstandard 2 as it doesn't seem to read the "default" configuration from app.config. Might be an xunit/runner issue as the configuration stuff has been ported #if !NETCOREAPP2 From 3aa410b6d23d31ac0f6d7ca4569a322c3048cc28 Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Thu, 14 Jun 2018 23:43:32 +0100 Subject: [PATCH 2/3] Add configuration by code for System.Runtime.Caching memory options #177 --- .../MemoryCacheHandle`1.cs | 6 ++- test/CacheManager.Tests/MemoryCacheTests.cs | 39 ++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/CacheManager.SystemRuntimeCaching/MemoryCacheHandle`1.cs b/src/CacheManager.SystemRuntimeCaching/MemoryCacheHandle`1.cs index da550c52..ec48653c 100644 --- a/src/CacheManager.SystemRuntimeCaching/MemoryCacheHandle`1.cs +++ b/src/CacheManager.SystemRuntimeCaching/MemoryCacheHandle`1.cs @@ -54,9 +54,13 @@ public MemoryCacheHandle(ICacheManagerConfiguration managerConfiguration, CacheH Logger = loggerFactory.CreateLogger(this); _cacheName = configuration.Name; - if (_cacheName.ToUpper(CultureInfo.InvariantCulture).Equals(DefaultName.ToUpper(CultureInfo.InvariantCulture))) + + //if (_cacheName.ToUpper(CultureInfo.InvariantCulture).Equals(DefaultName.ToUpper(CultureInfo.InvariantCulture))) + if (DefaultName.Equals(_cacheName, StringComparison.InvariantCultureIgnoreCase)) { //we can't change default cache configuration by code, can we? + Ensure(memoryCacheOptions == null, "MemoryCache Default instance can only be configured through app/web.config."); + _cache = MemoryCache.Default; } else diff --git a/test/CacheManager.Tests/MemoryCacheTests.cs b/test/CacheManager.Tests/MemoryCacheTests.cs index 1c776f4e..c1bab4a6 100644 --- a/test/CacheManager.Tests/MemoryCacheTests.cs +++ b/test/CacheManager.Tests/MemoryCacheTests.cs @@ -188,7 +188,7 @@ public void SysRuntime_Extensions_NamedB() } [Fact] - public void SysRuntime_Extensions_NamedWithCfg() + public void SysRuntime_Extensions_NamedWithCodeCfg() { var expectedCacheOptions = new CacheManager.SystemRuntimeCaching.RuntimeMemoryCacheOptions() { @@ -209,6 +209,21 @@ public void SysRuntime_Extensions_NamedWithCfg() } } + [Fact] + public void SysRuntime_Extensions_DefaultWithCodeCfg() + { + var expectedCacheOptions = new CacheManager.SystemRuntimeCaching.RuntimeMemoryCacheOptions() + { + CacheMemoryLimitMegabytes = 13, + PhysicalMemoryLimitPercentage = 24, + PollingInterval = TimeSpan.FromMinutes(3) + }; + + Action act = () => CacheFactory.Build(_ => _.WithSystemRuntimeCacheHandle("default", expectedCacheOptions)); + + act.Should().Throw().WithMessage("*Default*app/web.config*"); + } + // disabling for netstandard 2 as it doesn't seem to read the "default" configuration from app.config. Might be an xunit/runner issue as the configuration stuff has been ported #if !NETCOREAPP2 @@ -244,6 +259,28 @@ public void SysRuntime_CreateNamedCache() } } + [Fact] + [Trait("category", "NotOnMono")] + public void SysRuntime_CreateNamedCacheOverrideWithCodeCfg() + { + var expectedCacheOptions = new CacheManager.SystemRuntimeCaching.RuntimeMemoryCacheOptions() + { + CacheMemoryLimitMegabytes = 11, + PhysicalMemoryLimitPercentage = 22, + PollingInterval = TimeSpan.FromMinutes(4) + }; + + using (var act = CacheFactory.Build(_ => _.WithSystemRuntimeCacheHandle("NamedTest", expectedCacheOptions))) + { + // arrange + var settings = ((CacheManager.SystemRuntimeCaching.MemoryCacheHandle)act.CacheHandles.ElementAt(0)).CacheSettings; + + // act assert + settings["CacheMemoryLimitMegabytes"].Should().Be(expectedCacheOptions.CacheMemoryLimitMegabytes.ToString(CultureInfo.InvariantCulture)); + settings["PhysicalMemoryLimitPercentage"].Should().Be(expectedCacheOptions.PhysicalMemoryLimitPercentage.ToString(CultureInfo.InvariantCulture)); + settings["PollingInterval"].Should().Be(expectedCacheOptions.PollingInterval.ToString("c")); + } + } #endif #endregion System Runtime Caching From ba7219400b81492a39b9f0defe56a8c7a5936b0c Mon Sep 17 00:00:00 2001 From: Nuno Pereira Date: Tue, 3 Jul 2018 00:07:03 +0100 Subject: [PATCH 3/3] Added test with RuntimeMemoryCacheOptions --- test/CacheManager.Tests/TestCacheManagers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CacheManager.Tests/TestCacheManagers.cs b/test/CacheManager.Tests/TestCacheManagers.cs index 2cff4649..84999ec7 100644 --- a/test/CacheManager.Tests/TestCacheManagers.cs +++ b/test/CacheManager.Tests/TestCacheManagers.cs @@ -338,7 +338,7 @@ public static ICacheManager WithMemoryAndDictionaryHandles .Builder .WithSystemRuntimeCacheHandle() .EnableStatistics() - .And.WithSystemRuntimeCacheHandle() + .And.WithSystemRuntimeCacheHandle("LimitedCacheHandle", new SystemRuntimeCaching.RuntimeMemoryCacheOptions() { PhysicalMemoryLimitPercentage = 20, CacheMemoryLimitMegabytes = 200 }) .EnableStatistics() .WithExpiration(ExpirationMode.Absolute, TimeSpan.FromSeconds(1000)) .And.WithDictionaryHandle()