Introduce a hyper-log-log-based cardinality rate limiter

This commit is contained in:
Jon Chambers
2021-02-11 10:36:26 -05:00
committed by GitHub
parent dcbf285fae
commit e0ed8fa0b8
11 changed files with 235 additions and 39 deletions

View File

@@ -57,6 +57,7 @@ import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntity;
import org.whispersystems.textsecuregcm.entities.OutgoingMessageEntityList;
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
import org.whispersystems.textsecuregcm.entities.StaleDevices;
import org.whispersystems.textsecuregcm.limits.CardinalityRateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.push.ApnFallbackManager;
@@ -84,7 +85,7 @@ public class MessageControllerTest {
private final MessagesManager messagesManager = mock(MessagesManager.class);
private final RateLimiters rateLimiters = mock(RateLimiters.class);
private final RateLimiter rateLimiter = mock(RateLimiter.class);
private final RateLimiter unsealedSenderLimiter = mock(RateLimiter.class);
private final CardinalityRateLimiter unsealedSenderLimiter = mock(CardinalityRateLimiter.class);
private final ApnFallbackManager apnFallbackManager = mock(ApnFallbackManager.class);
private final FeatureFlagsManager featureFlagsManager = mock(FeatureFlagsManager.class);

View File

@@ -5,11 +5,14 @@ import org.junit.Test;
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRateLimitsConfiguration;
import org.whispersystems.textsecuregcm.limits.CardinalityRateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
import java.time.Duration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
@@ -34,11 +37,11 @@ public class DynamicRateLimitsTest {
public void testUnchangingConfiguration() {
RateLimiters rateLimiters = new RateLimiters(new RateLimitsConfiguration(), dynamicConfig, redisCluster);
RateLimiter limiter = rateLimiters.getUnsealedSenderLimiter();
RateLimiter limiter = rateLimiters.getUnsealedIpLimiter();
assertThat(limiter.getBucketSize()).isEqualTo(dynamicConfig.getConfiguration().getLimits().getUnsealedSenderNumber().getBucketSize());
assertThat(limiter.getLeakRatePerMinute()).isEqualTo(dynamicConfig.getConfiguration().getLimits().getUnsealedSenderNumber().getLeakRatePerMinute());
assertSame(rateLimiters.getUnsealedSenderLimiter(), limiter);
assertThat(limiter.getBucketSize()).isEqualTo(dynamicConfig.getConfiguration().getLimits().getUnsealedSenderIp().getBucketSize());
assertThat(limiter.getLeakRatePerMinute()).isEqualTo(dynamicConfig.getConfiguration().getLimits().getUnsealedSenderIp().getLeakRatePerMinute());
assertSame(rateLimiters.getUnsealedIpLimiter(), limiter);
}
@Test
@@ -47,25 +50,27 @@ public class DynamicRateLimitsTest {
DynamicRateLimitsConfiguration limitsConfiguration = mock(DynamicRateLimitsConfiguration.class);
when(configuration.getLimits()).thenReturn(limitsConfiguration);
when(limitsConfiguration.getUnsealedSenderNumber()).thenReturn(new RateLimitsConfiguration.RateLimitConfiguration(1, 2.0));
when(limitsConfiguration.getUnsealedSenderNumber()).thenReturn(new RateLimitsConfiguration.CardinalityRateLimitConfiguration(10, Duration.ofHours(1), Duration.ofMinutes(10)));
when(limitsConfiguration.getUnsealedSenderIp()).thenReturn(new RateLimitsConfiguration.RateLimitConfiguration(4, 1.0));
when(dynamicConfig.getConfiguration()).thenReturn(configuration);
RateLimiters rateLimiters = new RateLimiters(new RateLimitsConfiguration(), dynamicConfig, redisCluster);
RateLimiter limiter = rateLimiters.getUnsealedSenderLimiter();
CardinalityRateLimiter limiter = rateLimiters.getUnsealedSenderLimiter();
assertThat(limiter.getBucketSize()).isEqualTo(1);
assertThat(limiter.getLeakRatePerMinute()).isEqualTo(2.0);
assertThat(limiter.getMaxCardinality()).isEqualTo(10);
assertThat(limiter.getTtl()).isEqualTo(Duration.ofHours(1));
assertThat(limiter.getTtlJitter()).isEqualTo(Duration.ofMinutes(10));
assertSame(rateLimiters.getUnsealedSenderLimiter(), limiter);
when(limitsConfiguration.getUnsealedSenderNumber()).thenReturn(new RateLimitsConfiguration.RateLimitConfiguration(2, 3.0));
when(limitsConfiguration.getUnsealedSenderNumber()).thenReturn(new RateLimitsConfiguration.CardinalityRateLimitConfiguration(20, Duration.ofHours(2), Duration.ofMinutes(7)));
RateLimiter changed = rateLimiters.getUnsealedSenderLimiter();
CardinalityRateLimiter changed = rateLimiters.getUnsealedSenderLimiter();
assertThat(changed.getBucketSize()).isEqualTo(2);
assertThat(changed.getLeakRatePerMinute()).isEqualTo(3.0);
assertThat(changed.getMaxCardinality()).isEqualTo(20);
assertThat(changed.getTtl()).isEqualTo(Duration.ofHours(2));
assertThat(changed.getTtlJitter()).isEqualTo(Duration.ofMinutes(7));
assertNotSame(limiter, changed);
}