Skip to content

Commit 43fc43b

Browse files
committed
Use the AWS SDK exponential backoff logic
1 parent b7e871c commit 43fc43b

File tree

7 files changed

+356
-110
lines changed

7 files changed

+356
-110
lines changed

src/main/java/com/amazonaws/secretsmanager/caching/SecretCacheConfiguration.java

Lines changed: 131 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313

1414
package com.amazonaws.secretsmanager.caching;
1515

16-
import java.util.concurrent.TimeUnit;
17-
1816
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
1917
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
2018

19+
import java.time.Duration;
20+
2121

2222
/**
2323
* Cache configuration options such as max cache size, ttl for cached items, etc.
@@ -28,17 +28,36 @@ public class SecretCacheConfiguration {
2828
/** The default cache size. */
2929
public static final int DEFAULT_MAX_CACHE_SIZE = 1024;
3030

31-
/** The default TTL for an item stored in cache before access causing a refresh. */
32-
public static final long DEFAULT_CACHE_ITEM_TTL = TimeUnit.HOURS.toMillis(1);
31+
/**
32+
* The default TTL for an item stored in cache before access causing a refresh.
33+
*/
34+
public static final Duration DEFAULT_CACHE_ITEM_TTL_DURATION = Duration.ofHours(1);
35+
36+
/**
37+
* The default TTL for an item stored in cache before access causing a refresh.
38+
*
39+
* @deprecated use DEFAULT_CACHE_ITEM_TTL_DURATION instead.
40+
*/
41+
@Deprecated
42+
public static final long DEFAULT_CACHE_ITEM_TTL = DEFAULT_CACHE_ITEM_TTL_DURATION.toMillis();
3343

3444
/** The default version stage to use when retrieving secret values. */
3545
public static final String DEFAULT_VERSION_STAGE = "AWSCURRENT";
3646

37-
/**
38-
* The default maximum jitter value in milliseconds to use when forcing a refresh.
47+
/**
48+
* The default maximum jitter value to use when forcing a refresh.
3949
* This prevents continuous refreshNow() calls by adding a random sleep.
4050
*/
41-
public static final long DEFAULT_FORCE_REFRESH_JITTER = 100;
51+
public static final Duration DEFAULT_FORCE_REFRESH_JITTER_DURATION = Duration.ofMillis(100);
52+
53+
/**
54+
* The default maximum jitter value to use when forcing a refresh.
55+
* This prevents continuous refreshNow() calls by adding a random sleep.
56+
*
57+
* @deprecated use DEFAULT_FORCE_REFRESH_JITTER_DURATION instead
58+
*/
59+
@Deprecated
60+
public static final long DEFAULT_FORCE_REFRESH_JITTER = DEFAULT_FORCE_REFRESH_JITTER_DURATION.toMillis();
4261

