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
24 changes: 20 additions & 4 deletions src/CacheManager.SystemRuntimeCaching/MemoryCacheHandle`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,37 @@ public class MemoryCacheHandle<TCacheValue> : BaseCacheHandle<TCacheValue>
/// <param name="configuration">The cache handle configuration.</param>
/// <param name="loggerFactory">The logger factory.</param>
public MemoryCacheHandle(ICacheManagerConfiguration managerConfiguration, CacheHandleConfiguration configuration, ILoggerFactory loggerFactory)
: this(managerConfiguration, configuration, loggerFactory, null)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="MemoryCacheHandle{TCacheValue}"/> class.
/// </summary>
/// <param name="managerConfiguration">The manager configuration.</param>
/// <param name="configuration">The cache handle configuration.</param>
/// <param name="loggerFactory">The logger factory.</param>
/// <param name="memoryCacheOptions">The vendor specific options.</param>
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)))

//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
{
_cache = new MemoryCache(_cacheName);
_cache = new MemoryCache(_cacheName, memoryCacheOptions?.AsNameValueCollection());
}

_instanceKey = Guid.NewGuid().ToString();
Expand Down Expand Up @@ -397,4 +413,4 @@ private static void ParseKeyParts(int instanceKeyLength, string fullKey, out boo
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,45 @@ public static ConfigurationBuilderCacheHandlePart WithSystemRuntimeCacheHandle(t
/// <exception cref="ArgumentNullException">Thrown if <paramref name="instanceName"/> is null.</exception>
public static ConfigurationBuilderCacheHandlePart WithSystemRuntimeCacheHandle(this ConfigurationBuilderCachePart part, string instanceName, bool isBackplaneSource = false)
=> part?.WithHandle(typeof(MemoryCacheHandle<>), instanceName, isBackplaneSource);

/// <summary>
/// Adds a <see cref="MemoryCacheHandle{TCacheValue}" /> using a <see cref="System.Runtime.Caching.MemoryCache"/> instance with the given <paramref name="instanceName"/>.
/// The named cache instance can be configured via <paramref name="options"/>.
/// </summary>
/// <param name="part">The builder part.</param>
/// <param name="instanceName">The name to be used for the cache instance.</param>
/// <param name="options">
/// The <see cref="RuntimeMemoryCacheOptions"/> which should be used to initiate this cache.
/// If <c>Null</c>, default options will be used.
/// </param>
/// <returns>
/// The builder part.
/// </returns>
/// <exception cref="System.ArgumentNullException">If part is null.</exception>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="instanceName"/> is null.</exception>
public static ConfigurationBuilderCacheHandlePart WithSystemRuntimeCacheHandle(
this ConfigurationBuilderCachePart part, string instanceName, RuntimeMemoryCacheOptions options)
=> WithSystemRuntimeCacheHandle(part, instanceName, false, options);

/// <summary>
/// Adds a <see cref="MemoryCacheHandle{TCacheValue}" /> using a <see cref="System.Runtime.Caching.MemoryCache"/> instance with the given <paramref name="instanceName"/>.
/// The named cache instance can be configured via <paramref name="options"/>.
/// </summary>
/// <param name="part">The builder part.</param>
/// <param name="instanceName">The name to be used for the cache instance.</param>
/// <param name="isBackplaneSource">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.</param>
/// <param name="options">
/// The <see cref="RuntimeMemoryCacheOptions"/> which should be used to initiate this cache.
/// If <c>Null</c>, default options will be used.
/// </param>
/// <returns>
/// The builder part.
/// </returns>
/// <exception cref="System.ArgumentNullException">If part is null.</exception>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="instanceName"/> is null.</exception>
public static ConfigurationBuilderCacheHandlePart WithSystemRuntimeCacheHandle(
this ConfigurationBuilderCachePart part, string instanceName, bool isBackplaneSource, RuntimeMemoryCacheOptions options)
=> part?.WithHandle(typeof(MemoryCacheHandle<>), instanceName, isBackplaneSource, options);
}
}
}
44 changes: 44 additions & 0 deletions src/CacheManager.SystemRuntimeCaching/RuntimeMemoryCacheOptions.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// <see cref="System.Runtime.Caching.MemoryCache"/> configuration options
/// </summary>
public class RuntimeMemoryCacheOptions
{
/// <summary>
/// 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.
/// </summary>
public int CacheMemoryLimitMegabytes { get; set; } = 0;

/// <summary>
/// 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.
/// </summary>
public int PhysicalMemoryLimitPercentage { get; set; } = 0;

/// <summary>
/// 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.
/// </summary>
public TimeSpan PollingInterval { get; set; } = TimeSpan.FromMinutes(2);

/// <summary>
/// Gets the configuration as a <see cref="NameValueCollection"/>
/// </summary>
/// <returns>A <see cref="NameValueCollection"/> with the current configuration.</returns>
public NameValueCollection AsNameValueCollection()
{
return new NameValueCollection(3)
{
{ nameof(CacheMemoryLimitMegabytes), CacheMemoryLimitMegabytes.ToString(CultureInfo.InvariantCulture) },
{ nameof(PhysicalMemoryLimitPercentage), PhysicalMemoryLimitPercentage.ToString(CultureInfo.InvariantCulture) },
{ nameof(PollingInterval), PollingInterval.ToString("c") }
};
}
}
}
60 changes: 60 additions & 0 deletions test/CacheManager.Tests/MemoryCacheTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using CacheManager.Core;
Expand Down Expand Up @@ -186,6 +187,43 @@ public void SysRuntime_Extensions_NamedB()
cache.CacheHandles.Count().Should().Be(1);
}

[Fact]
public void SysRuntime_Extensions_NamedWithCodeCfg()
{
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<object>)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"));
}
}

[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<InvalidOperationException>().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

Expand Down Expand Up @@ -221,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<object>)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
Expand Down
2 changes: 1 addition & 1 deletion test/CacheManager.Tests/TestCacheManagers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ public static ICacheManager<object> 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()
Expand Down