Skip to content
Closed
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
07d60c1
add connect Cdn
SamSadfa Oct 22, 2024
c2fd979
Update src/Microsoft.Extensions.Configuration.AzureAppConfiguration/A…
samsadsam Oct 22, 2024
d16a344
make internal
SamSadfa Oct 22, 2024
c8b3535
Merge branch 'user/samisadfa/connect-cdn' of https://github.com/Azure…
SamSadfa Oct 22, 2024
f3d6b58
Merge branch 'main' into user/samisadfa/connect-cdn
SamSadfa Oct 31, 2024
a2e1c5d
add cdn client manager and api version policy
SamSadfa Oct 31, 2024
9b2bcc9
remove unused param
samsadsam Oct 31, 2024
bf85303
fix format
samsadsam Nov 4, 2024
7c4fddb
users cannot Connect/ConnectCdn at the same time
samsadsam Nov 4, 2024
d323ecd
fix assertion
samsadsam Nov 4, 2024
a2ff1b1
feedback
samsadsam Nov 7, 2024
5f16867
add cdn tracing
samsadsam Nov 7, 2024
65b81fc
merge preview
samsadsam Jan 23, 2025
0e2c621
merge with preview
samsadsam May 28, 2025
8fc28e8
remove api version policy
samsadsam May 28, 2025
2d73ebb
address comment
samsadsam May 28, 2025
ef09e70
add cdn cache busting policy + accessor for sentinel keys
samsadsam May 28, 2025
b8ca58e
now refresh with sentinel key works (tested)
samsadsam May 29, 2025
f1e4bed
move cdn client manager under cdn folder
samsadsam May 29, 2025
47a27ea
add comment
samsadsam May 29, 2025
491e948
add collection monitoring cdn cache busting support
samsadsam May 29, 2025
782dc33
change design to correct one
samsadsam May 30, 2025
dec256b
bug fix
samsadsam May 30, 2025
186288b
another bug fix
samsadsam May 30, 2025
dbfa697
iterate and implement new design
samsadsam Jun 3, 2025
cfc83ce
make code clearer
samsadsam Jun 3, 2025
3631731
nit
samsadsam Jun 3, 2025
2a7f79b
add purpose and simplify hash function
samsadsam Jun 3, 2025
d78de74
nit: cleanup and simplification
samsadsam Jun 3, 2025
43a83a0
nit: use nameof on exception message string
samsadsam Jun 3, 2025
7ff8e94
nit: Cdn.ConfigurationClientManager takes one cdn endpoint
samsadsam Jun 3, 2025
73be7e9
nit: _client -> _clientWrapper
samsadsam Jun 3, 2025
bd941f0
remove last newline, sort etags before
samsadsam Jun 4, 2025
b89f870
rename
samsadsam Jun 4, 2025
4f5108f
handle deleted sentinel kv case
samsadsam Jun 4, 2025
4fc69d3
redesign, no need to ensure state does not regress, eventual consiste…
samsadsam Jun 4, 2025
1d62185
disable load balancing and replica discovery for cdn scenario
samsadsam Jun 4, 2025
6528a36
nit: rename cdn classes accordingly
samsadsam Jun 5, 2025
a846f0f
nit: change HaveCollectionChanged to reflect its new role.
samsadsam Jun 5, 2025
c95e9ce
nit
samsadsam Jun 5, 2025
e1740c9
load balancing is not supported when cdn enabled
samsadsam Jun 5, 2025
29eed77
move load balacing check when cdn is enabled to source
samsadsam Jun 6, 2025
66eccf6
be clearer
samsadsam Jun 6, 2025
326d85b
address avani's comments
samsadsam Jun 6, 2025
984650e
adopt new pattern for tracing features
samsadsam Jun 6, 2025
e64307a
bug fix
samsadsam Jun 6, 2025
f19cb5d
address jimmy's refactor comment
samsadsam Jun 7, 2025
1803e6b
change implementation
samsadsam Jun 7, 2025
4a54b2e
nit
samsadsam Jun 7, 2025
e544048
nit: add new lines
samsadsam Jun 7, 2025
ad8c49f
Merge branch 'preview' into user/samisadfa/connect-cdn
samsadsam Jun 7, 2025
ad5ac9b
add refresh under cdn testing and fix bug
samsadsam Jun 7, 2025
33a9fb5
make tests more robust
samsadsam Jun 7, 2025
de1dc29
Merge branch 'user/samisadfa/connect-cdn' of https://github.com/Azure…
samsadsam Jun 7, 2025
1abb191
nit
samsadsam Jun 7, 2025
911f815
nits
samsadsam Jun 9, 2025
568a7e8
ensure test is inductive
samsadsam Jun 9, 2025
4880269
handle ConnectCdn with SetClientFactory scenario
samsadsam Jun 10, 2025
23acfff
nit: add ms license
samsadsam Jun 10, 2025
bd487dc
move all cdn related tests to cdn tests
samsadsam Jun 10, 2025
2c2a413
add parallel test
samsadsam Jun 10, 2025
d8f280a
fix bug
samsadsam Jun 10, 2025
ae0e448
tests: add delete sentinel key to test
samsadsam Jun 10, 2025
feaec53
done
samsadsam Jun 12, 2025
3f71dd4
done1
samsadsam Jun 12, 2025
c1d15aa
done2
samsadsam Jun 12, 2025
0292b78
Update src/Microsoft.Extensions.Configuration.AzureAppConfiguration/A…
samsadsam Jun 12, 2025
be6d888
done3
samsadsam Jun 12, 2025
cfb5cf6
Merge branch 'user/samisadfa/connect-cdn' of https://github.com/Azure…
samsadsam Jun 12, 2025
eef8180
donedone
samsadsam Jun 12, 2025
cae49ce
donedonedone
samsadsam Jun 23, 2025
39ddcae
remove auth header when connecting to cdn
samsadsam Jun 25, 2025
4a40a22
remove afd token
samsadsam Sep 18, 2025
bdbd4ef
order query params
samsadsam Sep 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Azure.Data.AppConfiguration;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.AzureKeyVault;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Cdn;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Models;
Expand Down Expand Up @@ -156,6 +157,16 @@ internal IEnumerable<IKeyValueAdapter> Adapters
/// </summary>
internal IAzureClientFactory<ConfigurationClient> ClientFactory { get; private set; }

