From 91805caa9aad09c396cda220290cf1c1629cba91 Mon Sep 17 00:00:00 2001 From: Jon Chambers Date: Mon, 22 May 2023 11:44:56 -0400 Subject: [PATCH] Finalize rate limit unit inversion --- .../limits/RateLimiterConfig.java | 18 +----- .../textsecuregcm/limits/RateLimiters.java | 62 +++++++++---------- .../dynamic/DynamicConfigurationTest.java | 43 ++++--------- .../limits/RateLimiterConfigTest.java | 20 ++---- .../limits/RateLimitersLuaScriptTest.java | 10 +-- .../limits/RateLimitersTest.java | 23 ++++--- 6 files changed, 64 insertions(+), 112 deletions(-) diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiterConfig.java b/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiterConfig.java index c28aa2b1..b11cd6e7 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiterConfig.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiterConfig.java @@ -7,27 +7,15 @@ package org.whispersystems.textsecuregcm.limits; import javax.validation.constraints.AssertTrue; import java.time.Duration; -import java.util.Optional; -import java.util.OptionalDouble; -public record RateLimiterConfig(int bucketSize, OptionalDouble leakRatePerMinute, Optional permitRegenerationDuration) { +public record RateLimiterConfig(int bucketSize, Duration permitRegenerationDuration) { public double leakRatePerMillis() { - if (leakRatePerMinute.isPresent()) { - return leakRatePerMinute.getAsDouble() / (60.0 * 1000.0); - } else { - return permitRegenerationDuration.map(duration -> 1.0 / duration.toMillis()) - .orElseThrow(() -> new AssertionError("Configuration must have leak rate per minute or permit regeneration duration")); - } - } - - @AssertTrue - public boolean hasExactlyOneRegenerationRate() { - return leakRatePerMinute.isPresent() ^ permitRegenerationDuration().isPresent(); + return 1.0 / permitRegenerationDuration.toMillis(); } @AssertTrue public boolean hasPositiveRegenerationRate() { - return leakRatePerMillis() > 0; + return permitRegenerationDuration.toMillis() > 0; } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java b/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java index 37028b09..ca42dfd6 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/limits/RateLimiters.java @@ -9,8 +9,6 @@ import com.google.common.annotations.VisibleForTesting; import java.time.Clock; import java.time.Duration; import java.util.Map; -import java.util.Optional; -import java.util.OptionalDouble; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.redis.ClusterLuaScript; import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster; @@ -19,36 +17,36 @@ import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; public class RateLimiters extends BaseRateLimiters { public enum For implements RateLimiterDescriptor { - BACKUP_AUTH_CHECK("backupAuthCheck", false, new RateLimiterConfig(100, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(15)))), - SMS_DESTINATION("smsDestination", false, new RateLimiterConfig(2, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(30)))), - VOICE_DESTINATION("voxDestination", false, new RateLimiterConfig(2, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(2)))), - VOICE_DESTINATION_DAILY("voxDestinationDaily", false, new RateLimiterConfig(10, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(144)))), - SMS_VOICE_IP("smsVoiceIp", false, new RateLimiterConfig(1000, OptionalDouble.empty(), Optional.of(Duration.ofMillis(60)))), - SMS_VOICE_PREFIX("smsVoicePrefix", false, new RateLimiterConfig(1000, OptionalDouble.empty(), Optional.of(Duration.ofMillis(60)))), - VERIFY("verify", false, new RateLimiterConfig(6, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(30)))), - PIN("pin", false, new RateLimiterConfig(10, OptionalDouble.empty(), Optional.of(Duration.ofDays(1)))), - ATTACHMENT("attachmentCreate", false, new RateLimiterConfig(50, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(1)))), - PRE_KEYS("prekeys", false, new RateLimiterConfig(6, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(10)))), - MESSAGES("messages", false, new RateLimiterConfig(60, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(1)))), - ALLOCATE_DEVICE("allocateDevice", false, new RateLimiterConfig(2, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(2)))), - VERIFY_DEVICE("verifyDevice", false, new RateLimiterConfig(6, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(10)))), - TURN("turnAllocate", false, new RateLimiterConfig(60, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(1)))), - PROFILE("profile", false, new RateLimiterConfig(4320, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(20)))), - STICKER_PACK("stickerPack", false, new RateLimiterConfig(50, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(72)))), - ART_PACK("artPack", false, new RateLimiterConfig(50, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(72)))), - USERNAME_LOOKUP("usernameLookup", false, new RateLimiterConfig(100, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(15)))), - USERNAME_SET("usernameSet", false, new RateLimiterConfig(100, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(15)))), - USERNAME_RESERVE("usernameReserve", false, new RateLimiterConfig(100, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(15)))), - CHECK_ACCOUNT_EXISTENCE("checkAccountExistence", false, new RateLimiterConfig(1000, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(3)))), - REGISTRATION("registration", false, new RateLimiterConfig(6, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(30)))), - VERIFICATION_PUSH_CHALLENGE("verificationPushChallenge", false, new RateLimiterConfig(5, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(30)))), - VERIFICATION_CAPTCHA("verificationCaptcha", false, new RateLimiterConfig(10, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(30)))), - RATE_LIMIT_RESET("rateLimitReset", true, new RateLimiterConfig(2, OptionalDouble.empty(), Optional.of(Duration.ofHours(12)))), - RECAPTCHA_CHALLENGE_ATTEMPT("recaptchaChallengeAttempt", true, new RateLimiterConfig(10, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(144)))), - RECAPTCHA_CHALLENGE_SUCCESS("recaptchaChallengeSuccess", true, new RateLimiterConfig(2, OptionalDouble.empty(), Optional.of(Duration.ofHours(12)))), - PUSH_CHALLENGE_ATTEMPT("pushChallengeAttempt", true, new RateLimiterConfig(10, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(144)))), - PUSH_CHALLENGE_SUCCESS("pushChallengeSuccess", true, new RateLimiterConfig(2, OptionalDouble.empty(), Optional.of(Duration.ofHours(12)))), - CREATE_CALL_LINK("createCallLink", false, new RateLimiterConfig(100, OptionalDouble.empty(), Optional.of(Duration.ofMinutes(15)))); + BACKUP_AUTH_CHECK("backupAuthCheck", false, new RateLimiterConfig(100, Duration.ofSeconds(15))), + SMS_DESTINATION("smsDestination", false, new RateLimiterConfig(2, Duration.ofMillis(500))), + VOICE_DESTINATION("voxDestination", false, new RateLimiterConfig(2, Duration.ofSeconds(2))), + VOICE_DESTINATION_DAILY("voxDestinationDaily", false, new RateLimiterConfig(10, Duration.ofSeconds(144))), + SMS_VOICE_IP("smsVoiceIp", false, new RateLimiterConfig(1000, Duration.ofMillis(1))), + SMS_VOICE_PREFIX("smsVoicePrefix", false, new RateLimiterConfig(1000, Duration.ofMillis(1))), + VERIFY("verify", false, new RateLimiterConfig(6, Duration.ofMillis(500))), + PIN("pin", false, new RateLimiterConfig(10, Duration.ofMinutes(24))), + ATTACHMENT("attachmentCreate", false, new RateLimiterConfig(50, Duration.ofMillis(20))), + PRE_KEYS("prekeys", false, new RateLimiterConfig(6, Duration.ofSeconds(10))), + MESSAGES("messages", false, new RateLimiterConfig(60, Duration.ofMillis(17))), + ALLOCATE_DEVICE("allocateDevice", false, new RateLimiterConfig(2, Duration.ofSeconds(2))), + VERIFY_DEVICE("verifyDevice", false, new RateLimiterConfig(6, Duration.ofSeconds(10))), + TURN("turnAllocate", false, new RateLimiterConfig(60, Duration.ofMillis(17))), + PROFILE("profile", false, new RateLimiterConfig(4320, Duration.ofMillis(333))), + STICKER_PACK("stickerPack", false, new RateLimiterConfig(50, Duration.ofSeconds(72))), + ART_PACK("artPack", false, new RateLimiterConfig(50, Duration.ofSeconds(72))), + USERNAME_LOOKUP("usernameLookup", false, new RateLimiterConfig(100, Duration.ofSeconds(15))), + USERNAME_SET("usernameSet", false, new RateLimiterConfig(100, Duration.ofSeconds(15))), + USERNAME_RESERVE("usernameReserve", false, new RateLimiterConfig(100, Duration.ofSeconds(15))), + CHECK_ACCOUNT_EXISTENCE("checkAccountExistence", false, new RateLimiterConfig(1000, Duration.ofMillis(60))), + REGISTRATION("registration", false, new RateLimiterConfig(6, Duration.ofMillis(500))), + VERIFICATION_PUSH_CHALLENGE("verificationPushChallenge", false, new RateLimiterConfig(5, Duration.ofMillis(500))), + VERIFICATION_CAPTCHA("verificationCaptcha", false, new RateLimiterConfig(10, Duration.ofMillis(500))), + RATE_LIMIT_RESET("rateLimitReset", true, new RateLimiterConfig(2, Duration.ofMinutes(12))), + RECAPTCHA_CHALLENGE_ATTEMPT("recaptchaChallengeAttempt", true, new RateLimiterConfig(10, Duration.ofSeconds(144))), + RECAPTCHA_CHALLENGE_SUCCESS("recaptchaChallengeSuccess", true, new RateLimiterConfig(2, Duration.ofMinutes(12))), + PUSH_CHALLENGE_ATTEMPT("pushChallengeAttempt", true, new RateLimiterConfig(10, Duration.ofSeconds(144))), + PUSH_CHALLENGE_SUCCESS("pushChallengeSuccess", true, new RateLimiterConfig(2, Duration.ofMinutes(12))), + CREATE_CALL_LINK("createCallLink", false, new RateLimiterConfig(100, Duration.ofSeconds(15))); private final String id; diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicConfigurationTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicConfigurationTest.java index ef0a63e6..ba74ec15 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicConfigurationTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/configuration/dynamic/DynamicConfigurationTest.java @@ -18,7 +18,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.OptionalDouble; import java.util.Set; import java.util.UUID; import org.junit.jupiter.api.Test; @@ -299,39 +298,19 @@ class DynamicConfigurationTest { @Test void testParseLimits() throws JsonProcessingException { - { - final String limitsConfig = REQUIRED_CONFIG.concat(""" - limits: - rateLimitReset: - bucketSize: 17 - leakRatePerMinute: 44 - """); + final String limitsConfig = REQUIRED_CONFIG.concat(""" + limits: + rateLimitReset: + bucketSize: 17 + permitRegenerationDuration: PT4S + """); - final RateLimiterConfig resetRateLimiterConfig = - DynamicConfigurationManager.parseConfiguration(limitsConfig, DynamicConfiguration.class).orElseThrow() - .getLimits().get(RateLimiters.For.RATE_LIMIT_RESET.id()); + final RateLimiterConfig resetRateLimiterConfig = + DynamicConfigurationManager.parseConfiguration(limitsConfig, DynamicConfiguration.class).orElseThrow() + .getLimits().get(RateLimiters.For.RATE_LIMIT_RESET.id()); - assertThat(resetRateLimiterConfig.bucketSize()).isEqualTo(17); - assertThat(resetRateLimiterConfig.leakRatePerMinute()).isEqualTo(OptionalDouble.of(44)); - assertThat(resetRateLimiterConfig.permitRegenerationDuration()).isEmpty(); - } - - { - final String limitsConfig = REQUIRED_CONFIG.concat(""" - limits: - rateLimitReset: - bucketSize: 17 - permitRegenerationDuration: PT4S - """); - - final RateLimiterConfig resetRateLimiterConfig = - DynamicConfigurationManager.parseConfiguration(limitsConfig, DynamicConfiguration.class).orElseThrow() - .getLimits().get(RateLimiters.For.RATE_LIMIT_RESET.id()); - - assertThat(resetRateLimiterConfig.bucketSize()).isEqualTo(17); - assertThat(resetRateLimiterConfig.leakRatePerMinute()).isEmpty(); - assertThat(resetRateLimiterConfig.permitRegenerationDuration()).isEqualTo(Optional.of(Duration.ofSeconds(4))); - } + assertThat(resetRateLimiterConfig.bucketSize()).isEqualTo(17); + assertThat(resetRateLimiterConfig.permitRegenerationDuration()).isEqualTo(Duration.ofSeconds(4)); } @Test diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/limits/RateLimiterConfigTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/limits/RateLimiterConfigTest.java index 74a1c0b2..43a65bad 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/limits/RateLimiterConfigTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/limits/RateLimiterConfigTest.java @@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.Optional; -import java.util.OptionalDouble; import static org.junit.jupiter.api.Assertions.*; @@ -17,24 +16,13 @@ class RateLimiterConfigTest { @Test void leakRatePerMillis() { - assertEquals(0.001, new RateLimiterConfig(1, OptionalDouble.of(60), Optional.empty()).leakRatePerMillis()); - assertEquals(0.001, new RateLimiterConfig(1, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(1))).leakRatePerMillis()); - } - - @Test - void hasExactlyOneRegenerationRate() { - assertTrue(new RateLimiterConfig(1, OptionalDouble.of(1), Optional.empty()).hasExactlyOneRegenerationRate()); - assertTrue(new RateLimiterConfig(1, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(1))).hasExactlyOneRegenerationRate()); - assertFalse(new RateLimiterConfig(1, OptionalDouble.of(1), Optional.of(Duration.ofSeconds(1))).hasExactlyOneRegenerationRate()); - assertFalse(new RateLimiterConfig(1, OptionalDouble.empty(), Optional.empty()).hasExactlyOneRegenerationRate()); + assertEquals(0.001, new RateLimiterConfig(1, Duration.ofSeconds(1)).leakRatePerMillis()); } @Test void isRegenerationRatePositive() { - assertTrue(new RateLimiterConfig(1, OptionalDouble.of(1), Optional.empty()).hasPositiveRegenerationRate()); - assertTrue(new RateLimiterConfig(1, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(1))).hasPositiveRegenerationRate()); - assertFalse(new RateLimiterConfig(1, OptionalDouble.of(-1), Optional.empty()).hasPositiveRegenerationRate()); - assertFalse(new RateLimiterConfig(1, OptionalDouble.empty(), Optional.of(Duration.ofSeconds(-1))).hasPositiveRegenerationRate()); - assertFalse(new RateLimiterConfig(1, OptionalDouble.of(1 / 10), Optional.empty()).hasPositiveRegenerationRate()); + assertTrue(new RateLimiterConfig(1, Duration.ofSeconds(1)).hasPositiveRegenerationRate()); + assertFalse(new RateLimiterConfig(1, Duration.ZERO).hasPositiveRegenerationRate()); + assertFalse(new RateLimiterConfig(1, Duration.ofSeconds(-1)).hasPositiveRegenerationRate()); } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/limits/RateLimitersLuaScriptTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/limits/RateLimitersLuaScriptTest.java index 3a09408b..d2c3098b 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/limits/RateLimitersLuaScriptTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/limits/RateLimitersLuaScriptTest.java @@ -16,10 +16,10 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.lettuce.core.RedisException; import io.lettuce.core.ScriptOutputType; import java.time.Clock; +import java.time.Duration; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.OptionalDouble; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; @@ -57,7 +57,7 @@ public class RateLimitersLuaScriptTest { final RateLimiters.For descriptor = RateLimiters.For.REGISTRATION; final FaultTolerantRedisCluster redisCluster = REDIS_CLUSTER_EXTENSION.getRedisCluster(); final RateLimiters limiters = new RateLimiters( - Map.of(descriptor.id(), new RateLimiterConfig(60, OptionalDouble.of(60), Optional.empty())), + Map.of(descriptor.id(), new RateLimiterConfig(60, Duration.ofSeconds(1))), dynamicConfig, RateLimiters.defaultScript(redisCluster), redisCluster, @@ -74,7 +74,7 @@ public class RateLimitersLuaScriptTest { final RateLimiters.For descriptor = RateLimiters.For.REGISTRATION; final FaultTolerantRedisCluster redisCluster = REDIS_CLUSTER_EXTENSION.getRedisCluster(); final RateLimiters limiters = new RateLimiters( - Map.of(descriptor.id(), new RateLimiterConfig(60, OptionalDouble.of(60), Optional.empty())), + Map.of(descriptor.id(), new RateLimiterConfig(60, Duration.ofSeconds(1))), dynamicConfig, RateLimiters.defaultScript(redisCluster), redisCluster, @@ -118,7 +118,7 @@ public class RateLimitersLuaScriptTest { final RateLimiters.For descriptor = RateLimiters.For.REGISTRATION; final FaultTolerantRedisCluster redisCluster = REDIS_CLUSTER_EXTENSION.getRedisCluster(); final RateLimiters limiters = new RateLimiters( - Map.of(descriptor.id(), new RateLimiterConfig(1000, OptionalDouble.of(60), Optional.empty())), + Map.of(descriptor.id(), new RateLimiterConfig(1000, Duration.ofSeconds(1))), dynamicConfig, RateLimiters.defaultScript(redisCluster), redisCluster, @@ -169,7 +169,7 @@ public class RateLimitersLuaScriptTest { final RateLimiters.For descriptor = RateLimiters.For.REGISTRATION; final FaultTolerantRedisCluster redisCluster = mock(FaultTolerantRedisCluster.class); final RateLimiters limiters = new RateLimiters( - Map.of(descriptor.id(), new RateLimiterConfig(1000, OptionalDouble.of(60), Optional.empty())), + Map.of(descriptor.id(), new RateLimiterConfig(1000, Duration.ofSeconds(1))), dynamicConfig, RateLimiters.defaultScript(redisCluster), redisCluster, diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/limits/RateLimitersTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/limits/RateLimitersTest.java index 571a55a6..3173f875 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/limits/RateLimitersTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/limits/RateLimitersTest.java @@ -12,11 +12,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.Duration; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Optional; -import java.util.OptionalDouble; import javax.validation.Valid; import javax.validation.constraints.NotNull; import org.junit.jupiter.api.Test; @@ -46,20 +45,20 @@ public class RateLimitersTest { limits: smsVoicePrefix: bucketSize: 150 - leakRatePerMinute: 10 + permitRegenerationDuration: PT6S unexpected: bucketSize: 4 - leakRatePerMinute: 2 + permitRegenerationDuration: PT30S """; private static final String GOOD_YAML = """ limits: smsVoicePrefix: bucketSize: 150 - leakRatePerMinute: 10 + permitRegenerationDuration: PT6S attachmentCreate: bucketSize: 4 - leakRatePerMinute: 2 + permitRegenerationDuration: PT30S rateLimitPolicy: failOpen: true """; @@ -117,9 +116,9 @@ public class RateLimitersTest { @Test void testChangingConfiguration() { - final RateLimiterConfig initialRateLimiterConfig = new RateLimiterConfig(4, OptionalDouble.of(1), Optional.empty()); - final RateLimiterConfig updatedRateLimiterCongig = new RateLimiterConfig(17, OptionalDouble.of(19), Optional.empty()); - final RateLimiterConfig baseConfig = new RateLimiterConfig(1, OptionalDouble.of(1), Optional.empty()); + final RateLimiterConfig initialRateLimiterConfig = new RateLimiterConfig(4, Duration.ofMinutes(1)); + final RateLimiterConfig updatedRateLimiterCongig = new RateLimiterConfig(17, Duration.ofSeconds(3)); + final RateLimiterConfig baseConfig = new RateLimiterConfig(1, Duration.ofMinutes(1)); final Map limitsConfigMap = new HashMap<>(); @@ -147,8 +146,8 @@ public class RateLimitersTest { @Test public void testRateLimiterHasItsPrioritiesStraight() throws Exception { final RateLimiters.For descriptor = RateLimiters.For.RECAPTCHA_CHALLENGE_ATTEMPT; - final RateLimiterConfig configForDynamic = new RateLimiterConfig(1, OptionalDouble.of(1), Optional.empty()); - final RateLimiterConfig configForStatic = new RateLimiterConfig(2, OptionalDouble.of(2), Optional.empty()); + final RateLimiterConfig configForDynamic = new RateLimiterConfig(1, Duration.ofMinutes(1)); + final RateLimiterConfig configForStatic = new RateLimiterConfig(2, Duration.ofSeconds(30)); final RateLimiterConfig defaultConfig = descriptor.defaultConfig(); final Map mapForDynamic = new HashMap<>(); @@ -189,7 +188,7 @@ public class RateLimitersTest { @Override public RateLimiterConfig defaultConfig() { - return new RateLimiterConfig(1, OptionalDouble.of(1), Optional.empty()); + return new RateLimiterConfig(1, Duration.ofMinutes(1)); } } }