diff --git a/src/main/java/com/amazonaws/secretsmanager/caching/SecretCacheConfiguration.java b/src/main/java/com/amazonaws/secretsmanager/caching/SecretCacheConfiguration.java index 7284a97..744ebbd 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 forceRefreshJitterMillis = 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 getForceRefreshJitterMillis() { + return this.forceRefreshJitterMillis; + } + + /** + * 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 forceRefreshJitterMillis + * The maximum sleep time in milliseconds between force refresh calls. + * @throws IllegalArgumentException if the value is negative + */ + public void setForceRefreshJitterMillis(long forceRefreshJitterMillis) { + if (forceRefreshJitterMillis < 0) { + throw new IllegalArgumentException("Force refresh jitter must be greater than or equal to zero"); + } + this.forceRefreshJitterMillis = forceRefreshJitterMillis; + } + + /** + * 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 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 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 880e6ff..3d626c4 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.getForceRefreshJitterMillis(); 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..4f4e93b 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.getForceRefreshJitterMillis(), SecretCacheConfiguration.DEFAULT_FORCE_REFRESH_JITTER); + + // Test setting a custom value + long customJitter = 250L; + config.setForceRefreshJitterMillis(customJitter); + Assert.assertEquals(config.getForceRefreshJitterMillis(), customJitter); + + // Test zero is valid + config.setForceRefreshJitterMillis(0); + Assert.assertEquals(config.getForceRefreshJitterMillis(), 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.setForceRefreshJitterMillis(-1); + } @Test public void basicSecretCacheTest() {