/// <summary>
/// An accessor for current token to be used for CDN cache breakage/consistency.
/// </summary>
internal ICdnTokenAccessor CdnTokenAccessor { get; set; }

/// <summary>
/// Gets a value indicating whether CDN is enabled.
/// </summary>
internal bool IsCdnEnabled { get; private set; } = false;

/// <summary>
/// Initializes a new instance of the <see cref="AzureAppConfigurationOptions"/> class.
/// </summary>
Expand Down Expand Up @@ -339,6 +350,11 @@ public AzureAppConfigurationOptions Connect(string connectionString)
/// </param>
public AzureAppConfigurationOptions Connect(IEnumerable<string> connectionStrings)
{
if (IsCdnEnabled)
{
throw new InvalidOperationException("Cannot connect to both Azure App Configuration and CDN at the same time.");
}

if (connectionStrings == null || !connectionStrings.Any())
{
throw new ArgumentNullException(nameof(connectionStrings));
Expand All @@ -355,6 +371,29 @@ public AzureAppConfigurationOptions Connect(IEnumerable<string> connectionString
return this;
}

/// <summary>
/// Connect the provider to CDN endpoint.
/// </summary>
/// <param name="endpoint">The endpoint of the CDN instance to connect to.</param>
public AzureAppConfigurationOptions ConnectCdn(Uri endpoint)
{
if ((Credential != null && !(Credential is EmptyTokenCredential)) || (ConnectionStrings?.Any() ?? false))
{
throw new InvalidOperationException("Cannot connect to both Azure App Configuration and CDN at the same time.");
}

if (endpoint == null)
{
throw new ArgumentNullException(nameof(endpoint));
}

var result = Connect(new List<Uri>() { endpoint }, new EmptyTokenCredential());

IsCdnEnabled = true;

return result;
}

/// <summary>
/// Connect the provider to Azure App Configuration using endpoint and token credentials.
/// </summary>
Expand Down Expand Up @@ -382,6 +421,11 @@ public AzureAppConfigurationOptions Connect(Uri endpoint, TokenCredential creden
/// <param name="credential">Token credential to use to connect.</param>
public AzureAppConfigurationOptions Connect(IEnumerable<Uri> endpoints, TokenCredential credential)
{
if (IsCdnEnabled)
{
throw new InvalidOperationException("Cannot connect to both Azure App Configuration and CDN at the same time.");
}

if (endpoints == null || !endpoints.Any())
{
throw new ArgumentNullException(nameof(endpoints));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using System.Net.Http;
using System.Net.Sockets;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -39,6 +40,11 @@ internal class AzureAppConfigurationProvider : ConfigurationProvider, IConfigura
private Dictionary<Uri, ConfigurationClientBackoffStatus> _configClientBackoffs = new Dictionary<Uri, ConfigurationClientBackoffStatus>();
private DateTimeOffset _nextCollectionRefreshTime;

#region Cdn
private string _configVersion = null;
Copy link
Member

Choose a reason for hiding this comment

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

All we should need is a single etag. Doesn't matter if it's configuration or a feature flag.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What about when feature flag collection changes and the config collection doesn’t scenario?

Copy link
Member

Choose a reason for hiding this comment

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

After looking, this optimization seems valid to me. No need to reload configuration if flags are changing.

private string _ffCollectionVersion = null;
#endregion

private readonly TimeSpan MinRefreshInterval;

// The most-recent time when the refresh operation attempted to load the initial configuration
Expand Down Expand Up @@ -280,8 +286,8 @@ public async Task RefreshAsync(CancellationToken cancellationToken)
List<KeyValueChange> keyValueChanges = null;
Dictionary<string, ConfigurationSetting> data = null;
Dictionary<string, ConfigurationSetting> ffCollectionData = null;
bool ffCollectionUpdated = false;
bool refreshAll = false;
string ffCollectionUpdatedChangedEtag = null;
string refreshAllChangedEtag = null;
StringBuilder logInfoBuilder = new StringBuilder();
StringBuilder logDebugBuilder = new StringBuilder();

Expand All @@ -294,8 +300,8 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) =>
keyValueChanges = new List<KeyValueChange>();
data = null;
ffCollectionData = null;
ffCollectionUpdated = false;
refreshAll = false;
ffCollectionUpdatedChangedEtag = null;
refreshAllChangedEtag = null;
logDebugBuilder.Clear();
logInfoBuilder.Clear();
Uri endpoint = _configClientManager.GetEndpointForClient(client);
Expand All @@ -305,7 +311,12 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) =>
// Get key value collection changes if RegisterAll was called
if (isRefreshDue)
{
refreshAll = await HaveCollectionsChanged(
if (_options.IsCdnEnabled)
{
_options.CdnTokenAccessor.Current = _configVersion;
}

refreshAllChangedEtag = await GetCollectionsChangeEtag(
_options.Selectors.Where(selector => !selector.IsFeatureFlagSelector),
_kvEtags,
client,
Expand All @@ -314,7 +325,12 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) =>
}
else
{
refreshAll = await RefreshIndividualKvWatchers(
if (_options.IsCdnEnabled)
{
Copy link
Member

Choose a reason for hiding this comment

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

Is it possible for a deleted watched kv to have null etag. Then kvp.Value.ETag.ToString() will be a null reference exception.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks, added a fallback

_options.CdnTokenAccessor.Current = _configVersion;
}

refreshAllChangedEtag = await RefreshIndividualKvWatchers(
client,
keyValueChanges,
refreshableIndividualKvWatchers,
Expand All @@ -324,22 +340,39 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) =>
cancellationToken).ConfigureAwait(false);
}

if (refreshAll)
if (refreshAllChangedEtag != null)
{
// 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>();

if (_options.IsCdnEnabled)
{
//
// Break cdn cache
_options.CdnTokenAccessor.Current = refreshAllChangedEtag;

//
// Reset versions so that next watch request will not use stale versions.
_configVersion = refreshAllChangedEtag;
_ffCollectionVersion = refreshAllChangedEtag;
}

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(
if (_options.IsCdnEnabled)
{
_options.CdnTokenAccessor.Current = _ffCollectionVersion;
}

ffCollectionUpdatedChangedEtag = await GetCollectionsChangeEtag(
refreshableFfWatchers.Select(watcher => new KeyValueSelector
{
KeyFilter = watcher.Key,
Expand All @@ -350,11 +383,22 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) =>
client,
cancellationToken).ConfigureAwait(false);

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

if (_options.IsCdnEnabled)
{
//
// Break cdn cache
_options.CdnTokenAccessor.Current = ffCollectionUpdatedChangedEtag;

//
// Reset ff collection version so that next ff watch request will not use stale version.
_ffCollectionVersion = ffCollectionUpdatedChangedEtag;
}

ffCollectionData = await LoadSelected(
client,
new Dictionary<KeyValueSelector, IEnumerable<MatchConditions>>(),
Expand All @@ -373,6 +417,9 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) =>
cancellationToken)
.ConfigureAwait(false);

bool refreshAll = !string.IsNullOrEmpty(refreshAllChangedEtag);
bool ffCollectionUpdated = !string.IsNullOrEmpty(ffCollectionUpdatedChangedEtag);

if (refreshAll)
{
_mappedData = await MapConfigurationSettings(data).ConfigureAwait(false);
Expand Down Expand Up @@ -940,7 +987,7 @@ private async Task<Dictionary<KeyValueIdentifier, ConfigurationSetting>> LoadKey
return watchedIndividualKvs;
}

private async Task<bool> RefreshIndividualKvWatchers(
private async Task<string> RefreshIndividualKvWatchers(
ConfigurationClient client,
List<KeyValueChange> keyValueChanges,
IEnumerable<KeyValueWatcher> refreshableIndividualKvWatchers,
Expand All @@ -963,7 +1010,7 @@ private async Task<bool> RefreshIndividualKvWatchers(
if (_watchedIndividualKvs.TryGetValue(watchedKeyLabel, out ConfigurationSetting watchedKv))
{
await TracingUtils.CallWithRequestTracing(_requestTracingEnabled, RequestType.Watch, _requestTracingOptions,
async () => change = await client.GetKeyValueChange(watchedKv, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
async () => change = await client.GetKeyValueChange(watchedKv, makeConditionalRequest: !_options.IsCdnEnabled, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
}
else
{
Expand Down Expand Up @@ -1000,7 +1047,19 @@ await CallWithRequestTracing(

if (kvWatcher.RefreshAll)
{
return true;
string changedEtag;

if (change.ChangeType == KeyValueChangeType.Deleted)
{
using SHA256 sha256 = SHA256.Create();
changedEtag = sha256.ComputeHash(Encoding.UTF8.GetBytes($"ResourceDeleted\n{change.Previous.ETag}")).ToBase64Url();
}
else
{
changedEtag = change.Current.ETag.ToString();
}

return changedEtag;
}
}
else
Expand All @@ -1009,7 +1068,7 @@ await CallWithRequestTracing(
}
}

return false;
return null;
}

private void SetData(IDictionary<string, string> data)
Expand Down Expand Up @@ -1065,7 +1124,8 @@ private void SetRequestTracingOptions()
IsKeyVaultConfigured = _options.IsKeyVaultConfigured,
IsKeyVaultRefreshConfigured = _options.IsKeyVaultRefreshConfigured,
FeatureFlagTracing = _options.FeatureFlagTracing,
IsLoadBalancingEnabled = _options.LoadBalancingEnabled
IsLoadBalancingEnabled = _options.LoadBalancingEnabled,
IsCdnEnabled = _options.IsCdnEnabled
};
}

Expand Down Expand Up @@ -1328,33 +1388,35 @@ private void UpdateClientBackoffStatus(Uri endpoint, bool successful)
_configClientBackoffs[endpoint] = clientBackoffStatus;
}

private async Task<bool> HaveCollectionsChanged(
private async Task<string> GetCollectionsChangeEtag(
IEnumerable<KeyValueSelector> selectors,
Dictionary<KeyValueSelector, IEnumerable<MatchConditions>> pageEtags,
ConfigurationClient client,
CancellationToken cancellationToken)
{
bool haveCollectionsChanged = false;
string changedEtag = null;

foreach (KeyValueSelector selector in selectors)
{
if (pageEtags.TryGetValue(selector, out IEnumerable<MatchConditions> matchConditions))
{
await TracingUtils.CallWithRequestTracing(_requestTracingEnabled, RequestType.Watch, _requestTracingOptions,
async () => haveCollectionsChanged = await client.HaveCollectionsChanged(
async () => changedEtag = await client.GetCollectionChangeEtag(
selector,
matchConditions,
_options.ConfigurationSettingPageIterator,
makeConditionalRequest: !_options.IsCdnEnabled,
cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
}

if (haveCollectionsChanged)
if (changedEtag != null)
{
return true;
// If we have a changed ETag, we can stop checking further selectors
return changedEtag;
}
}

return haveCollectionsChanged;
return changedEtag;
}

private async Task ProcessKeyValueChangesAsync(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
using Azure.Core;
using Azure.Data.AppConfiguration;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Cdn;
using System;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -56,10 +58,25 @@ public IConfigurationProvider Build(IConfigurationBuilder builder)
}
else
{
throw new ArgumentException($"Please call {nameof(AzureAppConfigurationOptions)}.{nameof(AzureAppConfigurationOptions.Connect)} to specify how to connect to Azure App Configuration.");
throw new ArgumentException($"Please call {nameof(AzureAppConfigurationOptions)}.{nameof(AzureAppConfigurationOptions.Connect)} or {nameof(AzureAppConfigurationOptions)}.{nameof(AzureAppConfigurationOptions.ConnectCdn)} to specify how to connect to Azure App Configuration.");
}

provider = new AzureAppConfigurationProvider(new ConfigurationClientManager(clientFactory, endpoints, options.ReplicaDiscoveryEnabled, options.LoadBalancingEnabled), options, _optional);
if (options.IsCdnEnabled)
{
if (options.LoadBalancingEnabled)
{
throw new InvalidOperationException("Load balancing is not supported for CDN endpoint.");
}

options.CdnTokenAccessor = new CdnTokenAccessor();
options.ClientOptions.AddPolicy(new CdnPolicy(options.CdnTokenAccessor), HttpPipelinePosition.PerCall);

provider = new AzureAppConfigurationProvider(new CdnConfigurationClientManager(clientFactory, endpoints.First()), options, _optional);
Copy link
Member

Choose a reason for hiding this comment

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

I think we should throw if CDN is enabled and ClientFactory has been set. This usage is ambiguous since we are creating a special client with an additional policy, EmptyTokenCredentials and making non-conditional refresh requests. At this time, I dont think we should let users create their own custom client when connecting to CDN.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it is not about this, it is about re-using the same ConfigurationClient instance when calling the same cdn endpoint with parallel apps. I think we should still offer this functionality.

Copy link
Member

Choose a reason for hiding this comment

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

This is my concern:

clientFactory ??= new AzureAppConfigurationClientFactory(options.Credential, options.ClientOptions);

We do need a client factory, but I think we should always create our own instance of AzureAppConfigurationClientFactory instead of using the one passed in by users through SetClientFactory method.

Copy link
Member

Choose a reason for hiding this comment

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

Can a user shoot themselves in the foot easily if they have their own custom client factory and are using CDN? I suppose the question is, what care does a user need to take if they were to use client factory and also want to use cdn?

Copy link
Member

@avanigupta avanigupta Jun 9, 2025

Choose a reason for hiding this comment

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

Its the cache breaking policy that would be missing from custom clients. If someone wants to use client factory with cdn, their refresh requests will not contain etag in the query params.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

I still think that we should retain full control over the client used for connecting to CDN. We are already using a very customized AppConfig client to connect to FrontDoor service - which in itself is an unconventional practice. Abstracting the client logic helps prevent accidental misuse. To me, the current solutions feel off, and without a clear user scenario, it's hard to design this properly. We can always add support for a custom client factory with CDN later if customers need it.

Copy link
Member

@jimmyca15 jimmyca15 Jun 11, 2025

Choose a reason for hiding this comment

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

It's a good point. And thinking about it, one of the reasons to have the client factory method was to be able to use that same client that is loading configuration in startup to later read/write to App Configuration outside of the provider. However, with CDN, there is no write capability, and for any read calls outside of our provider there shouldn't be any desire to use our cache break mechanism that is relevant to the provider. So it seems the client factory scenario doesn't actually play well with cdn consumption.

Copy link
Member

Choose a reason for hiding this comment

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

+1 to what @avanigupta said.

We're using a client designed for one service (App Configuration) to talk to another (Azure Front Door CDN), which is already a bit of a workaround/hack. I'm unsure how well we can support a custom App Configuration client factory in this context, given the unconventional setup.

We also have the ConfigureClientOptions API, but it's unclear to what extent Azure Front Door will respect those settings. Our stance may end up being: "If it works, great; if not, there's not much we can do." Therefore, I'm hesitant to support using custom clients with CDN.

On the other hand, I don't think the custom App Configuration client factory has too many users (we probably should collect telemetry), so I don't think we miss too much anyway even if we don't support them for CDN.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}
else
{
provider = new AzureAppConfigurationProvider(new ConfigurationClientManager(clientFactory, endpoints, options.ReplicaDiscoveryEnabled, options.LoadBalancingEnabled), options, _optional);
}
}
catch (InvalidOperationException ex) // InvalidOperationException is thrown when any problems are found while configuring AzureAppConfigurationOptions or when SDK fails to create a configurationClient.
{
Expand Down
Loading