Skip to content

Add RegisterAll API to enable monitoring collections of key-values for refresh #574

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

Merged
merged 81 commits into from
Jan 22, 2025

Conversation

amerjusupovic
Copy link
Member

@amerjusupovic amerjusupovic commented Jul 15, 2024

This PR adds the new RegisterAll API to monitor any key-values added with AzureAppConfigurationOptions.Select for refresh as a collection. If a refresh is triggered and any of these registered key-values have changed, all key-values and feature flags will be reloaded. This PR will also begin monitoring feature flags as a collection instead of on an individual basis. Consequently, feature flag changes will no longer be logged on an individual basis. This solves an existing issue.

Example usage

configurationBuilder.AddAzureAppConfiguration(options =>
{
    options.Select("TestApp*");
    options.Select("Settings*");

    options.ConfigureRefresh(refresh =>
    {
        // Calling RegisterAll will monitor all key-values beginning with TestApp or Settings for refresh.
        refresh.RegisterAll();
    });
}


foreach (KeyValueWatcher changeWatcher in refreshableWatchers.Concat(refreshableMultiKeyWatchers))
// Invalidate all the cached KeyVault secrets
foreach (IKeyValueAdapter adapter in _options.Adapters)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do deleted flags get removed?

Copy link
Member Author

@amerjusupovic amerjusupovic Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed offline, lost this along the way when restructuring the refresh methods. I'll try checking the existing data and using that to decide whether the setting for that flag's key should be updated. Might need a new data structure/method for this

@@ -22,12 +22,14 @@ internal class LoggingConstants
public const string RefreshKeyVaultSecretRead = "Secret read from Key Vault for key-value.";
public const string RefreshFeatureFlagRead = "Feature flag read from App Configuration.";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, will remove

ffKeys,
cancellationToken).ConfigureAwait(false);

logInfoBuilder.Append(LogHelper.BuildFeatureFlagsUpdatedMessage());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see anywhere that we logged feature flag updates as info before. Am I missing it.

Also, why

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had a LogFeatureFlagUpdated message when refreshing individual flags in the ConfigurationClientExtensions file, so I thought it might make sense to log the collection being updated since it seems like whenever we update a value (kv, flag, secret) we would log it as info.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. In that case, I see for configuration we log "Configuration reloaded.", but for flags it is currently "Feature flags updated." It seems to me we should be consistent and log "Feature flags reloaded."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, they both do a full reload so it makes sense