4362
/** The client this cache instance will use for accessing AWS Secrets Manager. */
4463
private SecretsManagerClient client = null;
@@ -53,25 +72,20 @@ public class SecretCacheConfiguration {
5372
private int maxCacheSize = DEFAULT_MAX_CACHE_SIZE;
5473

5574
/**
56-
* The number of milliseconds that a cached item is considered valid before
57-
* requiring a refresh of the secret state. Items that have exceeded this
58-
* TTL will be refreshed synchronously when requesting the secret value. If
75+
* The duration that a cached item is considered valid before
76+
* requiring a refresh of the secret state. Items that have exceeded this
77+
* TTL will be refreshed synchronously when requesting the secret value. If
5978
* the synchronous refresh failed, the stale secret will be returned.
6079
*/
61-
private long cacheItemTTL = DEFAULT_CACHE_ITEM_TTL;
80+
private Duration cacheItemTTL = DEFAULT_CACHE_ITEM_TTL_DURATION;
6281

6382
/**
6483
* The version stage that will be used when requesting the secret values for
6584
* this cache.
6685
*/
6786
private String versionStage = DEFAULT_VERSION_STAGE;
6887

69-
/**
70-
* When forcing a refresh using the refreshNow method, a random sleep
71-
* will be performed using this value. This helps prevent code from
72-
* executing a refreshNow in a continuous loop without waiting.
73-
*/
74-
private long forceRefreshJitterMillis = DEFAULT_FORCE_REFRESH_JITTER;
88+
private Duration forceRefreshJitter = DEFAULT_FORCE_REFRESH_JITTER_DURATION;
7589

7690
/**
7791
* Default constructor for the SecretCacheConfiguration object.
@@ -186,37 +200,75 @@ public SecretCacheConfiguration withMaxCacheSize(int maxCacheSize) {
186200

187201
/**
188202
* Returns the TTL for the cached items.
203+
* @deprecated use getCacheItemTTLDuration() instead
189204
*
190205
* @return The TTL in milliseconds before refreshing cached items.
191206
*/
207+
@Deprecated
192208
public long getCacheItemTTL() {
209+
return this.cacheItemTTL.toMillis();
210+
}
211+
212+
/**
213+
* Returns the TTL for the cached items.
214+
*
215+
* @return The TTL in milliseconds before refreshing cached items.
216+
*/
217+
public Duration getCacheItemTTLDuration() {
193218
return this.cacheItemTTL;
194219
}
195220

196221
/**
197222
* Sets the TTL in milliseconds for the cached items. Once cached items exceed this
198223
* TTL, the item will be refreshed using the AWS Secrets Manager client.
199224
*
225+
* @deprecated use setCacheItemTTL(Duration cacheItemTTL) instead
200226
* @param cacheItemTTL
201227
* The TTL for cached items before requiring a refresh.
202228
*/
229+
@Deprecated
203230
public void setCacheItemTTL(long cacheItemTTL) {
231+
this.cacheItemTTL = Duration.ofMillis(cacheItemTTL);
232+
}
233+
234+
/**
235+
* Sets the TTL for the cached items. Once cached items exceed this
236+
* TTL, the item will be refreshed using the AWS Secrets Manager client.
237+
*
238+
* @param cacheItemTTL
239+
* The TTL for cached items before requiring a refresh.
240+
*/
241+
public void setCacheItemTTL(Duration cacheItemTTL) {
204242
this.cacheItemTTL = cacheItemTTL;
205243
}
206244

207245
/**
208246
* Sets the TTL in milliseconds for the cached items. Once cached items exceed this
209247
* TTL, the item will be refreshed using the AWS Secrets Manager client.
210248
*
249+
* @deprecated use withCacheItemTTL(Duration cacheItemTTL) instead
211250
* @param cacheItemTTL
212251
* The TTL for cached items before requiring a refresh.
213252
* @return The updated ClientConfiguration object with the new TTL setting.
214253
*/
254+
@Deprecated
215255
public SecretCacheConfiguration withCacheItemTTL(long cacheItemTTL) {
216256
this.setCacheItemTTL(cacheItemTTL);
217257
return this;
218258
}
219259

260+
/**
261+
* Sets the TTL for the cached items. Once cached items exceed this
262+
* TTL, the item will be refreshed using the AWS Secrets Manager client.
263+
*
264+
* @param cacheItemTTL The TTL for cached items before requiring a refresh.
265+
* @return The updated ClientConfiguration object with the new TTL setting.
266+
*/
267+
public SecretCacheConfiguration withCacheItemTTL(Duration cacheItemTTL) {
268+
this.setCacheItemTTL(cacheItemTTL);
269+
return this;
270+
}
271+
220272
/**
221273
* Returns the version stage that is used for requesting secret values.
222274
*
@@ -252,43 +304,94 @@ public SecretCacheConfiguration withVersionStage(String versionStage) {
252304

253305
/**
254306
* Returns the refresh jitter that is used when force refreshing secrets.
307+
*
308+
* @deprecated use getForceRefreshJitter() instead
255309
*
256-
* @return The maximum jitter sleep time in milliseconds used with refreshing secrets.
310+
* @return The maximum jitter sleep time in milliseconds used with refreshing
311+
* secrets.
257312
*/
313+
@Deprecated
258314
public long getForceRefreshJitterMillis() {
259-
return this.forceRefreshJitterMillis;
315+
return this.forceRefreshJitter.toMillis();
316+
}
317+
318+
/**
319+
* Returns the refresh jitter that is used when force refreshing secrets.
320+
*
321+
* @return The jitter sleep time used with refreshing secrets.
322+
*/
323+
public Duration getForceRefreshJitter() {
324+
return this.forceRefreshJitter;
260325
}
261326

262327
/**
263328
* Sets the maximum sleep time in milliseconds between force refresh calls.
264329
* This value is used to prevent continuous refreshNow() calls in tight loops
265-
* by adding a random sleep between half the configured value and the full value.
330+
* by adding a random sleep between half the configured value and the full
331+
* value.
266332
* The value must be greater than or equal to zero.
333+
*
334+
* @deprecated use setForceRefreshJitter(Duration forceRefreshJitter) instead
267335
*
268-
* @param forceRefreshJitterMillis
269-
* The maximum sleep time in milliseconds between force refresh calls.
336+
* @param forceRefreshJitterMillis The maximum sleep time in milliseconds
337+
* between force refresh calls.
270338
* @throws IllegalArgumentException if the value is negative
271339
*/
340+
@Deprecated
272341
public void setForceRefreshJitterMillis(long forceRefreshJitterMillis) {
273-
if (forceRefreshJitterMillis < 0) {
342+
this.setForceRefreshJitter(Duration.ofMillis(forceRefreshJitterMillis));
343+
}
344+
345+
/**
346+
* Sets the maximum sleep time between force refresh calls.
347+
* This value is used to prevent continuous refreshNow() calls in tight loops
348+
* by adding a random sleep between half the configured value and the full
349+
* value.
350+
* The value must be greater than or equal to zero.
351+
*
352+
* @param forceRefreshJitter The maximum sleep time between force refresh calls.
353+
* @throws IllegalArgumentException if the value is negative
354+
*/
355+
public void setForceRefreshJitter(Duration forceRefreshJitter) {
356+
if (forceRefreshJitter.isNegative()) {
274357
throw new IllegalArgumentException("Force refresh jitter must be greater than or equal to zero");
275358
}
276-
this.forceRefreshJitterMillis = forceRefreshJitterMillis;
359+
this.forceRefreshJitter = forceRefreshJitter;
277360
}
278361

279362
/**
280363
* Sets the maximum sleep time in milliseconds between force refresh calls.
281364
* This value is used to prevent continuous refreshNow() calls in tight loops
282-
* by adding a random sleep between half the configured value and the full value.
365+
* by adding a random sleep between half the configured value and the full
366+
* value.
367+
*
368+
* @deprecated use withForceRefreshJitter(Duration forceRefreshJitter) instead
283369
*
284-
* @param forceRefreshJitterMillis
285-
* The maximum sleep time in milliseconds between force refresh calls.
286-
* @return The updated ClientConfiguration object with the new refresh sleep time.
370+
* @param forceRefreshJitterMillis The maximum sleep time in milliseconds
371+
* between force refresh calls.
372+
* @return The updated ClientConfiguration object with the new refresh sleep
373+
* time.
287374
* @throws IllegalArgumentException if the value is negative
288375
*/
376+
@Deprecated
289377
public SecretCacheConfiguration withForceRefreshJitterMillis(long forceRefreshJitterMillis) {
290378
this.setForceRefreshJitterMillis(forceRefreshJitterMillis);
291379
return this;
292380
}
293381

382+
/**
383+
* Sets the maximum sleep time between force refresh calls.
384+
* This value is used to prevent continuous refreshNow() calls in tight loops
385+
* by adding a random sleep between half the configured value and the full
386+
* value.
387+
*
388+
* @param forceRefreshJitter The maximum sleep time between force refresh calls.
389+
* @return The updated ClientConfiguration object with the new refresh sleep
390+
* time.
391+
* @throws IllegalArgumentException if the value is negative
392+
*/
393+
public SecretCacheConfiguration withForceRefreshJitter(Duration forceRefreshJitter) {
394+
this.setForceRefreshJitter(forceRefreshJitter);
395+
return this;
396+
}
294397
}

src/main/java/com/amazonaws/secretsmanager/caching/cache/SecretCacheItem.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,18 @@
1313

1414
package com.amazonaws.secretsmanager.caching.cache;
1515

16-
import java.util.Objects;
17-
import java.util.Optional;
18-
import java.util.concurrent.ThreadLocalRandom;
19-
2016
import com.amazonaws.secretsmanager.caching.SecretCacheConfiguration;
21-
17+
import software.amazon.awssdk.retries.api.internal.backoff.FixedDelayWithJitter;
2218
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
2319
import software.amazon.awssdk.services.secretsmanager.model.DescribeSecretRequest;
2420
import software.amazon.awssdk.services.secretsmanager.model.DescribeSecretResponse;
2521
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse;
2622

23+
import java.time.Instant;
24+
import java.util.Objects;
25+
import java.util.Optional;
26+
import java.util.concurrent.ThreadLocalRandom;
27+
2728
/**
2829
* The cached secret item which contains information from the DescribeSecret
2930
* request to AWS Secrets Manager along with any associated GetSecretValue
@@ -39,7 +40,8 @@ public class SecretCacheItem extends SecretCacheObject<DescribeSecretResponse> {
3940
* The next scheduled refresh time for this item. Once the item is accessed
4041
* after this time, the item will be synchronously refreshed.
4142
*/
42-
private long nextRefreshTime = 0;
43+
private Instant nextRefreshTime = Instant.ofEpochMilli(0);
44+
private FixedDelayWithJitter fixedDelayWithJitter;
4345

4446
/**
4547
* Construct a new cached item for the secret.
@@ -56,6 +58,8 @@ public SecretCacheItem(final String secretId,
5658
final SecretsManagerClient client,
5759
final SecretCacheConfiguration config) {
5860
super(secretId, client, config);
61+
this.fixedDelayWithJitter = new FixedDelayWithJitter(ThreadLocalRandom::current,
62+
config.getCacheItemTTLDuration());
5963
}
6064

6165
@Override
@@ -87,10 +91,7 @@ public String toString() {
8791
protected boolean isRefreshNeeded() {
8892
if (super.isRefreshNeeded()) { return true; }
8993
if (null != this.exception) { return false; }
90-
if (System.currentTimeMillis() >= this.nextRefreshTime) {
91-
return true;
92-
}
93-
return false;
94+
return Instant.now().isAfter(nextRefreshTime);
9495
}
9596

9697
/**
@@ -101,9 +102,8 @@ protected boolean isRefreshNeeded() {
101102
@Override
102103
protected DescribeSecretResponse executeRefresh() {
103104
DescribeSecretResponse describeSecretResponse = client.describeSecret(DescribeSecretRequest.builder().secretId(this.secretId).build());
104-
long ttl = this.config.getCacheItemTTL();
105-
this.nextRefreshTime = System.currentTimeMillis() +
106-
ThreadLocalRandom.current().nextLong(ttl / 2,ttl + 1) ;
105+
// Attempt count is irrelevant for fixed delay refresh strategy
106+
this.nextRefreshTime = Instant.now().plus(this.fixedDelayWithJitter.computeDelay(1));
107107

108108
return describeSecretResponse;
109109
}

0 commit comments

Comments
 (0)