From ac36294d3c701920fbf91ae4409306f5f0b201ee Mon Sep 17 00:00:00 2001 From: Caitlin Russell Date: Mon, 16 Jun 2025 10:25:07 -0700 Subject: [PATCH 1/2] Decreasing default sleep time for refreshNow() calls, sleep time now configurable --- .../caching/SecretCacheConfiguration.java | 54 +++++++++++++++++++ .../caching/cache/SecretCacheObject.java | 12 ++--- .../caching/SecretCacheTest.java | 24 +++++++++ 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/amazonaws/secretsmanager/caching/SecretCacheConfiguration.java b/src/main/java/com/amazonaws/secretsmanager/caching/SecretCacheConfiguration.java index 7284a97..68c0d6d 100644 --- a/src/main/java/com/amazonaws/secretsmanager/caching/SecretCacheConfiguration.java +++ b/src/main/java/com/amazonaws/secretsmanager/caching/SecretCacheConfiguration.java @@ -34,6 +34,12 @@ public class SecretCacheConfiguration { /** The default version stage to use when retrieving secret values. */ public static final String DEFAULT_VERSION_STAGE = "AWSCURRENT"; + /** + * The default maximum jitter value in milliseconds to use when forcing a refresh. + * This prevents continuous refreshNow() calls by adding a random sleep. + */ + public static final long DEFAULT_FORCE_REFRESH_JITTER = 100; + /** The client this cache instance will use for accessing AWS Secrets Manager. */ private SecretsManagerClient client = null; @@ -60,6 +66,13 @@ public class SecretCacheConfiguration { */ private String versionStage = DEFAULT_VERSION_STAGE; + /** + * When forcing a refresh using the refreshNow method, a random sleep + * will be performed using this value. This helps prevent code from + * executing a refreshNow in a continuous loop without waiting. + */ + private long forceRefreshJitter = DEFAULT_FORCE_REFRESH_JITTER; + /** * Default constructor for the SecretCacheConfiguration object. * @@ -237,4 +250,45 @@ public SecretCacheConfiguration withVersionStage(String versionStage) { return this; } + /** + * Returns the refresh jitter that is used when force refreshing secrets. + * + * @return The maximum jitter sleep time in milliseconds used with refreshing secrets. + */ + public long getForceRefreshJitter() { + return this.forceRefreshJitter; + } + + /** + * Sets the maximum sleep time in milliseconds between force refresh calls. + * This value is used to prevent continuous refreshNow() calls in tight loops + * by adding a random sleep between half the configured value and the full value. + * The value must be greater than or equal to zero. + * + * @param forceRefreshJitter + * The maximum sleep time in milliseconds between force refresh calls. + * @throws IllegalArgumentException if the value is negative + */ + public void setForceRefreshJitter(long forceRefreshJitter) { + if (forceRefreshJitter < 0) { + throw new IllegalArgumentException("Force refresh jitter must be greater than or equal to zero"); + } + this.forceRefreshJitter = forceRefreshJitter; + } + + /** + * Sets the maximum sleep time in milliseconds between force refresh calls. + * This value is used to prevent continuous refreshNow() calls in tight loops + * by adding a random sleep between half the configured value and the full value. + * + * @param forceRefreshJitter + * The maximum sleep time in milliseconds between force refresh calls. + * @return The updated ClientConfiguration object with the new refresh sleep time. + * @throws IllegalArgumentException if the value is negative + */ + public SecretCacheConfiguration withForceRefreshJitter(long forceRefreshJitter) { + this.setForceRefreshJitter(forceRefreshJitter); + return this; + } + } diff --git a/src/main/java/com/amazonaws/secretsmanager/caching/cache/SecretCacheObject.java b/src/main/java/com/amazonaws/secretsmanager/caching/cache/SecretCacheObject.java index 880e6ff..22bfd2a 100644 --- a/src/main/java/com/amazonaws/secretsmanager/caching/cache/SecretCacheObject.java +++ b/src/main/java/com/amazonaws/secretsmanager/caching/cache/SecretCacheObject.java @@ -37,13 +37,6 @@ public abstract class SecretCacheObject { */ private static final long BACKOFF_PLATEAU = EXCEPTION_BACKOFF * 128; - /** - * When forcing a refresh using the refreshNow method, a random sleep - * will be performed using this value. This helps prevent code from - * executing a refreshNow in a continuous loop without waiting. - */ - private static final long FORCE_REFRESH_JITTER_SLEEP = 5000; - /** The secret identifier for this cached object. */ protected final String secretId; @@ -215,10 +208,11 @@ public boolean refreshNow() throws InterruptedException { // When forcing a refresh, always sleep with a random jitter // to prevent coding errors that could be calling refreshNow // in a loop. + long jitter = this.config.getForceRefreshJitter(); long sleep = ThreadLocalRandom.current() .nextLong( - FORCE_REFRESH_JITTER_SLEEP / 2, - FORCE_REFRESH_JITTER_SLEEP + 1); + jitter / 2, + jitter + 1); // Make sure we are not waiting for the next refresh after an // exception. If we are, sleep based on the retry delay of // the refresh to prevent a hard loop in attempting to refresh a diff --git a/src/test/java/com/amazonaws/secretsmanager/caching/SecretCacheTest.java b/src/test/java/com/amazonaws/secretsmanager/caching/SecretCacheTest.java index 40e64fa..7586cb3 100644 --- a/src/test/java/com/amazonaws/secretsmanager/caching/SecretCacheTest.java +++ b/src/test/java/com/amazonaws/secretsmanager/caching/SecretCacheTest.java @@ -83,6 +83,30 @@ public void secretCacheConstructorTest() { } catch (Exception e) { } } + + @Test + public void testForceRefreshJitterConfiguration() { + // Test default value + SecretCacheConfiguration config = new SecretCacheConfiguration(); + Assert.assertEquals(config.getForceRefreshJitter(), SecretCacheConfiguration.DEFAULT_FORCE_REFRESH_JITTER); + + // Test setting a custom value + long customJitter = 250L; + config.setForceRefreshJitter(customJitter); + Assert.assertEquals(config.getForceRefreshJitter(), customJitter); + + // Test zero is valid + config.setForceRefreshJitter(0); + Assert.assertEquals(config.getForceRefreshJitter(), 0); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Force refresh jitter must be greater than or equal to zero") + public void testForceRefreshJitterValidation() { + // Test that negative values throw an exception + SecretCacheConfiguration config = new SecretCacheConfiguration(); + config.setForceRefreshJitter(-1); + } @Test public void basicSecretCacheTest() { From c4a616fe018dcfdda02ab8bd8eb7bea7b15258ec Mon Sep 17 00:00:00 2001 From: crus-umich Date: Mon, 16 Jun 2025 11:52:08 -0700 Subject: [PATCH 2/2] Updated variable name --- .../caching/SecretCacheConfiguration.java | 20 +++++++++---------- .../caching/cache/SecretCacheObject.java | 2 +- .../caching/SecretCacheTest.java | 12 +++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/amazonaws/secretsmanager/caching/SecretCacheConfiguration.java b/src/main/java/com/amazonaws/secretsmanager/caching/SecretCacheConfiguration.java index 68c0d6d..744ebbd 100644 --- a/src/main/java/com/amazonaws/secretsmanager/caching/SecretCacheConfiguration.java +++ b/src/main/java/com/amazonaws/secretsmanager/caching/SecretCacheConfiguration.java @@ -71,7 +71,7 @@ public class SecretCacheConfiguration { * will be performed using this value. This helps prevent code from * executing a refreshNow in a continuous loop without waiting. */ - private long forceRefreshJitter = DEFAULT_FORCE_REFRESH_JITTER; + private long forceRefreshJitterMillis = DEFAULT_FORCE_REFRESH_JITTER; /** * Default constructor for the SecretCacheConfiguration object. @@ -255,8 +255,8 @@ public SecretCacheConfiguration withVersionStage(String versionStage) { * * @return The maximum jitter sleep time in milliseconds used with refreshing secrets. */ - public long getForceRefreshJitter() { - return this.forceRefreshJitter; + public long getForceRefreshJitterMillis() { + return this.forceRefreshJitterMillis; } /** @@ -265,15 +265,15 @@ public long getForceRefreshJitter() { * by adding a random sleep between half the configured value and the full value. * The value must be greater than or equal to zero. * - * @param forceRefreshJitter + * @param forceRefreshJitterMillis * The maximum sleep time in milliseconds between force refresh calls. * @throws IllegalArgumentException if the value is negative */ - public void setForceRefreshJitter(long forceRefreshJitter) { - if (forceRefreshJitter < 0) { + public void setForceRefreshJitterMillis(long forceRefreshJitterMillis) { + if (forceRefreshJitterMillis < 0) { throw new IllegalArgumentException("Force refresh jitter must be greater than or equal to zero"); } - this.forceRefreshJitter = forceRefreshJitter; + this.forceRefreshJitterMillis = forceRefreshJitterMillis; } /** @@ -281,13 +281,13 @@ public void setForceRefreshJitter(long forceRefreshJitter) { * This value is used to prevent continuous refreshNow() calls in tight loops * by adding a random sleep between half the configured value and the full value. * - * @param forceRefreshJitter + * @param forceRefreshJitterMillis * The maximum sleep time in milliseconds between force refresh calls. * @return The updated ClientConfiguration object with the new refresh sleep time. * @throws IllegalArgumentException if the value is negative */ - public SecretCacheConfiguration withForceRefreshJitter(long forceRefreshJitter) { - this.setForceRefreshJitter(forceRefreshJitter); + public SecretCacheConfiguration withForceRefreshJitterMillis(long forceRefreshJitterMillis) { + this.setForceRefreshJitterMillis(forceRefreshJitterMillis); return this; } diff --git a/src/main/java/com/amazonaws/secretsmanager/caching/cache/SecretCacheObject.java b/src/main/java/com/amazonaws/secretsmanager/caching/cache/SecretCacheObject.java index 22bfd2a..3d626c4 100644 --- a/src/main/java/com/amazonaws/secretsmanager/caching/cache/SecretCacheObject.java +++ b/src/main/java/com/amazonaws/secretsmanager/caching/cache/SecretCacheObject.java @@ -208,7 +208,7 @@ public boolean refreshNow() throws InterruptedException { // When forcing a refresh, always sleep with a random jitter // to prevent coding errors that could be calling refreshNow // in a loop. - long jitter = this.config.getForceRefreshJitter(); + long jitter = this.config.getForceRefreshJitterMillis(); long sleep = ThreadLocalRandom.current() .nextLong( jitter / 2, diff --git a/src/test/java/com/amazonaws/secretsmanager/caching/SecretCacheTest.java b/src/test/java/com/amazonaws/secretsmanager/caching/SecretCacheTest.java index 7586cb3..4f4e93b 100644 --- a/src/test/java/com/amazonaws/secretsmanager/caching/SecretCacheTest.java +++ b/src/test/java/com/amazonaws/secretsmanager/caching/SecretCacheTest.java @@ -88,16 +88,16 @@ public void secretCacheConstructorTest() { public void testForceRefreshJitterConfiguration() { // Test default value SecretCacheConfiguration config = new SecretCacheConfiguration(); - Assert.assertEquals(config.getForceRefreshJitter(), SecretCacheConfiguration.DEFAULT_FORCE_REFRESH_JITTER); + Assert.assertEquals(config.getForceRefreshJitterMillis(), SecretCacheConfiguration.DEFAULT_FORCE_REFRESH_JITTER); // Test setting a custom value long customJitter = 250L; - config.setForceRefreshJitter(customJitter); - Assert.assertEquals(config.getForceRefreshJitter(), customJitter); + config.setForceRefreshJitterMillis(customJitter); + Assert.assertEquals(config.getForceRefreshJitterMillis(), customJitter); // Test zero is valid - config.setForceRefreshJitter(0); - Assert.assertEquals(config.getForceRefreshJitter(), 0); + config.setForceRefreshJitterMillis(0); + Assert.assertEquals(config.getForceRefreshJitterMillis(), 0); } @Test(expectedExceptions = IllegalArgumentException.class, @@ -105,7 +105,7 @@ public void testForceRefreshJitterConfiguration() { public void testForceRefreshJitterValidation() { // Test that negative values throw an exception SecretCacheConfiguration config = new SecretCacheConfiguration(); - config.setForceRefreshJitter(-1); + config.setForceRefreshJitterMillis(-1); } @Test