1

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?

2 Answers2

0

This is by design.

The following code adds AzureAppConfigurationRefreshMiddleware to the request pipeline.

    app.UseAzureAppConfiguration();

See https://github.com/Azure/AppConfiguration-DotnetProvider/blob/main/src/Microsoft.Azure.AppConfiguration.AspNetCore/AzureAppConfigurationRefreshExtensions.cs

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.

See https://github.com/Azure/AppConfiguration-DotnetProvider/blob/main/src/Microsoft.Azure.AppConfiguration.AspNetCore/AzureAppConfigurationRefreshMiddleware.cs

Jerry Joseph
  • 1,002
  • 8
  • 14
0

So what I do is:

  1. I grab the refresher from AzureAppConfigurationOptions
  2. Call the method TryRefreshAsync on an interval using a BackgroundService.

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);
LarsN
  • 1