Reputation: 21
In ASP.NET Core 6 minimal API, I've been working with Azure App Configuration feature flags. I have set up the feature flag configuration so that the flags expire in 5 seconds.
builder.Configuration.AddAzureAppConfiguration(
options => options.UseFeatureFlags(opts => opts.CacheExpirationInterval = TimeSpan.FromSeconds(5)));
I also have added Azure App Configuration and Feature Management services
builder.Services.AddAzureAppConfiguration();
builder.Services.AddFeatureManagement();
And set up the usage
app.UseAzureAppConfiguration();
I tried out one of the feature flags if it is enabled with code below
bool isServiceEnabled = await _featureManager.IsEnabledAsync(FeatureFlags.IsServiceEnabled);
At first it does read the correct value from the App Configuration, then I tried toggling it and calling API after the cache expires, first call to the API still shows me the old value. It's only the second call to the API after expiration that does show the new value.
It seems like first API call still has the old value cached.
Have I missed something? Did I do something wrong while setting up the feature flags?
Upvotes: 2
Views: 1108
Reputation: 10311
I don't know if this is the proper answer, but what worked for us was that we had to register IConfiguration
as a singleton. Yes the framework should have done that for us already but when added this line, any feature flag update would be retrieved. In my startup.cs, I did this.
// Get IConfiguration through DI
private IConfiguration Configuration { get; set;}
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
// (if you're not using the minimal API)
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Configuration);
}
Upvotes: 0
Reputation: 1
So what I do is:
Example:
builder.Configuration.AddAzureAppConfiguration(options =>
{
options.Connect(connectionString)
.Select("*", AzureAppConfigurationLabel(appConfiguration))
.ConfigureRefresh(refresh =>
{
refresh.Register("sentinel", AzureAppConfigurationLabel(appConfiguration), refreshAll: true)
.SetCacheExpiration(TimeSpan.FromMinutes(1));
});
// Load all feature flags with environment label
options.UseFeatureFlags(featureFlagOptions =>
{
featureFlagOptions.Select(KeyFilter.Any, AzureAppConfigurationLabel(appConfiguration));
});
refresher = options.GetRefresher();
});
And then in my BackgroundService
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
while (!stoppingToken.IsCancellationRequested)
{
var level = GetLogLevel();
Log.Information( $"Updating Configuration information {DateTime.Now}");
var success = await _refresher.TryRefreshAsync(stoppingToken);
Upvotes: 0
Reputation: 1012
This is by design.
The following code adds AzureAppConfigurationRefreshMiddleware
to the request pipeline.
app.UseAzureAppConfiguration();
This middleware does activity-based refresh and has the following code.
public async Task InvokeAsync(HttpContext context)
{
foreach (var refresher in Refreshers)
{
_ = refresher.TryRefreshAsync();
}
// Call the next delegate/middleware in the pipeline
await _next(context).ConfigureAwait(false);
}
Note that there is no await
for refresher.TryRefreshAsync();
. Thus you still get the dirty data until the refresh succeeds.
Upvotes: 0