Skip to content

Commit 9363ea8

Browse files
update
1 parent f0b8b87 commit 9363ea8

File tree

3 files changed

+89
-74
lines changed

3 files changed

+89
-74
lines changed

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

Lines changed: 85 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ internal class AzureAppConfigurationProvider : ConfigurationProvider, IConfigura
3333
private Uri _lastSuccessfulEndpoint;
3434
private AzureAppConfigurationOptions _options;
3535
private Dictionary<string, ConfigurationSetting> _mappedData;
36-
private Dictionary<KeyValueIdentifier, ConfigurationSetting> _watchedIndividualKvs = new Dictionary<KeyValueIdentifier, ConfigurationSetting>();
36+
private Dictionary<KeyValueIdentifier, WatchedSetting> _watchedIndividualKvs = new Dictionary<KeyValueIdentifier, WatchedSetting>();
3737
private HashSet<string> _ffKeys = new HashSet<string>();
3838
private Dictionary<KeyValueSelector, IEnumerable<PageWatcher>> _kvEtags = new Dictionary<KeyValueSelector, IEnumerable<PageWatcher>>();
3939
private Dictionary<KeyValueSelector, IEnumerable<PageWatcher>> _ffEtags = new Dictionary<KeyValueSelector, IEnumerable<PageWatcher>>();
@@ -42,7 +42,6 @@ internal class AzureAppConfigurationProvider : ConfigurationProvider, IConfigura
4242
private DateTimeOffset _nextCollectionRefreshTime;
4343

4444
#region Afd
45-
private Dictionary<KeyValueIdentifier, DateTimeOffset> _watchedIndividualKvChangeDetectedTime = new Dictionary<KeyValueIdentifier, DateTimeOffset>();
4645
private DateTimeOffset _lastChangeDetectedTime = default;
4746
private bool _isLastRefreshAborted = false;
4847
#endregion
@@ -64,6 +63,12 @@ internal class AzureAppConfigurationProvider : ConfigurationProvider, IConfigura
6463
private DateTimeOffset? _lastSuccessfulAttempt = null;
6564
private DateTimeOffset? _lastFailedAttempt = null;
6665

66+
private class WatchedSetting
67+
{
68+
public ConfigurationSetting setting { get; set; }
69+
public DateTimeOffset lastServerResponseTime { get; set; }
70+
}
71+
6772
private class CollectionChange
6873
{
6974
public bool Found { get; set; }
@@ -154,6 +159,24 @@ public AzureAppConfigurationProvider(IConfigurationClientManager configClientMan
154159
MinRefreshInterval = RefreshConstants.DefaultRefreshInterval;
155160
}
156161

162+
if (options.IndividualKvWatchers.Any())
163+
{
164+
foreach (KeyValueWatcher kvWatcher in options.IndividualKvWatchers)
165+
{
166+
_watchedIndividualKvs.Add(
167+
new KeyValueIdentifier
168+
{
169+
Key = kvWatcher.Key,
170+
Label = kvWatcher.Label
171+
},
172+
new WatchedSetting
173+
{
174+
setting = null,
175+
lastServerResponseTime = DateTimeOffset.MinValue
176+
});
177+
}
178+
}
179+
157180
_requestTracingEnabled = !EnvironmentVariableHelper.GetBoolOrDefault(EnvironmentVariableNames.RequestTracingDisabled);
158181

159182
if (_requestTracingEnabled)
@@ -291,8 +314,7 @@ public async Task RefreshAsync(CancellationToken cancellationToken)
291314
Dictionary<KeyValueSelector, IEnumerable<PageWatcher>> kvEtags = null;
292315
Dictionary<KeyValueSelector, IEnumerable<PageWatcher>> ffEtags = null;
293316
HashSet<string> ffKeys = null;
294-
Dictionary<KeyValueIdentifier, ConfigurationSetting> watchedIndividualKvs = null;
295-
Dictionary<KeyValueIdentifier, DateTimeOffset> watchedIndividualKvChangeDetectedTime = null;
317+
Dictionary<KeyValueIdentifier, WatchedSetting> watchedIndividualKvs = null;
296318
List<KeyValueChange> watchedIndividualKvChanges = null;
297319
Dictionary<string, ConfigurationSetting> data = null;
298320
Dictionary<string, ConfigurationSetting> ffCollectionData = null;
@@ -367,13 +389,12 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) =>
367389
kvEtags = new Dictionary<KeyValueSelector, IEnumerable<PageWatcher>>();
368390
ffEtags = new Dictionary<KeyValueSelector, IEnumerable<PageWatcher>>();
369391
ffKeys = new HashSet<string>();
370-
watchedIndividualKvChangeDetectedTime = new Dictionary<KeyValueIdentifier, DateTimeOffset>();
371392

