Skip to content

Commit 74ca0f0

Browse files
committed
[Credential Cache Pr 3/3] Standardize refresh window configuration across all credential providers (#7053)
* Unify refresh window configuration across credential providers Standardize staleTime/prefetchTime defaults and add builder configuration across all credential providers: InstanceProfileCredentialsProvider: - Change default staleTime from 1 second to 1 minute - Change prefetchTime from max(timeUntilExpiry/2, 5min) to flat 5 minutes before expiry - Add prefetchTime(Duration) builder method - Add staleTime <= prefetchTime validation ContainerCredentialsProvider: - Change prefetchTime from min(1hr, 15min-before-expiry) to flat 5 minutes before expiry - Add staleTime(Duration) and prefetchTime(Duration) builder methods - Add staleTime <= prefetchTime validation ProcessCredentialsProvider: - Add staleTime(Duration) and prefetchTime(Duration) builder methods - Change default staleTime from 0 (at expiration) to 1 minute - Change default prefetchTime from 15 seconds to 5 minutes - Deprecate credentialRefreshThreshold (now sets prefetchTime) - Add staleTime <= prefetchTime validation WebIdentityTokenFileCredentialsProvider: - Add staleTime(Duration) and prefetchTime(Duration) builder methods (pass-through to underlying STS provider) StsCredentialsProvider / SsoCredentialsProvider / LoginCredentialsProvider: - Add staleTime <= prefetchTime validation - Update Javadoc for staleTime, prefetchTime, and asyncCredentialUpdateEnabled to use consistent terminology (mandatory refresh window / advisory refresh window) HttpCredentialsProvider: - Update asyncCredentialUpdateEnabled Javadoc for clarity All providers now use consistent defaults: - staleTime: 1 minute (mandatory/blocking refresh window) - prefetchTime: 5 minutes (advisory/proactive refresh window) * Update docs to match validator
1 parent 6b14305 commit 74ca0f0

11 files changed

Lines changed: 486 additions & 132 deletions

File tree

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProvider.java

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.nio.file.Files;
2626
import java.nio.file.Path;
2727
import java.nio.file.Paths;
28+
import java.time.Duration;
2829
import java.time.Instant;
2930
import java.time.temporal.ChronoUnit;
3031
import java.util.Arrays;
@@ -44,7 +45,6 @@
4445
import software.amazon.awssdk.core.util.SdkUserAgent;
4546
import software.amazon.awssdk.regions.util.ResourcesEndpointProvider;
4647
import software.amazon.awssdk.regions.util.ResourcesEndpointRetryPolicy;
47-
import software.amazon.awssdk.utils.ComparableUtils;
4848
import software.amazon.awssdk.utils.StringUtils;
4949
import software.amazon.awssdk.utils.ToString;
5050
import software.amazon.awssdk.utils.Validate;
@@ -86,6 +86,9 @@ public final class ContainerCredentialsProvider
8686
private static final List<String> VALID_LOOP_BACK_IPV4 = Arrays.asList(ECS_CONTAINER_HOST, EKS_CONTAINER_HOST_IPV4);
8787
private static final List<String> VALID_LOOP_BACK_IPV6 = Arrays.asList(EKS_CONTAINER_HOST_IPV6);
8888

89+
private static final Duration DEFAULT_STALE_TIME = Duration.ofMinutes(1);
90+
private static final Duration DEFAULT_PREFETCH_TIME = Duration.ofMinutes(5);
91+
8992
private final String endpoint;
9093
private final HttpCredentialsLoader httpCredentialsLoader;
9194
private final CachedSupplier<AwsCredentials> credentialsCache;
@@ -95,6 +98,8 @@ public final class ContainerCredentialsProvider
9598
private final String asyncThreadName;
9699
private final String sourceChain;
97100
private final String providerName;
101+
private final Duration staleTime;
102+
private final Duration prefetchTime;
98103

99104
/**
100105
* @see #builder()
@@ -108,6 +113,10 @@ private ContainerCredentialsProvider(BuilderImpl builder) {
108113
? PROVIDER_NAME
109114
: builder.sourceChain + "," + PROVIDER_NAME;
110115
this.httpCredentialsLoader = HttpCredentialsLoader.create(this.providerName);
116+
this.staleTime = Optional.ofNullable(builder.staleTime).orElse(DEFAULT_STALE_TIME);
117+
this.prefetchTime = Optional.ofNullable(builder.prefetchTime).orElse(DEFAULT_PREFETCH_TIME);
118+
Validate.isTrue(this.staleTime.compareTo(this.prefetchTime) <= 0,
119+
"staleTime (%s) must be less than or equal to prefetchTime (%s).", this.staleTime, this.prefetchTime);
111120

112121
if (Boolean.TRUE.equals(builder.asyncCredentialUpdateEnabled)) {
113122
Validate.paramNotBlank(builder.asyncThreadName, "asyncThreadName");
@@ -156,19 +165,14 @@ private Instant staleTime(Instant expiration) {
156165
return null;
157166
}
158167

159-
return expiration.minus(1, ChronoUnit.MINUTES);
168+
return expiration.minus(staleTime);
160169
}
161170

162171
private Instant prefetchTime(Instant expiration) {
163-
Instant oneHourFromNow = Instant.now().plus(1, ChronoUnit.HOURS);
164-
165172
if (expiration == null) {
166-
return oneHourFromNow;
173+
return Instant.now().plus(1, ChronoUnit.HOURS);
167174
}
168-
169-
Instant fifteenMinutesBeforeExpiration = expiration.minus(15, ChronoUnit.MINUTES);
170-
171-
return ComparableUtils.minimum(oneHourFromNow, fifteenMinutesBeforeExpiration);
175+
return expiration.minus(prefetchTime);
172176
}
173177

174178
@Override
@@ -323,13 +327,49 @@ public boolean isMetadataServiceEndpoint(String host) {
323327
*/
324328
public interface Builder extends HttpCredentialsProvider.Builder<ContainerCredentialsProvider, Builder>,
325329
CopyableBuilder<Builder, ContainerCredentialsProvider> {
330+
331+
/**
332+
* Configure the amount of time, relative to credential expiration, that defines the mandatory refresh window. When
333+
* the cached credentials are within this window (i.e., their remaining lifetime is less than this duration), the
334+
* provider will block all callers until a refresh attempt completes. If the refresh attempt fails, the provider
335+
* returns the cached credentials and will not attempt another refresh until a backoff period has elapsed.
336+
*
337+
* <p>This value must be less than or equal to {@link #prefetchTime(Duration)}. Setting this equal to
338+
* {@code prefetchTime} effectively disables prefetch, causing all refreshes to be mandatory (blocking).
339+
*
340+
* <p>By default, this is 1 minute.
341+
*
342+
* @param staleTime the duration before expiration that triggers mandatory (blocking) refresh
343+
*/
344+
Builder staleTime(Duration staleTime);
345+
346+
/**
347+
* Configure the amount of time, relative to credential expiration, that defines the advisory refresh window. When
348+
* the cached credentials are within this window (i.e., their remaining lifetime is less than this duration), the
349+
* provider will attempt to refresh them proactively. If the refresh fails, the provider returns the existing cached
350+
* credentials without error and will not attempt another refresh until a backoff period has elapsed.
351+
*
352+
* <p>When {@link #asyncCredentialUpdateEnabled(Boolean)} is true, advisory refreshes happen in a background thread
353+
* and callers immediately receive the current cached credentials. When it is false, one caller will block to perform
354+
* the refresh while other callers receive the current cached credentials.
355+
*
356+
* <p>This value must be greater than or equal to {@link #staleTime(Duration)}. Setting this equal to
357+
* {@code staleTime} effectively disables prefetch, causing all refreshes to be mandatory (blocking).
358+
*
359+
* <p>By default, this is 5 minutes.
360+
*
361+
* @param prefetchTime the duration before expiration that triggers advisory (proactive) refresh
362+
*/
363+
Builder prefetchTime(Duration prefetchTime);
326364
}
327365

328366
private static final class BuilderImpl implements Builder {
329367
private String endpoint;
330368
private Boolean asyncCredentialUpdateEnabled;
331369
private String asyncThreadName;
332370
private String sourceChain;
371+
private Duration staleTime;
372+
private Duration prefetchTime;
333373

334374
private BuilderImpl() {
335375
asyncThreadName("container-credentials-provider");
@@ -340,6 +380,8 @@ private BuilderImpl(ContainerCredentialsProvider credentialsProvider) {
340380
this.asyncCredentialUpdateEnabled = credentialsProvider.asyncCredentialUpdateEnabled;
341381
this.asyncThreadName = credentialsProvider.asyncThreadName;
342382
this.sourceChain = credentialsProvider.sourceChain;
383+
this.staleTime = credentialsProvider.staleTime;
384+
this.prefetchTime = credentialsProvider.prefetchTime;
343385
}
344386

345387
@Override
@@ -372,6 +414,26 @@ public void setAsyncThreadName(String asyncThreadName) {
372414
asyncThreadName(asyncThreadName);
373415
}
374416

417+
@Override
418+
public Builder staleTime(Duration staleTime) {
419+
this.staleTime = staleTime;
420+
return this;
421+
}
422+
423+
public void setStaleTime(Duration staleTime) {
424+
staleTime(staleTime);
425+
}
426+
427+
@Override
428+
public Builder prefetchTime(Duration prefetchTime) {
429+
this.prefetchTime = prefetchTime;
430+
return this;
431+
}
432+
433+
public void setPrefetchTime(Duration prefetchTime) {
434+
prefetchTime(prefetchTime);
435+
}
436+
375437
/**
376438
* An optional string denoting previous credentials providers that are chained with this one.
377439
* <p><b>Note:</b> This method is primarily intended for use by AWS SDK internal components

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
public interface HttpCredentialsProvider extends AwsCredentialsProvider, SdkAutoCloseable {
2929
interface Builder<TypeToBuildT extends HttpCredentialsProvider, BuilderT extends Builder<?, ?>> {
3030
/**
31-
* Configure whether the provider should fetch credentials asynchronously in the background. If this is true,
32-
* threads are less likely to block when credentials are loaded, but additional resources are used to maintain
33-
* the provider.
31+
* Configure whether the provider should fetch credentials asynchronously in the background. When enabled, a
32+
* dedicated thread performs credential refreshes during the advisory refresh window, so that callers are less
33+
* likely to block waiting for credentials. Additional resources (a thread) are used to maintain the provider.
3434
*
3535
* <p>By default, this is disabled.</p>
3636
*/

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package software.amazon.awssdk.auth.credentials;
1717

1818
import static java.time.temporal.ChronoUnit.MINUTES;
19-
import static software.amazon.awssdk.utils.ComparableUtils.maximum;
2019
import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely;
2120
import static software.amazon.awssdk.utils.cache.CachedSupplier.StaleValueBehavior.ALLOW;
2221

@@ -93,6 +92,8 @@ public final class InstanceProfileCredentialsProvider
9392

9493
private final Duration staleTime;
9594

95+
private final Duration prefetchTime;
96+
9697
private final String sourceChain;
9798
private final String providerName;
9899

@@ -120,7 +121,10 @@ private InstanceProfileCredentialsProvider(BuilderImpl builder) {
120121
.profileName(profileName)
121122
.build();
122123

123-
this.staleTime = Validate.getOrDefault(builder.staleTime, () -> Duration.ofSeconds(1));
124+
this.staleTime = Validate.getOrDefault(builder.staleTime, () -> Duration.ofMinutes(1));
125+
this.prefetchTime = Validate.getOrDefault(builder.prefetchTime, () -> Duration.ofMinutes(5));
126+
Validate.isTrue(this.staleTime.compareTo(this.prefetchTime) <= 0,
127+
"staleTime (%s) must be less than or equal to prefetchTime (%s).", this.staleTime, this.prefetchTime);
124128

125129
if (Boolean.TRUE.equals(builder.asyncCredentialUpdateEnabled)) {
126130
Validate.paramNotBlank(builder.asyncThreadName, "asyncThreadName");
@@ -204,7 +208,12 @@ private Instant prefetchTime(Instant expiration) {
204208
return null;
205209
}
206210

207-
return now.plus(maximum(timeUntilExpiration.dividedBy(2), Duration.ofMinutes(5)));
211+
// Advisory refresh window: use configured prefetchTime before expiry.
212+
// If remaining lifetime < prefetchTime, refresh immediately.
213+
if (timeUntilExpiration.compareTo(prefetchTime) < 0) {
214+
return now;
215+
}
216+
return expiration.minus(prefetchTime);
208217
}
209218

210219
@Override
@@ -355,17 +364,39 @@ public interface Builder extends HttpCredentialsProvider.Builder<InstanceProfile
355364
Builder profileName(String profileName);
356365

357366
/**
358-
* Configure the amount of time before the moment of expiration of credentials for which to consider the credentials to
359-
* be stale. A higher value can lead to a higher rate of request being made to the Amazon EC2 Instance Metadata Service.
360-
* The default is 1 sec.
361-
* <p>Increasing this value to a higher value (10s or more) may help with situations where a higher load on the instance
362-
* metadata service causes it to return 503s error, for which the SDK may not be able to recover fast enough and
363-
* returns expired credentials.
367+
* Configure the amount of time, relative to credential expiration, that defines the mandatory refresh window. When
368+
* the cached credentials are within this window (i.e., their remaining lifetime is less than this duration), the
369+
* provider will block all callers until a refresh attempt completes. If the refresh attempt fails, the provider
370+
* returns the cached credentials and will not attempt another refresh until a backoff period has elapsed.
371+
*
372+
* <p>This value must be less than or equal to {@link #prefetchTime(Duration)}. Setting this equal to
373+
* {@code prefetchTime} effectively disables prefetch, causing all refreshes to be mandatory (blocking).
364374
*
365-
* @param duration the amount of time before expiration for when to consider the credentials to be stale and need refresh
375+
* <p>By default, this is 1 minute.
376+
*
377+
* @param duration the duration before expiration that triggers mandatory (blocking) refresh
366378
*/
367379
Builder staleTime(Duration duration);
368380

381+
/**
382+
* Configure the amount of time, relative to credential expiration, that defines the advisory refresh window. When
383+
* the cached credentials are within this window (i.e., their remaining lifetime is less than this duration), the
384+
* provider will attempt to refresh them proactively. If the refresh fails, the provider returns the existing cached
385+
* credentials without error and will not attempt another refresh until a backoff period has elapsed.
386+
*
387+
* <p>When {@link #asyncCredentialUpdateEnabled(Boolean)} is true, advisory refreshes happen in a background thread
388+
* and callers immediately receive the current cached credentials. When it is false, one caller will block to perform
389+
* the refresh while other callers receive the current cached credentials.
390+
*
391+
* <p>This value must be greater than or equal to {@link #staleTime(Duration)}. Setting this equal to
392+
* {@code staleTime} effectively disables prefetch, causing all refreshes to be mandatory (blocking).
393+
*
394+
* <p>By default, this is 5 minutes.
395+
*
396+
* @param duration the duration before expiration that triggers advisory (proactive) refresh
397+
*/
398+
Builder prefetchTime(Duration duration);
399+
369400
/**
370401
* Build a {@link InstanceProfileCredentialsProvider} from the provided configuration.
371402
*/
@@ -382,6 +413,7 @@ static final class BuilderImpl implements Builder {
382413
private Supplier<ProfileFile> profileFile;
383414
private String profileName;
384415
private Duration staleTime;
416+
private Duration prefetchTime;
385417
private String sourceChain;
386418

387419
private BuilderImpl() {
@@ -396,6 +428,7 @@ private BuilderImpl(InstanceProfileCredentialsProvider provider) {
396428
this.profileFile = provider.profileFile;
397429
this.profileName = provider.profileName;
398430
this.staleTime = provider.staleTime;
431+
this.prefetchTime = provider.prefetchTime;
399432
this.sourceChain = provider.sourceChain;
400433
}
401434

@@ -475,6 +508,16 @@ public void setStaleTime(Duration duration) {
475508
staleTime(duration);
476509
}
477510

511+
@Override
512+
public Builder prefetchTime(Duration duration) {
513+
this.prefetchTime = duration;
514+
return this;
515+
}
516+
517+
public void setPrefetchTime(Duration duration) {
518+
prefetchTime(duration);
519+
}
520+
478521
/**
479522
* An optional string denoting previous credentials providers that are chained with this one.
480523
* <p><b>Note:</b> This method is primarily intended for use by AWS SDK internal components

0 commit comments

Comments
 (0)