@@ -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