372393
data = await LoadSelected(client, kvEtags, ffEtags, _options.Selectors, ffKeys, false, cancellationToken).ConfigureAwait(false);
373394

374395
if (!_isLastRefreshAborted)
375396
{
376-
watchedIndividualKvs = await LoadKeyValuesRegisteredForRefresh(client, data, watchedIndividualKvChangeDetectedTime, cancellationToken).ConfigureAwait(false);
397+
watchedIndividualKvs = await LoadKeyValuesRegisteredForRefresh(client, data, cancellationToken).ConfigureAwait(false);
377398
}
378399

379400
if (!_isLastRefreshAborted)
@@ -457,14 +478,12 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) =>
457478
}
458479
else
459480
{
460-
watchedIndividualKvs = new Dictionary<KeyValueIdentifier, ConfigurationSetting>(_watchedIndividualKvs);
461-
watchedIndividualKvChangeDetectedTime = new Dictionary<KeyValueIdentifier, DateTimeOffset>(_watchedIndividualKvChangeDetectedTime);
481+
watchedIndividualKvs = new Dictionary<KeyValueIdentifier, WatchedSetting>(_watchedIndividualKvs);
462482

463483
await ProcessKeyValueChangesAsync(
464484
watchedIndividualKvChanges,
465485
_mappedData,
466-
watchedIndividualKvs,
467-
watchedIndividualKvChangeDetectedTime).ConfigureAwait(false);
486+
watchedIndividualKvs).ConfigureAwait(false);
468487

