diff --git a/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs b/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs index dbaa1200..91be7d6e 100644 --- a/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs +++ b/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs @@ -15,7 +15,7 @@ namespace Microsoft.FeatureManagement /// /// A feature definition provider that pulls feature definitions from the .NET Core system. /// - sealed class ConfigurationFeatureDefinitionProvider : IFeatureDefinitionProvider, IDisposable + sealed class ConfigurationFeatureDefinitionProvider : IFeatureDefinitionProvider, IDisposable, IFeatureDefinitionProviderCacheable { private const string FeatureFiltersSectionName = "EnabledFor"; private const string RequirementTypeKeyword = "RequirementType"; diff --git a/src/Microsoft.FeatureManagement/FeatureManagementOptions.cs b/src/Microsoft.FeatureManagement/FeatureManagementOptions.cs index 49350ff9..329ddfc3 100644 --- a/src/Microsoft.FeatureManagement/FeatureManagementOptions.cs +++ b/src/Microsoft.FeatureManagement/FeatureManagementOptions.cs @@ -24,14 +24,5 @@ public class FeatureManagementOptions /// The default value is true. /// public bool IgnoreMissingFeatures { get; set; } = true; - - /// - /// Controls the cache lifetime of settings bound by in the feature management cache. - /// By default, this cache is off, with a ttl of . - /// To enable caching of filter parameters, a non-zero ttl should be provided. A recommendation is five seconds. - /// Increasing the value may cause an observed increase in memory footprint as items live longer. - /// Lowering the value will decrease performance benefits yielded by caching bound parameters. - /// - public TimeSpan FilterSettingsCacheTtl { get; set; } = TimeSpan.Zero; } } diff --git a/src/Microsoft.FeatureManagement/FeatureManager.cs b/src/Microsoft.FeatureManagement/FeatureManager.cs index c34883ba..c0090fe6 100644 --- a/src/Microsoft.FeatureManagement/FeatureManager.cs +++ b/src/Microsoft.FeatureManagement/FeatureManager.cs @@ -25,7 +25,7 @@ class FeatureManager : IFeatureManager, IDisposable private readonly ConcurrentDictionary _filterMetadataCache; private readonly ConcurrentDictionary _contextualFeatureFilterCache; private readonly FeatureManagementOptions _options; - private readonly IMemoryCache _cache; + private readonly IMemoryCache _parametersCache; private class ConfigurationCacheItem { @@ -47,12 +47,8 @@ public FeatureManager( _logger = loggerFactory.CreateLogger(); _filterMetadataCache = new ConcurrentDictionary(); _contextualFeatureFilterCache = new ConcurrentDictionary(); - TryValidateOptions(options, out _options); - _cache = new MemoryCache( - new MemoryCacheOptions - { - ExpirationScanFrequency = _options.FilterSettingsCacheTtl - }); + _options = options?.Value ?? throw new ArgumentNullException(nameof(options)); + _parametersCache = new MemoryCache(new MemoryCacheOptions()); } public Task IsEnabledAsync(string feature) @@ -75,7 +71,7 @@ public async IAsyncEnumerable GetFeatureNamesAsync() public void Dispose() { - _cache.Dispose(); + _parametersCache.Dispose(); } private async Task IsEnabledAsync(string feature, TContext appContext, bool useAppContext) @@ -227,14 +223,14 @@ private void BindSettings(IFeatureFilterMetadata filter, FeatureFilterEvaluation // // Check if settings already bound from configuration or the parameters have changed - if (!_cache.TryGetValue(context.FeatureName, out cacheItem) || + if (!_parametersCache.TryGetValue(context.FeatureName, out cacheItem) || cacheItem.Parameters != context.Parameters) { settings = binder.BindParameters(context.Parameters); - if (_options.FilterSettingsCacheTtl > TimeSpan.Zero) + if (_featureDefinitionProvider is IFeatureDefinitionProviderCacheable) { - _cache.Set( + _parametersCache.Set( context.FeatureName, new ConfigurationCacheItem { @@ -243,7 +239,8 @@ private void BindSettings(IFeatureFilterMetadata filter, FeatureFilterEvaluation }, new MemoryCacheEntryOptions { - AbsoluteExpirationRelativeToNow = _options.FilterSettingsCacheTtl + SlidingExpiration = TimeSpan.FromMinutes(5), + AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1) }); } } @@ -327,17 +324,5 @@ private ContextualFeatureFilterEvaluator GetContextualFeatureFilter(string filte return filter; } - - private FeatureManagementOptions TryValidateOptions(IOptions options, out FeatureManagementOptions _options) - { - _options = options?.Value ?? throw new ArgumentNullException(nameof(options)); - - if (_options.FilterSettingsCacheTtl < TimeSpan.Zero) - { - throw new ArgumentException("FilterSettingsCacheTtl option must be greater than or equal to TimeSpan.Zero."); - } - - return _options; - } } } diff --git a/src/Microsoft.FeatureManagement/IFeatureDefinitionProviderCacheable.cs b/src/Microsoft.FeatureManagement/IFeatureDefinitionProviderCacheable.cs new file mode 100644 index 00000000..973ec079 --- /dev/null +++ b/src/Microsoft.FeatureManagement/IFeatureDefinitionProviderCacheable.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.FeatureManagement +{ + public interface IFeatureDefinitionProviderCacheable + { + } +} diff --git a/tests/Tests.FeatureManagement/FeatureManagement.cs b/tests/Tests.FeatureManagement/FeatureManagement.cs index 08b092ce..a2c45c31 100644 --- a/tests/Tests.FeatureManagement/FeatureManagement.cs +++ b/tests/Tests.FeatureManagement/FeatureManagement.cs @@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Configuration.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.FeatureManagement; using Microsoft.FeatureManagement.FeatureFilters; @@ -876,11 +875,6 @@ public async Task BindsFeatureFlagSettings() } }); - services.Configure(options => - { - options.FilterSettingsCacheTtl = TimeSpan.FromSeconds(5); - }); - services.AddSingleton(definitionProvider) .AddSingleton(new ConfigurationBuilder().Build()) .AddFeatureManagement() diff --git a/tests/Tests.FeatureManagement/InMemoryFeatureDefinitionProvider.cs b/tests/Tests.FeatureManagement/InMemoryFeatureDefinitionProvider.cs index 91cbcefd..fe613254 100644 --- a/tests/Tests.FeatureManagement/InMemoryFeatureDefinitionProvider.cs +++ b/tests/Tests.FeatureManagement/InMemoryFeatureDefinitionProvider.cs @@ -6,7 +6,7 @@ namespace Tests.FeatureManagement { - class InMemoryFeatureDefinitionProvider : IFeatureDefinitionProvider + class InMemoryFeatureDefinitionProvider : IFeatureDefinitionProvider, IFeatureDefinitionProviderCacheable { private IEnumerable _definitions;