@@ -111,6 +111,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
111111 #secretReferences: ConfigurationSetting [ ] = [ ] ; // cached key vault references
112112 #secretRefreshTimer: RefreshTimer | undefined = undefined ;
113113 #resolveSecretsInParallel: boolean = false ;
114+ #keyVaultAdapter: AzureKeyVaultKeyValueAdapter ;
114115
115116 /**
116117 * Selectors of key-values obtained from @see AzureAppConfigurationOptions.selectors
@@ -204,7 +205,8 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
204205 }
205206 this . #resolveSecretsInParallel = options . keyVaultOptions . parallelSecretResolutionEnabled ?? false ;
206207 }
207- this . #adapters. push ( new AzureKeyVaultKeyValueAdapter ( options ?. keyVaultOptions , this . #secretRefreshTimer) ) ;
208+ this . #keyVaultAdapter = new AzureKeyVaultKeyValueAdapter ( options ?. keyVaultOptions , this . #secretRefreshTimer) ;
209+ this . #adapters. push ( this . #keyVaultAdapter) ;
208210 this . #adapters. push ( new JsonKeyValueAdapter ( ) ) ;
209211 }
210212
@@ -715,6 +717,8 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
715717 return Promise . resolve ( false ) ;
716718 }
717719
720+ // Invalidate cached secret values so the refresh round re-fetches them from Key Vault.
721+ this . #keyVaultAdapter. clearCache ( ) ;
718722 await this . #resolveSecretReferences( this . #secretReferences, ( key , value ) => {
719723 this . #configMap. set ( key , value ) ;
720724 } ) ;
@@ -895,22 +899,30 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
895899
896900 async #resolveSecretReferences( secretReferences : ConfigurationSetting [ ] , resultHandler : ( key : string , value : unknown ) => void ) : Promise < void > {
897901 if ( this . #resolveSecretsInParallel) {
898- const secretResolutionPromises : Promise < void > [ ] = [ ] ;
902+ // Preload phase: resolve each unique secret exactly once, in parallel, to warm the cache.
903+ // References that resolve to the same secret identifier are deduplicated so that only a
904+ // single Key Vault request is issued per unique secret.
905+ const seenSecretIds = new Set < string > ( ) ;
906+ const uniqueSecretReferences : ConfigurationSetting [ ] = [ ] ;
899907 for ( const setting of secretReferences ) {
900- const secretResolutionPromise = this . #processKeyValue( setting )
901- . then ( ( [ key , value ] ) => {
902- resultHandler ( key , value ) ;
903- } ) ;
904- secretResolutionPromises . push ( secretResolutionPromise ) ;
908+ const secretId = this . #keyVaultAdapter. getSecretReferenceId ( setting ) ;
909+ if ( secretId === undefined ) {
910+ // The reference cannot be parsed; resolve it individually so the appropriate error
911+ // is surfaced when it is processed below.
912+ uniqueSecretReferences . push ( setting ) ;
913+ } else if ( ! seenSecretIds . has ( secretId ) ) {
914+ seenSecretIds . add ( secretId ) ;
915+ uniqueSecretReferences . push ( setting ) ;
916+ }
905917 }
918+ await Promise . all ( uniqueSecretReferences . map ( setting => this . #processKeyValue( setting ) ) ) ;
919+ }
906920
907- // Wait for all secret resolution promises to be resolved
908- await Promise . all ( secretResolutionPromises ) ;
909- } else {
910- for ( const setting of secretReferences ) {
911- const [ key , value ] = await this . #processKeyValue( setting ) ;
912- resultHandler ( key , value ) ;
913- }
921+ // Resolve every reference. In parallel mode the values are served from the warmed cache
922+ // without any additional I/O.
923+ for ( const setting of secretReferences ) {
924+ const [ key , value ] = await this . #processKeyValue( setting ) ;
925+ resultHandler ( key , value ) ;
914926 }
915927 }
916928
0 commit comments