public async Task RefreshAsync()

in src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs [204:461]


        public async Task RefreshAsync(CancellationToken cancellationToken)
        {
            // Ensure that concurrent threads do not simultaneously execute refresh operation.
            if (Interlocked.Exchange(ref _networkOperationsInProgress, 1) == 0)
            {
                try
                {
                    // FeatureManagement assemblies may not be loaded on provider startup, so version information is gathered upon first refresh for tracing
                    EnsureAssemblyInspected();

                    var utcNow = DateTimeOffset.UtcNow;
                    IEnumerable<KeyValueWatcher> refreshableIndividualKvWatchers = _options.IndividualKvWatchers.Where(kvWatcher => utcNow >= kvWatcher.NextRefreshTime);
                    IEnumerable<KeyValueWatcher> refreshableFfWatchers = _options.FeatureFlagWatchers.Where(ffWatcher => utcNow >= ffWatcher.NextRefreshTime);
                    bool isRefreshDue = _options.RegisterAllEnabled && utcNow >= _nextCollectionRefreshTime;

                    // Skip refresh if mappedData is loaded, but none of the watchers or adapters are refreshable.
                    if (_mappedData != null &&
                        !refreshableIndividualKvWatchers.Any() &&
                        !refreshableFfWatchers.Any() &&
                        !isRefreshDue &&
                        !_options.Adapters.Any(adapter => adapter.NeedsRefresh()))
                    {
                        return;
                    }

                    IEnumerable<ConfigurationClient> clients = _configClientManager.GetClients();

                    if (_requestTracingOptions != null)
                    {
                        _requestTracingOptions.ReplicaCount = clients.Count() - 1;
                    }

                    //
                    // Filter clients based on their backoff status
                    clients = clients.Where(client =>
                    {
                        Uri endpoint = _configClientManager.GetEndpointForClient(client);

                        if (!_configClientBackoffs.TryGetValue(endpoint, out ConfigurationClientBackoffStatus clientBackoffStatus))
                        {
                            clientBackoffStatus = new ConfigurationClientBackoffStatus();

                            _configClientBackoffs[endpoint] = clientBackoffStatus;
                        }

                        return clientBackoffStatus.BackoffEndTime <= utcNow;
                    }
                    );

                    if (!clients.Any())
                    {
                        _configClientManager.RefreshClients();

                        _logger.LogDebug(LogHelper.BuildRefreshSkippedNoClientAvailableMessage());

                        return;
                    }

                    using Activity activity = _activitySource.StartActivity(ActivityNames.Refresh);
                    // Check if initial configuration load had failed
                    if (_mappedData == null)
                    {
                        if (InitializationCacheExpires < utcNow)
                        {
                            InitializationCacheExpires = utcNow.Add(MinRefreshInterval);

                            await InitializeAsync(clients, cancellationToken).ConfigureAwait(false);
                        }

                        return;
                    }

                    //
                    // Avoid instance state modification
                    Dictionary<KeyValueSelector, IEnumerable<MatchConditions>> kvEtags = null;
                    Dictionary<KeyValueSelector, IEnumerable<MatchConditions>> ffEtags = null;
                    HashSet<string> ffKeys = null;
                    Dictionary<KeyValueIdentifier, ConfigurationSetting> watchedIndividualKvs = null;
                    List<KeyValueChange> keyValueChanges = null;
                    Dictionary<string, ConfigurationSetting> data = null;
                    Dictionary<string, ConfigurationSetting> ffCollectionData = null;
                    bool ffCollectionUpdated = false;
                    bool refreshAll = false;
                    StringBuilder logInfoBuilder = new StringBuilder();
                    StringBuilder logDebugBuilder = new StringBuilder();

                    await ExecuteWithFailOverPolicyAsync(clients, async (client) =>
                    {
                        kvEtags = null;
                        ffEtags = null;
                        ffKeys = null;
                        watchedIndividualKvs = null;
                        keyValueChanges = new List<KeyValueChange>();
                        data = null;
                        ffCollectionData = null;
                        ffCollectionUpdated = false;
                        refreshAll = false;
                        logDebugBuilder.Clear();
                        logInfoBuilder.Clear();
                        Uri endpoint = _configClientManager.GetEndpointForClient(client);

                        if (_options.RegisterAllEnabled)
                        {
                            // Get key value collection changes if RegisterAll was called
                            if (isRefreshDue)
                            {
                                refreshAll = await HaveCollectionsChanged(
                                    _options.Selectors.Where(selector => !selector.IsFeatureFlagSelector),
                                    _kvEtags,
                                    client,
                                    cancellationToken).ConfigureAwait(false);
                            }
                        }
                        else
                        {
                            refreshAll = await RefreshIndividualKvWatchers(
                                client,
                                keyValueChanges,
                                refreshableIndividualKvWatchers,
                                endpoint,
                                logDebugBuilder,
                                logInfoBuilder,
                                cancellationToken).ConfigureAwait(false);
                        }

                        if (refreshAll)
                        {
                            // Trigger a single load-all operation if a change was detected in one or more key-values with refreshAll: true,
                            // or if any key-value collection change was detected.
                            kvEtags = new Dictionary<KeyValueSelector, IEnumerable<MatchConditions>>();
                            ffEtags = new Dictionary<KeyValueSelector, IEnumerable<MatchConditions>>();
                            ffKeys = new HashSet<string>();

                            data = await LoadSelected(client, kvEtags, ffEtags, _options.Selectors, ffKeys, cancellationToken).ConfigureAwait(false);
                            watchedIndividualKvs = await LoadKeyValuesRegisteredForRefresh(client, data, cancellationToken).ConfigureAwait(false);
                            logInfoBuilder.AppendLine(LogHelper.BuildConfigurationUpdatedMessage());
                            return;
                        }

                        // Get feature flag changes
                        ffCollectionUpdated = await HaveCollectionsChanged(
                            refreshableFfWatchers.Select(watcher => new KeyValueSelector
                            {
                                KeyFilter = watcher.Key,
                                LabelFilter = watcher.Label,
                                TagFilters = watcher.Tags,
                                IsFeatureFlagSelector = true
                            }),
                            _ffEtags,
                            client,
                            cancellationToken).ConfigureAwait(false);

                        if (ffCollectionUpdated)
                        {
                            ffEtags = new Dictionary<KeyValueSelector, IEnumerable<MatchConditions>>();
                            ffKeys = new HashSet<string>();

                            ffCollectionData = await LoadSelected(
                                client,
                                new Dictionary<KeyValueSelector, IEnumerable<MatchConditions>>(),
                                ffEtags,
                                _options.Selectors.Where(selector => selector.IsFeatureFlagSelector),
                                ffKeys,
                                cancellationToken).ConfigureAwait(false);

                            logInfoBuilder.Append(LogHelper.BuildFeatureFlagsUpdatedMessage());
                        }
                        else
                        {
                            logDebugBuilder.AppendLine(LogHelper.BuildFeatureFlagsUnchangedMessage(endpoint.ToString()));
                        }
                    },
                    cancellationToken)
                    .ConfigureAwait(false);

                    if (refreshAll)
                    {
                        _mappedData = await MapConfigurationSettings(data).ConfigureAwait(false);

                        // Invalidate all the cached KeyVault secrets
                        foreach (IKeyValueAdapter adapter in _options.Adapters)
                        {
                            adapter.OnChangeDetected();
                        }

                        // Update the next refresh time for all refresh registered settings and feature flags
                        foreach (KeyValueWatcher changeWatcher in _options.IndividualKvWatchers.Concat(_options.FeatureFlagWatchers))
                        {
                            UpdateNextRefreshTime(changeWatcher);
                        }
                    }
                    else
                    {
                        watchedIndividualKvs = new Dictionary<KeyValueIdentifier, ConfigurationSetting>(_watchedIndividualKvs);

                        await ProcessKeyValueChangesAsync(keyValueChanges, _mappedData, watchedIndividualKvs).ConfigureAwait(false);

                        if (ffCollectionUpdated)
                        {
                            // Remove all feature flag keys that are not present in the latest loading of feature flags, but were loaded previously
                            foreach (string key in _ffKeys.Except(ffKeys))
                            {
                                _mappedData.Remove(key);
                            }

                            Dictionary<string, ConfigurationSetting> mappedFfData = await MapConfigurationSettings(ffCollectionData).ConfigureAwait(false);

                            foreach (KeyValuePair<string, ConfigurationSetting> kvp in mappedFfData)
                            {
                                _mappedData[kvp.Key] = kvp.Value;
                            }
                        }

                        //
                        // update the next refresh time for all refresh registered settings and feature flags
                        foreach (KeyValueWatcher changeWatcher in refreshableIndividualKvWatchers.Concat(refreshableFfWatchers))
                        {
                            UpdateNextRefreshTime(changeWatcher);
                        }
                    }

                    if (_options.RegisterAllEnabled && isRefreshDue)
                    {
                        _nextCollectionRefreshTime = DateTimeOffset.UtcNow.Add(_options.KvCollectionRefreshInterval);
                    }

                    if (_options.Adapters.Any(adapter => adapter.NeedsRefresh()) || keyValueChanges.Any() || refreshAll || ffCollectionUpdated)
                    {
                        _watchedIndividualKvs = watchedIndividualKvs ?? _watchedIndividualKvs;

                        _ffEtags = ffEtags ?? _ffEtags;

                        _kvEtags = kvEtags ?? _kvEtags;

                        _ffKeys = ffKeys ?? _ffKeys;

                        if (logDebugBuilder.Length > 0)
                        {
                            _logger.LogDebug(logDebugBuilder.ToString().Trim());
                        }

                        if (logInfoBuilder.Length > 0)
                        {
                            _logger.LogInformation(logInfoBuilder.ToString().Trim());
                        }

                        // PrepareData makes calls to KeyVault and may throw exceptions. But, we still update watchers before
                        // SetData because repeating appconfig calls (by not updating watchers) won't help anything for keyvault calls.
                        // As long as adapter.NeedsRefresh is true, we will attempt to update keyvault again the next time RefreshAsync is called.
                        SetData(await PrepareData(_mappedData, cancellationToken).ConfigureAwait(false));
                    }
                }
                finally
                {
                    Interlocked.Exchange(ref _networkOperationsInProgress, 0);
                }
            }
        }