Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IOptionsMonitor doesn't work dinamically with Azure AppConfiguration #52485

Open
jonnovaretti opened this issue May 8, 2021 · 7 comments
Open

Comments

@jonnovaretti
Copy link

jonnovaretti commented May 8, 2021

I'm trying to use IOptionsMonitor for dinamic configuration with Azure AppConfiguration, the values are loaded correctly on starting the application. After started, when I update any settings on Azure App Configuration portal, the settings values keep the same.

I've already used App Configuration in a function and it works properly, but nowadays I'm using WebHostedService DotNetCore.

        public static void Main()
        {
            var host = CreateHostBuilder().Build();            
            host.Run();
        }

        public static IHostBuilder CreateHostBuilder()
        {
            var host = Host.CreateDefaultBuilder()
                 .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    var configuration = config.Build();

                    config.AddAzureAppConfiguration(options =>
                    {
                        options.Connect(configuration["ConnectionStrings:AppConfiguration"])
                               .UseFeatureFlags()
                               .ConfigureRefresh(refresh =>
                               {
                                   refresh.Register("RetryPolicyConfig:IntervalSecs", true);
                                   refresh.Register("RetryPolicyConfig:RetryCount", true);
                                   refresh.SetCacheExpiration(new TimeSpan(30, 0, 0));
                               });

                        _refresher = options.GetRefresher();
                    });
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    webBuilder.ConfigureServices((w, s) => { s.AddSingleton(_refresher); });
                });

            return host;
        }

the IOptionsMonitor is inject in service class

        private readonly RetryPolicyConfig retryPolicyConfig;
        private readonly IConfigurationRefresher _refresher;

        public Service(IOptionsMonitor<RetryPolicyConfig> configuration, IConfigurationRefresher refresher)
        {
            this.configuration = configuration;
            retryPolicyConfig = configuration.CurrentValue;
        }

        public async Task Execute(Commmand command)
        {
            try
            {
                await _refresher.TryRefreshAsync();
                var retryPolicy = retryPolicyConfig.RetryCount;
                var secs = retryPolicyConfig.IntervalExponentialEachRetryInSecs; //nothing happens here after updating setting by AppConfiguration

Any idea?

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label May 8, 2021
@centreboard
Copy link
Contributor

What's the lifetime of your service? It's resolving the current configuration value when it's constructed rather than when it's executed. If you move retryPolicyConfig from a field to a variable does that help?

 public async Task Execute(Commmand command)
    {
        try
        {
            await _refresher.TryRefreshAsync();
            
            var retryPolicyConfig = this.configuration.CurrentValue;
            var retryPolicy = retryPolicyConfig.RetryCount;
            var secs = retryPolicyConfig.IntervalExponentialEachRetryInSecs;
        }

@pinkfloydx33
Copy link

pinkfloydx33 commented May 8, 2021

Are you actually updating the watched configuration settings? Configuration won't update at the client unless you explicitly update any of the "watched" settings and even then it can take a minimum of the refresh interval. You also need to set the updateAll parameter for it to trigger an update for everything and not just the Refresh triggers.

I use this mechanism and it works perfectly well so I can only imagine you are triggering the wrong settings or that labels are involved and you haven't set the LabelFilter in the refresher configuration.

BTW since you are using a WebHost, there's an Aspnet sibling package for that library which adds a Middleware that automatically does the refreshing for you on each request (so you don't need to track the refresher object yourself)

In any case I think this belongs in the Azure repos.

@ghost
Copy link

ghost commented May 8, 2021

Tagging subscribers to this area: @maryamariyan
See info in area-owners.md if you want to be subscribed.

Issue Details

I'm trying to use IOptionsMonitor for dinamic configuration with Azure AppConfiguration, the values are loaded correctly on starting the application. After started, when I update any settings on Azure App Configuration portal, the settings values keep the same.

I've already used App Configuration in a function and it works properly, but nowadays I'm using WebHostedService DotNetCore.

`public static void Main()
{
var host = CreateHostBuilder().Build();
host.Run();
}

    public static IHostBuilder CreateHostBuilder()
    {
        var host = Host.CreateDefaultBuilder()
             .ConfigureAppConfiguration((hostingContext, config) =>
            {
                var configuration = config.Build();

                config.AddAzureAppConfiguration(options =>
                {
                    options.Connect(configuration["ConnectionStrings:AppConfiguration"])
                           .UseFeatureFlags()
                           .ConfigureRefresh(refresh =>
                           {
                               refresh.Register("RetryPolicyConfig:IntervalSecs", true);
                               refresh.Register("RetryPolicyConfig:RetryCount", true);
                               refresh.SetCacheExpiration(new TimeSpan(30, 0, 0));
                           });

                    _refresher = options.GetRefresher();
                });
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
                webBuilder.ConfigureServices((w, s) => { s.AddSingleton(_refresher); });
            });

        return host;
    }

`
the IOptionsMonitor is inject in service class

` private readonly RetryPolicyConfig retryPolicyConfig;
private readonly IConfigurationRefresher _refresher;

    public Service(IOptionsMonitor<RetryPolicyConfig> configuration, IConfigurationRefresher refresher)
    {
        this.configuration = configuration;
        retryPolicyConfig = configuration.CurrentValue;
    }

    public async Task Execute(Commmand command)
    {
        try
        {
            await _refresher.TryRefreshAsync();
            var retryPolicy = retryPolicyConfig.RetryCount;
            var secs = retryPolicyConfig.IntervalExponentialEachRetryInSecs; //nothing happens here after updating setting by AppConfiguration`

Any idea?

Author: jonnovaretti
Assignees: -
Labels:

area-Extensions-Options, untriaged

Milestone: -

@ghost ghost added this to Untriaged in ML, Extensions, Globalization, etc, POD. May 8, 2021
@maryamariyan maryamariyan removed the untriaged New issue has not been triaged by the area owner label Jun 5, 2021
@maryamariyan maryamariyan added this to the Future milestone Jun 5, 2021
@ghost ghost moved this from Untriaged to Future in ML, Extensions, Globalization, etc, POD. Jun 5, 2021
@tommck
Copy link

tommck commented Sep 23, 2021

Wait.. seriously? This doesn't work? I was just wondering why it seemed like it wasn't and started googling. If this doesn't work, that's a major problem!

Do we at least have a workaround for this?

@MRRQX
Copy link

MRRQX commented Mar 14, 2023

I was able to confirm the firing of IOptionsMontior.OnChange().

The 'gotcha' is you have to call IConfigurationRefresher.TryRefreshAsync() after the timespan passed to IConfigurationRefresher.ProcessPushNotification() has lapsed. The default value is a random time between 0-30 seconds.

_refresher.ProcessPushNotification(notification, TimeSpan.Zero);

await _refresher.TryRefreshAsync();

@pinkfloydx33
Copy link

@MRRQX if this is a web app, the Middleware provided by this Azure maintained package will do that for you automatically, assuming you're receiving traffic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants