-
Notifications
You must be signed in to change notification settings - Fork 119
/
Copy pathFeatureManagerSnapshot.cs
141 lines (116 loc) · 5.06 KB
/
FeatureManagerSnapshot.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
using Microsoft.FeatureManagement.FeatureFilters;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.FeatureManagement
{
/// <summary>
/// Provides a snapshot of feature state to ensure consistency across a given request.
/// </summary>
class FeatureManagerSnapshot : IFeatureManagerSnapshot, IVariantFeatureManagerSnapshot
{
private readonly IFeatureManager _featureManager;
private readonly IVariantFeatureManager _variantFeatureManager;
private readonly ConcurrentDictionary<string, ValueTask<bool>> _flagCache = new ConcurrentDictionary<string, ValueTask<bool>>();
private readonly ConcurrentDictionary<string, Variant> _variantCache = new ConcurrentDictionary<string, Variant>();
private IEnumerable<string> _featureNames;
// Takes both a feature manager and a variant feature manager for backwards compatibility.
public FeatureManagerSnapshot(IFeatureManager featureManager, IVariantFeatureManager variantFeatureManager)
{
_featureManager = featureManager ?? throw new ArgumentNullException(nameof(featureManager));
_variantFeatureManager = variantFeatureManager ?? throw new ArgumentNullException(nameof(variantFeatureManager));
}
public async IAsyncEnumerable<string> GetFeatureNamesAsync()
{
if (_featureNames == null)
{
var featureNames = new List<string>();
await foreach (string featureName in _featureManager.GetFeatureNamesAsync().ConfigureAwait(false))
{
featureNames.Add(featureName);
}
_featureNames = featureNames;
}
foreach (string featureName in _featureNames)
{
yield return featureName;
}
}
public async IAsyncEnumerable<string> GetFeatureNamesAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
if (_featureNames == null)
{
var featureNames = new List<string>();
await foreach (string featureName in _variantFeatureManager.GetFeatureNamesAsync(cancellationToken).ConfigureAwait(false))
{
featureNames.Add(featureName);
}
_featureNames = featureNames;
}
foreach (string featureName in _featureNames)
{
yield return featureName;
}
}
public Task<bool> IsEnabledAsync(string feature)
{
return _flagCache.GetOrAdd(
feature,
(key) => new ValueTask<bool>(_featureManager.IsEnabledAsync(key))).AsTask();
}
public Task<bool> IsEnabledAsync<TContext>(string feature, TContext context)
{
return _flagCache.GetOrAdd(
feature,
(key) => new ValueTask<bool>(_featureManager.IsEnabledAsync(key, context))).AsTask();
}
public ValueTask<bool> IsEnabledAsync(string feature, CancellationToken cancellationToken)
{
return _flagCache.GetOrAdd(
feature,
(key) => _variantFeatureManager.IsEnabledAsync(key, cancellationToken));
}
public ValueTask<bool> IsEnabledAsync<TContext>(string feature, TContext context, CancellationToken cancellationToken)
{
return _flagCache.GetOrAdd(
feature,
(key) => _variantFeatureManager.IsEnabledAsync(key, context, cancellationToken));
}
public async ValueTask<Variant> GetVariantAsync(string feature, CancellationToken cancellationToken)
{
string cacheKey = GetVariantCacheKey(feature);
//
// First, check local cache
if (_variantCache.ContainsKey(feature))
{
return _variantCache[cacheKey];
}
Variant variant = await _variantFeatureManager.GetVariantAsync(feature, cancellationToken).ConfigureAwait(false);
_variantCache[cacheKey] = variant;
return variant;
}
public async ValueTask<Variant> GetVariantAsync(string feature, ITargetingContext context, CancellationToken cancellationToken)
{
string cacheKey = GetVariantCacheKey(feature);
//
// First, check local cache
if (_variantCache.ContainsKey(feature))
{
return _variantCache[cacheKey];
}
Variant variant = await _variantFeatureManager.GetVariantAsync(feature, context, cancellationToken).ConfigureAwait(false);
_variantCache[cacheKey] = variant;
return variant;
}
private string GetVariantCacheKey(string feature)
{
return $"{typeof(Variant).FullName}\n{feature}";
}
}
}