469488
if (refreshFf)
470489
{
@@ -501,8 +520,6 @@ await ProcessKeyValueChangesAsync(
501520
{
502521
_watchedIndividualKvs = watchedIndividualKvs ?? _watchedIndividualKvs;
503522

504-
_watchedIndividualKvChangeDetectedTime = watchedIndividualKvChangeDetectedTime ?? _watchedIndividualKvChangeDetectedTime;
505-
506523
_ffEtags = ffEtags ?? _ffEtags;
507524

508525
_kvEtags = kvEtags ?? _kvEtags;
@@ -841,8 +858,7 @@ private async Task InitializeAsync(IEnumerable<ConfigurationClient> clients, Can
841858
Dictionary<string, ConfigurationSetting> data = null;
842859
Dictionary<KeyValueSelector, IEnumerable<PageWatcher>> kvEtags = new Dictionary<KeyValueSelector, IEnumerable<PageWatcher>>();
843860
Dictionary<KeyValueSelector, IEnumerable<PageWatcher>> ffEtags = new Dictionary<KeyValueSelector, IEnumerable<PageWatcher>>();
844-
Dictionary<KeyValueIdentifier, ConfigurationSetting> watchedIndividualKvs = null;
845-
Dictionary<KeyValueIdentifier, DateTimeOffset> watchedIndividualKvChangeDetectedTime = new Dictionary<KeyValueIdentifier, DateTimeOffset>();
861+
Dictionary<KeyValueIdentifier, WatchedSetting> watchedIndividualKvs = null;
846862
HashSet<string> ffKeys = new HashSet<string>();
847863

848864
await ExecuteWithFailOverPolicyAsync(
@@ -862,7 +878,6 @@ await ExecuteWithFailOverPolicyAsync(
862878
watchedIndividualKvs = await LoadKeyValuesRegisteredForRefresh(
863879
client,
864880
data,
865-
watchedIndividualKvChangeDetectedTime,
866881
cancellationToken)
867882
.ConfigureAwait(false);
868883
},
@@ -898,7 +913,6 @@ await ExecuteWithFailOverPolicyAsync(
898913
_kvEtags = kvEtags;
899914
_ffEtags = ffEtags;
900915
_watchedIndividualKvs = watchedIndividualKvs;
901-
_watchedIndividualKvChangeDetectedTime = watchedIndividualKvChangeDetectedTime;
902916
_ffKeys = ffKeys;
903917
}
904918
}
@@ -1059,20 +1073,21 @@ await CallWithRequestTracing(async () =>
10591073
return resolvedSettings;
10601074
}
10611075

1062-
private async Task<Dictionary<KeyValueIdentifier, ConfigurationSetting>> LoadKeyValuesRegisteredForRefresh(
1076+
private async Task<Dictionary<KeyValueIdentifier, WatchedSetting>> LoadKeyValuesRegisteredForRefresh(
10631077
ConfigurationClient client,
10641078
IDictionary<string, ConfigurationSetting> existingSettings,
1065-
Dictionary<KeyValueIdentifier, DateTimeOffset> watchedIndividualKvChangeDetectedTime,
10661079
CancellationToken cancellationToken)
10671080
{
1068-
var watchedIndividualKvs = new Dictionary<KeyValueIdentifier, ConfigurationSetting>();
1081+
var watchedIndividualKvs = new Dictionary<KeyValueIdentifier, WatchedSetting>(_watchedIndividualKvs);
10691082

10701083
foreach (KeyValueWatcher kvWatcher in _options.IndividualKvWatchers)
10711084
{
10721085
string watchedKey = kvWatcher.Key;
10731086
string watchedLabel = kvWatcher.Label;
10741087

1075-
KeyValueIdentifier watchedKeyLabel = new KeyValueIdentifier(watchedKey, watchedLabel);
1088+
var watchedKeyLabel = new KeyValueIdentifier(watchedKey, watchedLabel);
1089+
Debug.Assert(_watchedIndividualKvs.ContainsKey(watchedKeyLabel));
1090+
DateTimeOffset lastServerResponseTime = _watchedIndividualKvs[watchedKeyLabel].lastServerResponseTime;
10761091

10771092
// Send a request to retrieve key-value since it may be either not loaded or loaded with a different label or different casing
10781093
ConfigurationSetting watchedKv = null;
@@ -1086,60 +1101,42 @@ await CallWithRequestTracing(async () =>
10861101

10871102
using Response rawResponse = response.GetRawResponse();
10881103
DateTimeOffset responseDate = rawResponse.GetDate();
1089-
if (_watchedIndividualKvChangeDetectedTime.TryGetValue(watchedKeyLabel, out DateTimeOffset lastChangeDetectedTime))
1104+
1105+
if (responseDate >= lastServerResponseTime)
10901106
{
1091-
if (responseDate >= lastChangeDetectedTime)
1092-
{
1093-
watchedKv = response.Value;
1094-
watchedIndividualKvChangeDetectedTime[watchedKeyLabel] = responseDate;
1095-
_isLastRefreshAborted = false;
1096-
}
1097-
else
1098-
{
1099-
_isLastRefreshAborted = true;
1100-
return null;
1101-
}
1107+
watchedKv = response.Value;
1108+
watchedIndividualKvs[watchedKeyLabel].setting = watchedKv;
1109+
watchedIndividualKvs[watchedKeyLabel].lastServerResponseTime = responseDate;
1110+
_isLastRefreshAborted = false;
11021111
}
11031112
else
11041113
{
1105-
// initial load
1106-
watchedKv = response.Value;
1107-
watchedIndividualKvChangeDetectedTime[watchedKeyLabel] = responseDate;
1108-
_isLastRefreshAborted = false;
1114+
_isLastRefreshAborted = true;
1115+
return null;
11091116
}
11101117
}
11111118
catch (RequestFailedException e) when (e.Status == (int)HttpStatusCode.NotFound)
11121119
{
11131120
using Response rawResponse = e.GetRawResponse();
11141121
DateTimeOffset responseDate = rawResponse.GetDate();
1115-
if (_watchedIndividualKvChangeDetectedTime.TryGetValue(watchedKeyLabel, out DateTimeOffset lastChangeDetectedTime))
1122+
1123+
if (responseDate >= lastServerResponseTime)
11161124
{
1117-
if (responseDate >= lastChangeDetectedTime)
1118-
{
1119-
watchedKv = null;
1120-
watchedIndividualKvChangeDetectedTime[watchedKeyLabel] = responseDate;
1121-
_isLastRefreshAborted = false;
1122-
}
1123-
else
1124-
{
1125-
_isLastRefreshAborted = true;
1126-
return null;
1127-
}
1125+
watchedKv = null;
1126+
watchedIndividualKvs[watchedKeyLabel].setting = watchedKv;
1127+
watchedIndividualKvs[watchedKeyLabel].lastServerResponseTime = responseDate;
1128+
_isLastRefreshAborted = false;
11281129
}
11291130
else
11301131
{
1131-
// initial load
1132-
watchedKey = null;
1133-
watchedIndividualKvChangeDetectedTime[watchedKeyLabel] = responseDate;
1134-
_isLastRefreshAborted = false;
1132+
_isLastRefreshAborted = true;
1133+
return null;
11351134
}
11361135
}
11371136

11381137
// If the key-value was found, store it for updating the settings
11391138
if (watchedKv != null && existingSettings != null)
11401139
{
1141-
watchedIndividualKvs[watchedKeyLabel] = new ConfigurationSetting(watchedKv.Key, watchedKv.Value, watchedKv.Label, watchedKv.ETag);
1142-
11431140
if (watchedKv.ContentType == SnapshotReferenceConstants.ContentType)
11441141
{
11451142
// Track snapshot reference usage for telemetry
@@ -1190,29 +1187,37 @@ private async Task<bool> RefreshIndividualKvWatchers(
11901187

11911188
KeyValueChange change = default;
11921189

1193-
// Unless initial load failed, _watchedIndividualKvChangeDetectedTime should always have an entry for the watched key-label
1194-
// If fail to get, lastChangeDetectedTime will be DateTimeOffset.MinValue by default
1195-
_watchedIndividualKvChangeDetectedTime.TryGetValue(watchedKeyLabel, out DateTimeOffset lastChangeDetectedTime);
1190+
Debug.Assert(_watchedIndividualKvs.ContainsKey(watchedKeyLabel));
1191+
ConfigurationSetting watchedKv = _watchedIndividualKvs[watchedKeyLabel].setting;
1192+
DateTimeOffset lastServerResponseTime = _watchedIndividualKvs[watchedKeyLabel].lastServerResponseTime;
11961193

11971194
//
11981195
// Find if there is a change associated with watcher
1199-
if (_watchedIndividualKvs.TryGetValue(watchedKeyLabel, out ConfigurationSetting watchedKv))
1196+
if (watchedKv != null)
12001197
{
1201-
await TracingUtils.CallWithRequestTracing(_requestTracingEnabled, RequestType.Watch, _requestTracingOptions,
1202-
async () => change = await client.GetKeyValueChange(
1198+
await CallWithRequestTracing(async () => change = await client.GetKeyValueChange(
12031199
watchedKv,
12041200
makeConditionalRequest: !_options.IsAfdUsed,
1205-
lastChangeDetectedTime,
1201+
lastServerResponseTime,
12061202
cancellationToken).ConfigureAwait(false)
12071203
).ConfigureAwait(false);
12081204
}
12091205
else
12101206
{
1207+
DateTimeOffset responseDate = lastServerResponseTime;
12111208
// Load the key-value in case the previous load attempts had failed
12121209
try
12131210
{
1214-
await CallWithRequestTracing(
1215-
async () => watchedKv = await client.GetConfigurationSettingAsync(watchedKey, watchedLabel, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
1211+
await CallWithRequestTracing(async () =>
1212+
{
1213+
Response<ConfigurationSetting> response = await client.GetConfigurationSettingAsync(watchedKey, watchedLabel, cancellationToken).ConfigureAwait(false);
1214+
using Response rawResponse = response.GetRawResponse();
1215+
responseDate = rawResponse.GetDate();
1216+
if (responseDate >= lastServerResponseTime)
1217+
{
1218+
watchedKv = response.Value;
1219+
}
1220+
}).ConfigureAwait(false);
12161221
}
12171222
catch (RequestFailedException e) when (e.Status == (int)HttpStatusCode.NotFound)
12181223
{
@@ -1226,7 +1231,18 @@ await CallWithRequestTracing(
12261231
Key = watchedKv.Key,
12271232
Label = watchedKv.Label.NormalizeNull(),
12281233
Current = watchedKv,
1229-
ChangeType = KeyValueChangeType.Modified
1234+
ChangeType = KeyValueChangeType.Modified,
1235+
ServerResponseTime = responseDate
1236+
};
1237+
}
1238+
else
1239+
{
1240+
change = new KeyValueChange()
1241+
{
1242+
Key = watchedKey,
1243+
Label = watchedLabel.NormalizeNull(),
1244+
ChangeType = KeyValueChangeType.None,
1245+
ServerResponseTime = responseDate
12301246
};
12311247
}
12321248
}
@@ -1241,7 +1257,7 @@ await CallWithRequestTracing(
12411257
// If the watcher is set to refresh all, or the content type matches the snapshot reference content type then refresh all
12421258
if (kvWatcher.RefreshAll || watchedKv.ContentType == SnapshotReferenceConstants.ContentType)
12431259
{
1244-
_lastChangeDetectedTime = change.DetectedTime;
1260+
_lastChangeDetectedTime = change.ServerResponseTime;
12451261
return true;
12461262
}
12471263
}
@@ -1615,20 +1631,19 @@ await TracingUtils.CallWithRequestTracing(_requestTracingEnabled, RequestType.Wa
16151631
private async Task ProcessKeyValueChangesAsync(
16161632
IEnumerable<KeyValueChange> keyValueChanges,
16171633
Dictionary<string, ConfigurationSetting> mappedData,
1618-
Dictionary<KeyValueIdentifier, ConfigurationSetting> watchedIndividualKvs,
1619-
Dictionary<KeyValueIdentifier, DateTimeOffset> watchedIndividualKvChangeDetectedTime)
1634+
Dictionary<KeyValueIdentifier, WatchedSetting> watchedIndividualKvs)
16201635
{
16211636
foreach (KeyValueChange change in keyValueChanges)
16221637
{
16231638
KeyValueIdentifier changeIdentifier = new KeyValueIdentifier(change.Key, change.Label);
1624-
1625-
watchedIndividualKvChangeDetectedTime[changeIdentifier] = change.DetectedTime;
1639+
Debug.Assert(watchedIndividualKvs.ContainsKey(changeIdentifier));
16261640

16271641
if (change.ChangeType == KeyValueChangeType.Modified)
16281642
{
16291643
ConfigurationSetting setting = change.Current;
16301644

1631-
watchedIndividualKvs[changeIdentifier] = setting;
1645+
watchedIndividualKvs[changeIdentifier].setting = setting;
1646+
watchedIndividualKvs[changeIdentifier].lastServerResponseTime = change.ServerResponseTime;
16321647

16331648
foreach (Func<ConfigurationSetting, ValueTask<ConfigurationSetting>> func in _options.Mappers)
16341649
{

0 commit comments

Comments
 (0)