Migrate challenge-issuing rate limiters to the abusive message filter

This commit is contained in:
Jon Chambers
2021-11-18 15:39:46 -05:00
committed by Jon Chambers
parent 9628f147f1
commit 14cff958e9
26 changed files with 311 additions and 982 deletions

View File

@@ -5,27 +5,23 @@
package org.whispersystems.textsecuregcm.limits;
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration.CardinalityRateLimitConfiguration;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration.RateLimitConfiguration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
public class DynamicRateLimiters {
private final FaultTolerantRedisCluster cacheCluster;
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
private final AtomicReference<CardinalityRateLimiter> unsealedSenderCardinalityLimiter;
private final AtomicReference<RateLimiter> unsealedIpLimiter;
private final AtomicReference<RateLimiter> rateLimitResetLimiter;
private final AtomicReference<RateLimiter> recaptchaChallengeAttemptLimiter;
private final AtomicReference<RateLimiter> recaptchaChallengeSuccessLimiter;
private final AtomicReference<RateLimiter> pushChallengeAttemptLimiter;
private final AtomicReference<RateLimiter> pushChallengeSuccessLimiter;
private final AtomicReference<RateLimiter> dailyPreKeysLimiter;
public DynamicRateLimiters(final FaultTolerantRedisCluster rateLimitCluster,
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
@@ -33,18 +29,6 @@ public class DynamicRateLimiters {
this.cacheCluster = rateLimitCluster;
this.dynamicConfigurationManager = dynamicConfigurationManager;
this.dailyPreKeysLimiter = new AtomicReference<>(
createDailyPreKeysLimiter(this.cacheCluster,
this.dynamicConfigurationManager.getConfiguration().getLimits().getDailyPreKeys()));
this.unsealedSenderCardinalityLimiter = new AtomicReference<>(createUnsealedSenderCardinalityLimiter(
this.cacheCluster,
this.dynamicConfigurationManager.getConfiguration().getLimits().getUnsealedSenderNumber()));
this.unsealedIpLimiter = new AtomicReference<>(
createUnsealedIpLimiter(this.cacheCluster,
this.dynamicConfigurationManager.getConfiguration().getLimits().getUnsealedSenderIp()));
this.rateLimitResetLimiter = new AtomicReference<>(
createRateLimitResetLimiter(this.cacheCluster,
this.dynamicConfigurationManager.getConfiguration().getLimits().getRateLimitReset()));
@@ -64,26 +48,6 @@ public class DynamicRateLimiters {
this.dynamicConfigurationManager.getConfiguration().getLimits().getPushChallengeSuccess()));
}
public CardinalityRateLimiter getUnsealedSenderCardinalityLimiter() {
CardinalityRateLimitConfiguration currentConfiguration = dynamicConfigurationManager.getConfiguration().getLimits()
.getUnsealedSenderNumber();
return this.unsealedSenderCardinalityLimiter.updateAndGet(rateLimiter -> {
if (rateLimiter.hasConfiguration(currentConfiguration)) {
return rateLimiter;
} else {
return createUnsealedSenderCardinalityLimiter(cacheCluster, currentConfiguration);
}
});
}
public RateLimiter getUnsealedIpLimiter() {
return updateAndGetRateLimiter(
unsealedIpLimiter,
dynamicConfigurationManager.getConfiguration().getLimits().getUnsealedSenderIp(),
this::createUnsealedIpLimiter);
}
public RateLimiter getRateLimitResetLimiter() {
return updateAndGetRateLimiter(
rateLimitResetLimiter,
@@ -119,13 +83,6 @@ public class DynamicRateLimiters {
this::createPushChallengeSuccessLimiter);
}
public RateLimiter getDailyPreKeysLimiter() {
return updateAndGetRateLimiter(
dailyPreKeysLimiter,
dynamicConfigurationManager.getConfiguration().getLimits().getDailyPreKeys(),
this::createDailyPreKeysLimiter);
}
private RateLimiter updateAndGetRateLimiter(final AtomicReference<RateLimiter> rateLimiter,
RateLimitConfiguration currentConfiguration,
BiFunction<FaultTolerantRedisCluster, RateLimitConfiguration, RateLimiter> rateLimitFactory) {
@@ -139,17 +96,6 @@ public class DynamicRateLimiters {
});
}
private CardinalityRateLimiter createUnsealedSenderCardinalityLimiter(FaultTolerantRedisCluster cacheCluster,
CardinalityRateLimitConfiguration configuration) {
return new CardinalityRateLimiter(cacheCluster, "unsealedSender", configuration.getTtl(),
configuration.getMaxCardinality());
}
private RateLimiter createUnsealedIpLimiter(FaultTolerantRedisCluster cacheCluster,
RateLimitConfiguration configuration) {
return createLimiter(cacheCluster, configuration, "unsealedIp");
}
public RateLimiter createRateLimitResetLimiter(FaultTolerantRedisCluster cacheCluster,
RateLimitConfiguration configuration) {
return createLimiter(cacheCluster, configuration, "rateLimitReset");
@@ -175,11 +121,6 @@ public class DynamicRateLimiters {
return createLimiter(cacheCluster, configuration, "pushChallengeSuccess");
}
public RateLimiter createDailyPreKeysLimiter(FaultTolerantRedisCluster cacheCluster,
RateLimitConfiguration configuration) {
return createLimiter(cacheCluster, configuration, "dailyPreKeys");
}
private RateLimiter createLimiter(FaultTolerantRedisCluster cacheCluster, RateLimitConfiguration configuration,
String name) {
return new RateLimiter(cacheCluster, name,

View File

@@ -1,79 +0,0 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.limits;
import static com.codahale.metrics.MetricRegistry.name;
import io.dropwizard.util.Duration;
import io.micrometer.core.instrument.Metrics;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
import org.whispersystems.textsecuregcm.util.Util;
public class PreKeyRateLimiter {
private static final String RATE_LIMIT_RESET_COUNTER_NAME = name(PreKeyRateLimiter.class, "reset");
private static final String RATE_LIMITED_PREKEYS_COUNTER_NAME = name(PreKeyRateLimiter.class, "rateLimited");
private static final String RATE_LIMITED_PREKEYS_TOTAL_ACCOUNTS_COUNTER_NAME = name(PreKeyRateLimiter.class, "rateLimitedTotal");
private static final String RATE_LIMITED_PREKEYS_ACCOUNTS_ENFORCED_COUNTER_NAME = name(PreKeyRateLimiter.class, "rateLimitedAccountsEnforced");
private static final String RATE_LIMITED_PREKEYS_ACCOUNTS_UNENFORCED_COUNTER_NAME = name(PreKeyRateLimiter.class, "rateLimitedAccountsUnenforced");
private static final String RATE_LIMITED_ACCOUNTS_HLL_KEY = "PreKeyRateLimiter::rateLimitedAccounts";
private static final String RATE_LIMITED_ACCOUNTS_ENFORCED_HLL_KEY = "PreKeyRateLimiter::rateLimitedAccounts::enforced";
private static final String RATE_LIMITED_ACCOUNTS_UNENFORCED_HLL_KEY = "PreKeyRateLimiter::rateLimitedAccounts::unenforced";
private static final long RATE_LIMITED_ACCOUNTS_HLL_TTL_SECONDS = Duration.days(1).toSeconds();
private final DynamicRateLimiters rateLimiters;
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
private final RateLimitResetMetricsManager metricsManager;
public PreKeyRateLimiter(final DynamicRateLimiters rateLimiters,
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager,
final RateLimitResetMetricsManager metricsManager) {
this.rateLimiters = rateLimiters;
this.dynamicConfigurationManager = dynamicConfigurationManager;
this.metricsManager = metricsManager;
metricsManager.initializeFunctionCounters(RATE_LIMITED_PREKEYS_TOTAL_ACCOUNTS_COUNTER_NAME,
RATE_LIMITED_ACCOUNTS_HLL_KEY);
metricsManager.initializeFunctionCounters(RATE_LIMITED_PREKEYS_ACCOUNTS_ENFORCED_COUNTER_NAME,
RATE_LIMITED_ACCOUNTS_ENFORCED_HLL_KEY);
metricsManager.initializeFunctionCounters(RATE_LIMITED_PREKEYS_ACCOUNTS_UNENFORCED_COUNTER_NAME,
RATE_LIMITED_ACCOUNTS_UNENFORCED_HLL_KEY);
}
public void validate(final Account account) throws RateLimitExceededException {
try {
rateLimiters.getDailyPreKeysLimiter().validate(account.getUuid());
} catch (final RateLimitExceededException e) {
final boolean enforceLimit = dynamicConfigurationManager.getConfiguration()
.getRateLimitChallengeConfiguration().isPreKeyLimitEnforced();
metricsManager.recordMetrics(account, enforceLimit,
RATE_LIMITED_PREKEYS_COUNTER_NAME,
enforceLimit ? RATE_LIMITED_ACCOUNTS_ENFORCED_HLL_KEY : RATE_LIMITED_ACCOUNTS_UNENFORCED_HLL_KEY,
RATE_LIMITED_ACCOUNTS_HLL_KEY,
RATE_LIMITED_ACCOUNTS_HLL_TTL_SECONDS
);
if (enforceLimit) {
throw e;
}
}
}
public void handleRateLimitReset(final Account account) {
rateLimiters.getDailyPreKeysLimiter().clear(account.getUuid());
Metrics.counter(RATE_LIMIT_RESET_COUNTER_NAME, "countryCode", Util.getCountryCode(account.getNumber()))
.increment();
}
}

View File

@@ -2,35 +2,26 @@ package org.whispersystems.textsecuregcm.limits;
import static com.codahale.metrics.MetricRegistry.name;
import com.vdurmont.semver4j.Semver;
import io.micrometer.core.instrument.Metrics;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.abuse.RateLimitChallengeListener;
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
import org.whispersystems.textsecuregcm.push.NotPushRegisteredException;
import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
import org.whispersystems.textsecuregcm.util.Util;
import org.whispersystems.textsecuregcm.util.ua.UnrecognizedUserAgentException;
import org.whispersystems.textsecuregcm.util.ua.UserAgent;
import org.whispersystems.textsecuregcm.util.ua.UserAgentUtil;
public class RateLimitChallengeManager {
private final PushChallengeManager pushChallengeManager;
private final RecaptchaClient recaptchaClient;
private final PreKeyRateLimiter preKeyRateLimiter;
private final UnsealedSenderRateLimiter unsealedSenderRateLimiter;
private final DynamicRateLimiters rateLimiters;
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
public static final String OPTION_RECAPTCHA = "recaptcha";
public static final String OPTION_PUSH_CHALLENGE = "pushChallenge";
private final List<RateLimitChallengeListener> rateLimitChallengeListeners =
Collections.synchronizedList(new ArrayList<>());
private static final String RECAPTCHA_ATTEMPT_COUNTER_NAME = name(RateLimitChallengeManager.class, "recaptcha", "attempt");
private static final String RESET_RATE_LIMIT_EXCEEDED_COUNTER_NAME = name(RateLimitChallengeManager.class, "resetRateLimitExceeded");
@@ -41,17 +32,15 @@ public class RateLimitChallengeManager {
public RateLimitChallengeManager(
final PushChallengeManager pushChallengeManager,
final RecaptchaClient recaptchaClient,
final PreKeyRateLimiter preKeyRateLimiter,
final UnsealedSenderRateLimiter unsealedSenderRateLimiter,
final DynamicRateLimiters rateLimiters,
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
final DynamicRateLimiters rateLimiters) {
this.pushChallengeManager = pushChallengeManager;
this.recaptchaClient = recaptchaClient;
this.preKeyRateLimiter = preKeyRateLimiter;
this.unsealedSenderRateLimiter = unsealedSenderRateLimiter;
this.rateLimiters = rateLimiters;
this.dynamicConfigurationManager = dynamicConfigurationManager;
}
public void addListener(final RateLimitChallengeListener rateLimitChallengeListener) {
rateLimitChallengeListeners.add(rateLimitChallengeListener);
}
public void answerPushChallenge(final Account account, final String challenge) throws RateLimitExceededException {
@@ -92,40 +81,7 @@ public class RateLimitChallengeManager {
throw e;
}
preKeyRateLimiter.handleRateLimitReset(account);
unsealedSenderRateLimiter.handleRateLimitReset(account);
}
public boolean isClientBelowMinimumVersion(final String userAgent) {
try {
final UserAgent client = UserAgentUtil.parseUserAgentString(userAgent);
final Optional<Semver> minimumClientVersion = dynamicConfigurationManager.getConfiguration()
.getRateLimitChallengeConfiguration()
.getMinimumSupportedVersion(client.getPlatform());
return minimumClientVersion.map(version -> version.isGreaterThan(client.getVersion()))
.orElse(true);
} catch (final UnrecognizedUserAgentException ignored) {
return false;
}
}
public List<String> getChallengeOptions(final Account account) {
final List<String> options = new ArrayList<>(2);
if (rateLimiters.getRecaptchaChallengeAttemptLimiter().hasAvailablePermits(account.getUuid(), 1) &&
rateLimiters.getRecaptchaChallengeSuccessLimiter().hasAvailablePermits(account.getUuid(), 1)) {
options.add(OPTION_RECAPTCHA);
}
if (rateLimiters.getPushChallengeAttemptLimiter().hasAvailablePermits(account.getUuid(), 1) &&
rateLimiters.getPushChallengeSuccessLimiter().hasAvailablePermits(account.getUuid(), 1)) {
options.add(OPTION_PUSH_CHALLENGE);
}
return options;
rateLimitChallengeListeners.forEach(listener -> listener.handleRateLimitChallengeAnswered(account));
}
public void sendPushChallenge(final Account account) throws NotPushRegisteredException {

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.limits;
import com.vdurmont.semver4j.Semver;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
import org.whispersystems.textsecuregcm.util.ua.UnrecognizedUserAgentException;
import org.whispersystems.textsecuregcm.util.ua.UserAgent;
import org.whispersystems.textsecuregcm.util.ua.UserAgentUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class RateLimitChallengeOptionManager {
private final DynamicRateLimiters rateLimiters;
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
public static final String OPTION_RECAPTCHA = "recaptcha";
public static final String OPTION_PUSH_CHALLENGE = "pushChallenge";
public RateLimitChallengeOptionManager(final DynamicRateLimiters rateLimiters,
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager) {
this.rateLimiters = rateLimiters;
this.dynamicConfigurationManager = dynamicConfigurationManager;
}
public boolean isClientBelowMinimumVersion(final String userAgent) {
try {
final UserAgent client = UserAgentUtil.parseUserAgentString(userAgent);
final Optional<Semver> minimumClientVersion = dynamicConfigurationManager.getConfiguration()
.getRateLimitChallengeConfiguration()
.getMinimumSupportedVersion(client.getPlatform());
return minimumClientVersion.map(version -> version.isGreaterThan(client.getVersion()))
.orElse(true);
} catch (final UnrecognizedUserAgentException ignored) {
return false;
}
}
public List<String> getChallengeOptions(final Account account) {
final List<String> options = new ArrayList<>(2);
if (rateLimiters.getRecaptchaChallengeAttemptLimiter().hasAvailablePermits(account.getUuid(), 1) &&
rateLimiters.getRecaptchaChallengeSuccessLimiter().hasAvailablePermits(account.getUuid(), 1)) {
options.add(OPTION_RECAPTCHA);
}
if (rateLimiters.getPushChallengeAttemptLimiter().hasAvailablePermits(account.getUuid(), 1) &&
rateLimiters.getPushChallengeSuccessLimiter().hasAvailablePermits(account.getUuid(), 1)) {
options.add(OPTION_PUSH_CHALLENGE);
}
return options;
}
}

View File

@@ -1,45 +0,0 @@
package org.whispersystems.textsecuregcm.limits;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.FunctionCounter;
import io.micrometer.core.instrument.MeterRegistry;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.storage.Account;
public class RateLimitResetMetricsManager {
private final FaultTolerantRedisCluster metricsCluster;
private final MeterRegistry meterRegistry;
public RateLimitResetMetricsManager(
final FaultTolerantRedisCluster metricsCluster, final MeterRegistry meterRegistry) {
this.metricsCluster = metricsCluster;
this.meterRegistry = meterRegistry;
}
void initializeFunctionCounters(String counterKey, String hllKey) {
FunctionCounter
.builder(counterKey, this, manager -> manager.getCount(hllKey))
.register(meterRegistry);
}
Long getCount(final String hllKey) {
return metricsCluster.<Long>withCluster(conn -> conn.sync().pfcount(hllKey));
}
void recordMetrics(Account account, boolean enforced, String counterKey, String hllEnforcedKey, String hllTotalKey,
long hllTtl) {
Counter.builder(counterKey)
.tag("enforced", String.valueOf(enforced))
.register(meterRegistry)
.increment();
metricsCluster.useCluster(connection -> {
connection.sync().pfadd(hllEnforcedKey, account.getUuid().toString());
connection.sync().expire(hllEnforcedKey, hllTtl);
connection.sync().pfadd(hllTotalKey, account.getUuid().toString());
connection.sync().expire(hllTotalKey, hllTtl);
});
}
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.limits;
import static com.codahale.metrics.MetricRegistry.name;
import io.dropwizard.util.Duration;
import io.lettuce.core.SetArgs;
import io.micrometer.core.instrument.Metrics;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicRateLimitsConfiguration;
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
import org.whispersystems.textsecuregcm.util.Util;
public class UnsealedSenderRateLimiter {
private final DynamicRateLimiters rateLimiters;
private final FaultTolerantRedisCluster rateLimitCluster;
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
private final RateLimitResetMetricsManager metricsManager;
private static final String RATE_LIMIT_RESET_COUNTER_NAME = name(UnsealedSenderRateLimiter.class, "reset");
private static final String RATE_LIMITED_UNSEALED_SENDER_COUNTER_NAME = name(UnsealedSenderRateLimiter.class, "rateLimited");
private static final String RATE_LIMITED_UNSEALED_SENDER_ACCOUNTS_TOTAL_COUNTER_NAME = name(UnsealedSenderRateLimiter.class, "rateLimitedAccountsTotal");
private static final String RATE_LIMITED_UNSEALED_SENDER_ACCOUNTS_ENFORCED_COUNTER_NAME = name(UnsealedSenderRateLimiter.class, "rateLimitedAccountsEnforced");
private static final String RATE_LIMITED_UNSEALED_SENDER_ACCOUNTS_UNENFORCED_COUNTER_NAME = name(UnsealedSenderRateLimiter.class, "rateLimitedAccountsUnenforced");
private static final String RATE_LIMITED_ACCOUNTS_HLL_KEY = "UnsealedSenderRateLimiter::rateLimitedAccounts::total";
private static final String RATE_LIMITED_ACCOUNTS_ENFORCED_HLL_KEY = "UnsealedSenderRateLimiter::rateLimitedAccounts::enforced";
private static final String RATE_LIMITED_ACCOUNTS_UNENFORCED_HLL_KEY = "UnsealedSenderRateLimiter::rateLimitedAccounts::unenforced";
private static final long RATE_LIMITED_ACCOUNTS_HLL_TTL_SECONDS = Duration.days(1).toSeconds();
public UnsealedSenderRateLimiter(final DynamicRateLimiters rateLimiters,
final FaultTolerantRedisCluster rateLimitCluster,
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager,
final RateLimitResetMetricsManager metricsManager) {
this.rateLimiters = rateLimiters;
this.rateLimitCluster = rateLimitCluster;
this.dynamicConfigurationManager = dynamicConfigurationManager;
this.metricsManager = metricsManager;
metricsManager.initializeFunctionCounters(RATE_LIMITED_UNSEALED_SENDER_ACCOUNTS_TOTAL_COUNTER_NAME,
RATE_LIMITED_ACCOUNTS_HLL_KEY);
metricsManager.initializeFunctionCounters(RATE_LIMITED_UNSEALED_SENDER_ACCOUNTS_ENFORCED_COUNTER_NAME,
RATE_LIMITED_ACCOUNTS_ENFORCED_HLL_KEY);
metricsManager.initializeFunctionCounters(RATE_LIMITED_UNSEALED_SENDER_ACCOUNTS_UNENFORCED_COUNTER_NAME,
RATE_LIMITED_ACCOUNTS_UNENFORCED_HLL_KEY);
}
public void validate(final Account sender, final Account destination) throws RateLimitExceededException {
final int maxCardinality = rateLimitCluster.withCluster(connection -> {
final String cardinalityString = connection.sync().get(getMaxCardinalityKey(sender));
return cardinalityString != null
? Integer.parseInt(cardinalityString)
: dynamicConfigurationManager.getConfiguration().getLimits().getUnsealedSenderDefaultCardinalityLimit();
});
try {
rateLimiters.getUnsealedSenderCardinalityLimiter()
.validate(sender.getUuid().toString(), destination.getUuid().toString(), maxCardinality);
} catch (final RateLimitExceededException e) {
final boolean enforceLimit = dynamicConfigurationManager.getConfiguration()
.getRateLimitChallengeConfiguration().isUnsealedSenderLimitEnforced();
metricsManager.recordMetrics(sender, enforceLimit, RATE_LIMITED_UNSEALED_SENDER_COUNTER_NAME,
enforceLimit ? RATE_LIMITED_ACCOUNTS_ENFORCED_HLL_KEY : RATE_LIMITED_ACCOUNTS_UNENFORCED_HLL_KEY,
RATE_LIMITED_ACCOUNTS_HLL_KEY,
RATE_LIMITED_ACCOUNTS_HLL_TTL_SECONDS
);
if (enforceLimit) {
throw e;
}
}
}
public void handleRateLimitReset(final Account account) {
rateLimitCluster.useCluster(connection -> {
final CardinalityRateLimiter unsealedSenderCardinalityLimiter = rateLimiters.getUnsealedSenderCardinalityLimiter();
final DynamicRateLimitsConfiguration rateLimitsConfiguration =
dynamicConfigurationManager.getConfiguration().getLimits();
final long ttl;
{
final long remainingTtl = unsealedSenderCardinalityLimiter.getRemainingTtl(account.getUuid().toString());
ttl = remainingTtl > 0 ? remainingTtl : unsealedSenderCardinalityLimiter.getInitialTtl().toSeconds();
}
final String key = getMaxCardinalityKey(account);
connection.sync().set(key,
String.valueOf(rateLimitsConfiguration.getUnsealedSenderDefaultCardinalityLimit()),
SetArgs.Builder.nx().ex(ttl));
connection.sync().incrby(key, rateLimitsConfiguration.getUnsealedSenderPermitIncrement());
});
Metrics.counter(RATE_LIMIT_RESET_COUNTER_NAME,
"countryCode", Util.getCountryCode(account.getNumber())).increment();
}
private static String getMaxCardinalityKey(final Account account) {
return "max_unsealed_sender_cardinality::" + account.getUuid();
}
}