Skip to content

Commit ec83d21

Browse files
Fix session manager bug (#581)
* fix bug that session manager will be skipped if feature definition is null * add test * always set evaluation result to session manager
1 parent ed9860d commit ec83d21

3 files changed

Lines changed: 108 additions & 8 deletions

File tree

src/Microsoft.FeatureManagement/FeatureManager.cs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -355,14 +355,6 @@ private async ValueTask<EvaluationEvent> EvaluateFeature<TContext>(string featur
355355
}
356356
}
357357

358-
if (_sessionManagers != null)
359-
{
360-
foreach (ISessionManager sessionManager in _sessionManagers)
361-
{
362-
await sessionManager.SetAsync(evaluationEvent.FeatureDefinition.Name, evaluationEvent.Enabled).ConfigureAwait(false);
363-
}
364-
}
365-
366358
// Only add an activity event if telemetry is enabled for the feature and the activity is valid
367359
if (telemetryEnabled &&
368360
Activity.Current != null &&
@@ -371,6 +363,28 @@ private async ValueTask<EvaluationEvent> EvaluateFeature<TContext>(string featur
371363
FeatureEvaluationTelemetry.Publish(evaluationEvent, Logger);
372364
}
373365
}
366+
else if (_sessionManagers != null)
367+
{
368+
foreach (ISessionManager sessionManager in _sessionManagers)
369+
{
370+
bool? readSessionResult = await sessionManager.GetAsync(feature).ConfigureAwait(false);
371+
372+
if (readSessionResult.HasValue)
373+
{
374+
evaluationEvent.Enabled = readSessionResult.Value;
375+
376+
break;
377+
}
378+
}
379+
}
380+
381+
if (_sessionManagers != null)
382+
{
383+
foreach (ISessionManager sessionManager in _sessionManagers)
384+
{
385+
await sessionManager.SetAsync(feature, evaluationEvent.Enabled).ConfigureAwait(false);
386+
}
387+
}
374388

375389
return evaluationEvent;
376390
}

tests/Tests.FeatureManagement/FeatureManagementTest.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,61 @@ public async Task ThrowsForMissingFeatures()
280280
featureManager.IsEnabledAsync("NonExistentFeature"));
281281
}
282282

283+
[Fact]
284+
public async Task SessionManagerQueriedWhenFeatureDefinitionIsNull()
285+
{
286+
IConfiguration config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
287+
288+
var services = new ServiceCollection();
289+
290+
ISessionManager sessionManager = new TestSessionManager();
291+
292+
await sessionManager.SetAsync("UnexistedFeature", true);
293+
294+
services
295+
.AddSingleton(config)
296+
.AddSingleton<ISessionManager>(sessionManager)
297+
.AddFeatureManagement();
298+
299+
ServiceProvider serviceProvider = services.BuildServiceProvider();
300+
301+
IFeatureManager featureManager = serviceProvider.GetRequiredService<IFeatureManager>();
302+
303+
// Feature doesn't exist in configuration, but should return true from session
304+
Assert.True(await featureManager.IsEnabledAsync("UnexistedFeature"));
305+
306+
// Set the feature to false in session
307+
await sessionManager.SetAsync("UnexistedFeature", false);
308+
309+
// Should return false from session
310+
Assert.False(await featureManager.IsEnabledAsync("UnexistedFeature"));
311+
}
312+
313+
[Fact]
314+
public async Task SetResultInSessionManagerWhenFeatureDefinitionIsNull()
315+
{
316+
IConfiguration config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
317+
318+
var services = new ServiceCollection();
319+
320+
ISessionManager sessionManager = new TestSessionManager();
321+
322+
services
323+
.AddSingleton(config)
324+
.AddSingleton<ISessionManager>(sessionManager)
325+
.AddFeatureManagement();
326+
327+
ServiceProvider serviceProvider = services.BuildServiceProvider();
328+
329+
IFeatureManager featureManager = serviceProvider.GetRequiredService<IFeatureManager>();
330+
331+
// session manager is clean here
332+
var result = await featureManager.IsEnabledAsync("UnexistedFeature");
333+
334+
// session manager should store the result after evaluation
335+
Assert.Equal(result, await sessionManager.GetAsync("UnexistedFeature"));
336+
}
337+
283338
[Fact]
284339
public async Task ThreadSafeSnapshot()
285340
{
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
//
4+
using Microsoft.FeatureManagement;
5+
using System.Collections.Concurrent;
6+
using System.Threading.Tasks;
7+
8+
namespace Tests.FeatureManagement
9+
{
10+
class TestSessionManager : ISessionManager
11+
{
12+
private readonly ConcurrentDictionary<string, bool> _session = new ConcurrentDictionary<string, bool>();
13+
14+
public Task SetAsync(string featureName, bool enabled)
15+
{
16+
_session[featureName] = enabled;
17+
18+
return Task.CompletedTask;
19+
}
20+
21+
public Task<bool?> GetAsync(string featureName)
22+
{
23+
if (_session.TryGetValue(featureName, out bool enabled))
24+
{
25+
return Task.FromResult<bool?>(enabled);
26+
}
27+
28+
return Task.FromResult<bool?>(null);
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)