Comment on lines 1311 to 1314
if (haveCollectionsChanged)
{
return haveCollectionsChanged;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (haveCollectionsChanged)
{
return haveCollectionsChanged;
}
if (haveCollectionsChanged)
{
return true;
}

@@ -376,18 +406,40 @@ public AzureAppConfigurationOptions ConfigureClientOptions(Action<ConfigurationC
/// <param name="configure">A callback used to configure Azure App Configuration refresh options.</param>
public AzureAppConfigurationOptions ConfigureRefresh(Action<AzureAppConfigurationRefreshOptions> configure)
{
if (RegisterAllEnabled)
{
throw new ArgumentException($"{nameof(ConfigureRefresh)}() cannot be invoked multiple times when {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)} has been invoked.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be an invalid operation exception. There aren't any invalid arguments.


if (!isRegisterCalled && !RegisterAllEnabled)
{
throw new ArgumentException($"{nameof(ConfigureRefresh)}() must register at least one key-value for refresh or enable refresh of all selected key-values.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that one must either call Register or RegisterAll, if so then I think it would be helpful to mention that explicitly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, since we required calling Register before, so now at least one should be called. I can update it to mention the specific APIs instead of this description in the exception

{
throw new ArgumentException($"{nameof(ConfigureRefresh)}() must have at least one key-value registered for refresh.");
throw new ArgumentException($"Cannot call both {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)} and "
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InvalidOperationException.


if (!isRegisterCalled && !RegisterAllEnabled)
{
throw new ArgumentException($"{nameof(ConfigureRefresh)}() must call either {nameof(AzureAppConfigurationRefreshOptions.Register)}()" +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also be an invalid operation exception since there are no invalid arguments.

@@ -10,5 +10,6 @@ internal class ErrorMessages
public const string FeatureFlagInvalidJsonProperty = "Invalid property '{0}' for feature flag. Key: '{1}'. Found type: '{2}'. Expected type: '{3}'.";
public const string FeatureFlagInvalidFormat = "Invalid json format for feature flag. Key: '{0}'.";
public const string InvalidKeyVaultReference = "Invalid Key Vault reference.";
public const string InvalidConfigurationSettingPage = "Invalid page while loading configuration settings.";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not used. File changes can be reverted.

@amerjusupovic amerjusupovic merged commit 5e6a012 into preview Jan 22, 2025
3 checks passed
@amerjusupovic amerjusupovic deleted the ajusupovic/collection-monitoring branch January 22, 2025 20:51
@jimmyca15
Copy link
Member

Can you provide a usage snippet in the PR description.

amerjusupovic added a commit that referenced this pull request Feb 5, 2025
amerjusupovic added a commit that referenced this pull request Feb 6, 2025
…for refresh (#574)

* WIP

* WIP testing out client extensions methods

* WIP added selectors to multikeywatchers

* remove unused property

* WIP check for registerall changes to change refreshall

* WIP

* WIP fixing types and reslving errors

* WIP fixing client extensions class

* WIP

* WIP update feature flag logic

* WIP client extensions

* WIP reload all flags on change

* WIP

* WIP fixing tests to return response for getconfigurationsettingsasync

* WIP etag for tests

* fix watchedcollections null

* WIP tests, working for examples

* remove unused variables

* update to newest sdk version, remove unused

* WIP fixing tests

* WIP reworking testing to work with new etag approach

* tests passing, fix mockasyncpageable

* update sdk package version

* fix loghelper, tests

* WIP fixing aspages tests

* revert watchesfeatureflags test

* update test again

* WIP

* fixing watchconditions

* separate selected key value collections from feature flag collections, separate selectors, add new methods to support new logic

* comment and naming updates

* fixing unit tests, namespace of defining/calling code needs to be same

* fixing tests using AsPages

* fix tests with pageablemanager

* format

* fix tests

* fix tests

* remove unused extension test class

* fix comment, capitalization

* check etag on 200, fix tests

* add registerall test, fix refresh tests

* fix condition for pages and old match conditions

* WIP fixing PR comments, tests

* check status after advancing existing etag enumerator

* move around refresh logic

* null check page etag, revert break to existing keys check in getrefreshedcollections

* fix loadselected, replace selectedkvwatchers with registerall refresh time

* fix comment in options

* clean up tests

* PR comments

* PR comments

* don't allow both registerall and register

* fix check for calls to both register methods

* PR comments for rename/small changes

* fix compile error

* simplify refreshasync path, fix naming from comments

* remove redundant if check

* simplify logic for minrefreshinterval

* fix smaller comments

* call loadselected when refreshing collection, separate data for individual refresh

* in progress change to registerall include ff

* fix load order

* fix comments, rename logging constants to match new behavior

* pr comments, refactor refreshasync

* clean up etags dictionary creation

* PR comments

* add uncommitted changes to testhelper

* update tests for registerall with feature flags, check ff keys to remove flags on refresh

* PR comments

* PR comments

* use invalidoperationexception in configurerefresh, update loggingconstants to match behavior

* remove unused changes
amerjusupovic added a commit that referenced this pull request May 2, 2025
* Adding allocation id

* serialize with sorted keys

* use string empty

* nit

* rename ff id to TelemetryVariantPercentile

* add more values

* dotnet format

* Version bump

* Add `RegisterAll` API to enable monitoring collections of key-values for refresh (#574)

* WIP

* WIP testing out client extensions methods

* WIP added selectors to multikeywatchers

* remove unused property

* WIP check for registerall changes to change refreshall

* WIP

* WIP fixing types and reslving errors

* WIP fixing client extensions class

* WIP

* WIP update feature flag logic

* WIP client extensions

* WIP reload all flags on change

* WIP

* WIP fixing tests to return response for getconfigurationsettingsasync

* WIP etag for tests

* fix watchedcollections null

* WIP tests, working for examples

* remove unused variables

* update to newest sdk version, remove unused

* WIP fixing tests

* WIP reworking testing to work with new etag approach

* tests passing, fix mockasyncpageable

* update sdk package version

* fix loghelper, tests

* WIP fixing aspages tests

* revert watchesfeatureflags test

* update test again

* WIP

* fixing watchconditions

* separate selected key value collections from feature flag collections, separate selectors, add new methods to support new logic

* comment and naming updates

* fixing unit tests, namespace of defining/calling code needs to be same

* fixing tests using AsPages

* fix tests with pageablemanager

* format

* fix tests

* fix tests

* remove unused extension test class

* fix comment, capitalization

* check etag on 200, fix tests

* add registerall test, fix refresh tests

* fix condition for pages and old match conditions

* WIP fixing PR comments, tests

* check status after advancing existing etag enumerator

* move around refresh logic

* null check page etag, revert break to existing keys check in getrefreshedcollections

* fix loadselected, replace selectedkvwatchers with registerall refresh time

* fix comment in options

* clean up tests

* PR comments

* PR comments

* don't allow both registerall and register

* fix check for calls to both register methods

* PR comments for rename/small changes

* fix compile error

* simplify refreshasync path, fix naming from comments

* remove redundant if check

* simplify logic for minrefreshinterval

* fix smaller comments

* call loadselected when refreshing collection, separate data for individual refresh

* in progress change to registerall include ff

* fix load order

* fix comments, rename logging constants to match new behavior

* pr comments, refactor refreshasync

* clean up etags dictionary creation

* PR comments

* add uncommitted changes to testhelper

* update tests for registerall with feature flags, check ff keys to remove flags on refresh

* PR comments

* PR comments

* use invalidoperationexception in configurerefresh, update loggingconstants to match behavior

* remove unused changes

* Give the users the ability to have control over ConfigurationClient instance(s) used by the provider (#598) (#617)

* Introduced a new `AzureAppConfigurationClientFactory` class to handle the creation of `ConfigurationClient` instances

* remove clients dictionary since we will not have hits and clients are already stored in ConfigurationClientManager

* revert

* add license + remove unused usings

* ran dotnet format

* add capability of fallback to different stores

* add explicit type

* address comments

* remove scheme validation

---------

Co-authored-by: Sami Sadfa <71456174+samsadsam@users.noreply.github.com>
Co-authored-by: Sami Sadfa <samisadfa@microsoft.com>

* first draft tag filtering support

* add alternate APIs

* change to use ienumerable

* update featureflagoptions to match main options

* update keyvalueselector equals and hashcode

* update param comments for selects

* fix merge conflict errors

* add validation for tagsfilter param, add to comment

* edit error message for format

* edit comment

* add unit tests

* remove unused file

* revert versions

* update tests to include feature flag select

* add refresh test

* ff only refresh test

* update equals for selector

* fix equals

* update equals

* reorder properties in keyvalueselector

* upgrade to 8.2.0-preview (#638)

* fix incorrect test

* fix equals for selector

* update gethashcode for keyvalueselector

* PR comments, in progress

* update tests from PR comments

* add validation for number of tags, add test

* rename tagsFilter to tagsFilters everywhere

* fix usings, missing updates to ffoptions

* update ffoptions select again

* fix tests

* update sdk version

* update tagsfilters to tagfilters

* remove tagsfilters again

* PR comments

* PR comments

* Revert "Merge pull request #600 from Azure/rossgrambo/allocation_id"

This reverts commit 51d4ad7, reversing
changes made to d551536.

* Revert "Give the users the ability to have control over ConfigurationClient instance(s) used by the provider (#598) (#617)"

This reverts commit 6dc9ae2.

---------

Co-authored-by: Ross Grambo <rossgrambo@microsoft.com>
Co-authored-by: Sami Sadfa <samisadfa@microsoft.com>
Co-authored-by: Sami Sadfa <71456174+samsadsam@users.noreply.github.com>
Co-authored-by: Sami Sadfa <sami.sadfa2@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add new API to reload on changes to key-values A feature flag obtained via refresh may override a feature flag with higher precedence.
7 participants