Skip to content

Commit 573320e

Browse files
authored
Merge pull request #636 from Azure/user/samisadfa/merge-main-to-preview
merge main to preview
2 parents 6dc9ae2 + 258452d commit 573320e

21 files changed

+247
-140
lines changed

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationExtensions.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ private static bool IsProviderDisabled()
3333
/// </summary>
3434
/// <param name="configurationBuilder">The configuration builder to add key-values to.</param>
3535
/// <param name="connectionString">The connection string used to connect to the configuration store.</param>
36-
/// <param name="optional">Determines the behavior of the App Configuration provider when an exception occurs while loading data from server. If false, the exception is thrown. If true, the exception is suppressed and no settings are populated from Azure App Configuration.</param>
36+
/// <param name="optional">Determines the behavior of the App Configuration provider when an exception occurs while loading data from server. If false, the exception is thrown. If true, the exception is suppressed and no settings are populated from Azure App Configuration.
37+
/// <exception cref="ArgumentException"/> will always be thrown when the caller gives an invalid input configuration (connection strings, endpoints, key/label filters...etc).
38+
/// </param>
3739
/// <returns>The provided configuration builder.</returns>
3840
public static IConfigurationBuilder AddAzureAppConfiguration(
3941
this IConfigurationBuilder configurationBuilder,
@@ -48,7 +50,9 @@ public static IConfigurationBuilder AddAzureAppConfiguration(
4850
/// </summary>
4951
/// <param name="configurationBuilder">The configuration builder to add key-values to.</param>
5052
/// <param name="connectionStrings">The list of connection strings used to connect to the configuration store and its replicas.</param>
51-
/// <param name="optional">Determines the behavior of the App Configuration provider when an exception occurs while loading data from server. If false, the exception is thrown. If true, the exception is suppressed and no settings are populated from Azure App Configuration.</param>
53+
/// <param name="optional">Determines the behavior of the App Configuration provider when an exception occurs while loading data from server. If false, the exception is thrown. If true, the exception is suppressed and no settings are populated from Azure App Configuration.
54+
/// <exception cref="ArgumentException"/> will always be thrown when the caller gives an invalid input configuration (connection strings, endpoints, key/label filters...etc).
55+
/// </param>
5256
/// <returns>The provided configuration builder.</returns>
5357
public static IConfigurationBuilder AddAzureAppConfiguration(
5458
this IConfigurationBuilder configurationBuilder,
@@ -63,7 +67,9 @@ public static IConfigurationBuilder AddAzureAppConfiguration(
6367
/// </summary>
6468
/// <param name="configurationBuilder">The configuration builder to add key-values to.</param>
6569
/// <param name="action">A callback used to configure Azure App Configuration options.</param>
66-
/// <param name="optional">Determines the behavior of the App Configuration provider when an exception occurs while loading data from server. If false, the exception is thrown. If true, the exception is suppressed and no settings are populated from Azure App Configuration.</param>
70+
/// <param name="optional">Determines the behavior of the App Configuration provider when an exception occurs while loading data from server. If false, the exception is thrown. If true, the exception is suppressed and no settings are populated from Azure App Configuration.
71+
/// <exception cref="ArgumentException"/> will always be thrown when the caller gives an invalid input configuration (connection strings, endpoints, key/label filters...etc).
72+
/// </param>
6773
/// <returns>The provided configuration builder.</returns>
6874
public static IConfigurationBuilder AddAzureAppConfiguration(
6975
this IConfigurationBuilder configurationBuilder,

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33
//
44
using Azure.Core;
5+
using Azure.Core.Pipeline;
56
using Azure.Data.AppConfiguration;
67
using Microsoft.Extensions.Azure;
78
using Microsoft.Extensions.Configuration.AzureAppConfiguration.AzureKeyVault;
@@ -11,6 +12,7 @@
1112
using System;
1213
using System.Collections.Generic;
1314
using System.Linq;
15+
using System.Net.Http;
1416
using System.Threading.Tasks;
1517

1618
namespace Microsoft.Extensions.Configuration.AzureAppConfiguration
@@ -23,6 +25,8 @@ public class AzureAppConfigurationOptions
2325
{
2426
private const int MaxRetries = 2;
2527
private static readonly TimeSpan MaxRetryDelay = TimeSpan.FromMinutes(1);
28+
private static readonly TimeSpan NetworkTimeout = TimeSpan.FromSeconds(10);
29+
private static readonly KeyValueSelector DefaultQuery = new KeyValueSelector { KeyFilter = KeyFilter.Any, LabelFilter = LabelFilter.Null };
2630

2731
private List<KeyValueWatcher> _individualKvWatchers = new List<KeyValueWatcher>();
2832
private List<KeyValueWatcher> _ffWatchers = new List<KeyValueWatcher>();
@@ -165,7 +169,7 @@ public AzureAppConfigurationOptions()
165169
};
166170

167171
// Adds the default query to App Configuration if <see cref="Select"/> and <see cref="SelectSnapshot"/> are never called.
168-
_selectors = new List<KeyValueSelector> { new KeyValueSelector { KeyFilter = KeyFilter.Any, LabelFilter = LabelFilter.Null } };
172+
_selectors = new List<KeyValueSelector> { DefaultQuery };
169173
}
170174

171175
/// <summary>
@@ -218,7 +222,7 @@ public AzureAppConfigurationOptions Select(string keyFilter, string labelFilter
218222

219223
if (!_selectCalled)
220224
{
221-
_selectors.Clear();
225+
_selectors.Remove(DefaultQuery);
222226

223227
_selectCalled = true;
224228
}
@@ -246,7 +250,7 @@ public AzureAppConfigurationOptions SelectSnapshot(string name)
246250

247251
if (!_selectCalled)
248252
{
249-
_selectors.Clear();
253+
_selectors.Remove(DefaultQuery);
250254

251255
_selectCalled = true;
252256
}
@@ -526,6 +530,10 @@ private static ConfigurationClientOptions GetDefaultClientOptions()
526530
clientOptions.Retry.MaxDelay = MaxRetryDelay;
527531
clientOptions.Retry.Mode = RetryMode.Exponential;
528532
clientOptions.AddPolicy(new UserAgentHeaderPolicy(), HttpPipelinePosition.PerCall);
533+
clientOptions.Transport = new HttpClientTransport(new HttpClient()
534+
{
535+
Timeout = NetworkTimeout
536+
});
529537

530538
return clientOptions;
531539
}

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ public AzureAppConfigurationProvider(IConfigurationClientManager configClientMan
118118

119119
if (options.RegisterAllEnabled)
120120
{
121+
if (options.KvCollectionRefreshInterval <= TimeSpan.Zero)
122+
{
123+
throw new ArgumentException(
124+
$"{nameof(options.KvCollectionRefreshInterval)} must be greater than zero seconds when using RegisterAll for refresh",
125+
nameof(options));
126+
}
127+
121128
MinRefreshInterval = TimeSpan.FromTicks(Math.Min(minWatcherRefreshInterval.Ticks, options.KvCollectionRefreshInterval.Ticks));
122129
}
123130
else if (hasWatchers)
@@ -206,7 +213,7 @@ public async Task RefreshAsync(CancellationToken cancellationToken)
206213
var utcNow = DateTimeOffset.UtcNow;
207214
IEnumerable<KeyValueWatcher> refreshableIndividualKvWatchers = _options.IndividualKvWatchers.Where(kvWatcher => utcNow >= kvWatcher.NextRefreshTime);
208215
IEnumerable<KeyValueWatcher> refreshableFfWatchers = _options.FeatureFlagWatchers.Where(ffWatcher => utcNow >= ffWatcher.NextRefreshTime);
209-
bool isRefreshDue = utcNow >= _nextCollectionRefreshTime;
216+
bool isRefreshDue = _options.RegisterAllEnabled && utcNow >= _nextCollectionRefreshTime;
210217

211218
// Skip refresh if mappedData is loaded, but none of the watchers or adapters are refreshable.
212219
if (_mappedData != null &&
@@ -220,6 +227,11 @@ public async Task RefreshAsync(CancellationToken cancellationToken)
220227

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

230+
if (_requestTracingOptions != null)
231+
{
232+
_requestTracingOptions.ReplicaCount = clients.Count() - 1;
233+
}
234+
223235
//
224236
// Filter clients based on their backoff status
225237
clients = clients.Where(client =>
@@ -407,7 +419,7 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) =>
407419
}
408420
}
409421

410-
if (isRefreshDue)
422+
if (_options.RegisterAllEnabled && isRefreshDue)
411423
{
412424
_nextCollectionRefreshTime = DateTimeOffset.UtcNow.Add(_options.KvCollectionRefreshInterval);
413425
}
@@ -543,6 +555,11 @@ public void ProcessPushNotification(PushNotification pushNotification, TimeSpan?
543555

544556
if (_configClientManager.UpdateSyncToken(pushNotification.ResourceUri, pushNotification.SyncToken))
545557
{
558+
if (_requestTracingEnabled && _requestTracingOptions != null)
559+
{
560+
_requestTracingOptions.IsPushRefreshUsed = true;
561+
}
562+
546563
SetDirty(maxDelay);
547564
}
548565
else
@@ -619,6 +636,11 @@ private async Task LoadAsync(bool ignoreFailures, CancellationToken cancellation
619636
{
620637
IEnumerable<ConfigurationClient> clients = _configClientManager.GetClients();
621638

639+
if (_requestTracingOptions != null)
640+
{
641+
_requestTracingOptions.ReplicaCount = clients.Count() - 1;
642+
}
643+
622644
if (await TryInitializeAsync(clients, startupExceptions, cancellationToken).ConfigureAwait(false))
623645
{
624646
break;
@@ -802,8 +824,6 @@ await CallWithRequestTracing(async () =>
802824
{
803825
using Response response = page.GetRawResponse();
804826

805-
ETag serverEtag = (ETag)response.Headers.ETag;
806-
807827
foreach (ConfigurationSetting setting in page.Values)
808828
{
809829
data[setting.Key] = setting;
@@ -814,7 +834,9 @@ await CallWithRequestTracing(async () =>
814834
}
815835
}
816836

817-
matchConditions.Add(new MatchConditions { IfNoneMatch = serverEtag });
837+
// The ETag will never be null here because it's not a conditional request
838+
// Each successful response should have 200 status code and an ETag
839+
matchConditions.Add(new MatchConditions { IfNoneMatch = response.Headers.ETag });
818840
}
819841
}).ConfigureAwait(false);
820842

@@ -1030,7 +1052,6 @@ private void SetRequestTracingOptions()
10301052
IsDevEnvironment = TracingUtils.IsDevEnvironment(),
10311053
IsKeyVaultConfigured = _options.IsKeyVaultConfigured,
10321054
IsKeyVaultRefreshConfigured = _options.IsKeyVaultRefreshConfigured,
1033-
ReplicaCount = _options.Endpoints?.Count() - 1 ?? _options.ConnectionStrings?.Count() - 1 ?? 0,
10341055
FeatureFlagTracing = _options.FeatureFlagTracing,
10351056
IsLoadBalancingEnabled = _options.LoadBalancingEnabled
10361057
};
@@ -1188,6 +1209,13 @@ await ExecuteWithFailOverPolicyAsync<object>(clients, async (client) =>
11881209

11891210
private bool IsFailOverable(AggregateException ex)
11901211
{
1212+
TaskCanceledException tce = ex.InnerExceptions?.LastOrDefault(e => e is TaskCanceledException) as TaskCanceledException;
1213+
1214+
if (tce != null && tce.InnerException is TimeoutException)
1215+
{
1216+
return true;
1217+
}
1218+
11911219
RequestFailedException rfe = ex.InnerExceptions?.LastOrDefault(e => e is RequestFailedException) as RequestFailedException;
11921220

11931221
return rfe != null ? IsFailOverable(rfe) : false;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
//
4+
5+
namespace Microsoft.Extensions.Configuration.AzureAppConfiguration
6+
{
7+
/// <summary>
8+
/// Provides extension methods for configuration sources.
9+
/// </summary>
10+
public static class ConfigurationSourceExtensions
11+
{
12+
/// <summary>
13+
/// Determines whether the specified configuration source is an Azure App Configuration source.
14+
/// </summary>
15+
/// <param name="source">The configuration source to check.</param>
16+
/// <returns>true if the specified source is an Azure App Configuration source; otherwise, false.</returns>
17+
public static bool IsAzureAppConfigurationSource(this IConfigurationSource source)
18+
{
19+
return source is AzureAppConfigurationSource;
20+
}
21+
}
22+
}

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Constants/RequestTracingConstants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ internal class RequestTracingConstants
3333
public const string LoadBalancingEnabledTag = "LB";
3434
public const string SignalRUsedTag = "SignalR";
3535
public const string FailoverRequestTag = "Failover";
36+
public const string PushRefreshTag = "PushRefresh";
3637

3738
public const string FeatureFlagFilterTypeKey = "Filter";
3839
public const string CustomFilter = "CSTM";
@@ -43,7 +44,6 @@ internal class RequestTracingConstants
4344
public const string FeatureFlagUsesTelemetryTag = "Telemetry";
4445
public const string FeatureFlagUsesSeedTag = "Seed";
4546
public const string FeatureFlagMaxVariantsKey = "MaxVariants";
46-
public const string FeatureFlagUsesVariantConfigurationReferenceTag = "ConfigRef";
4747

4848
public const string DiagnosticHeaderActivityName = "Azure.CustomDiagnosticHeaders";
4949
public const string CorrelationContextHeader = "Correlation-Context";

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ConfigurationClientExtensions.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ public static async Task<KeyValueChange> GetKeyValueChange(this ConfigurationCli
2929
try
3030
{
3131
Response<ConfigurationSetting> response = await client.GetConfigurationSettingAsync(setting, onlyIfChanged: true, cancellationToken).ConfigureAwait(false);
32-
if (response.GetRawResponse().Status == (int)HttpStatusCode.OK)
32+
if (response.GetRawResponse().Status == (int)HttpStatusCode.OK &&
33+
!response.Value.ETag.Equals(setting.ETag))
3334
{
3435
return new KeyValueChange
3536
{
@@ -94,11 +95,9 @@ public static async Task<bool> HaveCollectionsChanged(this ConfigurationClient c
9495
{
9596
using Response response = page.GetRawResponse();
9697

97-
ETag serverEtag = (ETag)response.Headers.ETag;
98-
9998
// Return true if the lists of etags are different
10099
if ((!existingMatchConditionsEnumerator.MoveNext() ||
101-
!existingMatchConditionsEnumerator.Current.IfNoneMatch.Equals(serverEtag)) &&
100+
!existingMatchConditionsEnumerator.Current.IfNoneMatch.Equals(response.Headers.ETag)) &&
102101
response.Status == (int)HttpStatusCode.OK)
103102
{
104103
return true;

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFlagTracing.cs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ internal class FeatureFlagTracing
2525
public bool UsesTargetingFilter { get; set; } = false;
2626
public bool UsesSeed { get; set; } = false;
2727
public bool UsesTelemetry { get; set; } = false;
28-
public bool UsesVariantConfigurationReference { get; set; } = false;
2928
public int MaxVariants { get; set; }
3029

3130
public bool UsesAnyFeatureFilter()
@@ -35,7 +34,7 @@ public bool UsesAnyFeatureFilter()
3534

3635
public bool UsesAnyTracingFeature()
3736
{
38-
return UsesSeed || UsesTelemetry || UsesVariantConfigurationReference;
37+
return UsesSeed || UsesTelemetry;
3938
}
4039

4140
public void ResetFeatureFlagTracing()
@@ -46,7 +45,6 @@ public void ResetFeatureFlagTracing()
4645
UsesTargetingFilter = false;
4746
UsesSeed = false;
4847
UsesTelemetry = false;
49-
UsesVariantConfigurationReference = false;
5048
MaxVariants = 0;
5149
}
5250

@@ -147,16 +145,6 @@ public string CreateFeaturesString()
147145
sb.Append(RequestTracingConstants.FeatureFlagUsesSeedTag);
148146
}
149147

150-
if (UsesVariantConfigurationReference)
151-
{
152-
if (sb.Length > 0)
153-
{
154-
sb.Append(RequestTracingConstants.Delimiter);
155-
}
156-
157-
sb.Append(RequestTracingConstants.FeatureFlagUsesVariantConfigurationReferenceTag);
158-
}
159-
160148
if (UsesTelemetry)
161149
{
162150
if (sb.Length > 0)

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementConstants.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ internal class FeatureManagementConstants
3030
public const string Parameters = "parameters";
3131
public const string Variant = "variant";
3232
public const string ConfigurationValue = "configuration_value";
33-
public const string ConfigurationReference = "configuration_reference";
3433
public const string StatusOverride = "status_override";
3534
public const string DefaultWhenDisabled = "default_when_disabled";
3635
public const string DefaultWhenEnabled = "default_when_enabled";

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -188,13 +188,6 @@ private List<KeyValuePair<string, string>> ProcessMicrosoftSchemaFeatureFlag(Fea
188188
(string.IsNullOrEmpty(kvp.Key) ? "" : $":{kvp.Key}"), kvp.Value));
189189
}
190190

191-
if (featureVariant.ConfigurationReference != null)
192-
{
193-
_featureFlagTracing.UsesVariantConfigurationReference = true;
194-
195-
keyValues.Add(new KeyValuePair<string, string>($"{variantsPath}:{FeatureManagementConstants.ConfigurationReference}", featureVariant.ConfigurationReference));
196-
}
197-
198191
if (featureVariant.StatusOverride != null)
199192
{
200193
keyValues.Add(new KeyValuePair<string, string>($"{variantsPath}:{FeatureManagementConstants.StatusOverride}", featureVariant.StatusOverride));
@@ -1244,24 +1237,6 @@ private FeatureVariant ParseFeatureVariant(ref Utf8JsonReader reader, string set
12441237
break;
12451238
}
12461239

1247-
case FeatureManagementConstants.ConfigurationReference:
1248-
{
1249-
if (reader.Read() && reader.TokenType == JsonTokenType.String)
1250-
{
1251-
featureVariant.ConfigurationReference = reader.GetString();
1252-
}
1253-
else if (reader.TokenType != JsonTokenType.Null)
1254-
{
1255-
throw CreateFeatureFlagFormatException(
1256-
FeatureManagementConstants.ConfigurationReference,
1257-
settingKey,
1258-
reader.TokenType.ToString(),
1259-
JsonTokenType.String.ToString());
1260-
}
1261-
1262-
break;
1263-
}
1264-
12651240
case FeatureManagementConstants.ConfigurationValue:
12661241
{
12671242
if (reader.Read())

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureVariant.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ internal class FeatureVariant
1111

1212
public JsonElement ConfigurationValue { get; set; }
1313

14-
public string ConfigurationReference { get; set; }
15-
1614
public string StatusOverride { get; set; }
1715
}
1816
}

0 commit comments

Comments
 (0)