1
0
mirror of https://github.com/google/nomulus synced 2025-12-23 14:25:44 +00:00

Add a rate limiter to remove all domain contacts action (#2838)

The maximum QPS defaults to 10, but can also be specified at runtime through
use of a query-string parameter.

BUG = http://b/439636188
This commit is contained in:
Ben McIlwain
2025-10-02 18:15:19 -04:00
committed by GitHub
parent 84884de77b
commit 9c5510f05d
3 changed files with 29 additions and 2 deletions

View File

@@ -29,9 +29,11 @@ import static google.registry.request.RequestParameters.extractRequiredParameter
import static google.registry.request.RequestParameters.extractSetOfDatetimeParameters; import static google.registry.request.RequestParameters.extractSetOfDatetimeParameters;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.RateLimiter;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import google.registry.request.Parameter; import google.registry.request.Parameter;
import jakarta.inject.Named;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import java.util.Optional; import java.util.Optional;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@@ -137,4 +139,18 @@ public class BatchModule {
static boolean provideIsFast(HttpServletRequest req) { static boolean provideIsFast(HttpServletRequest req) {
return extractBooleanParameter(req, PARAM_FAST); return extractBooleanParameter(req, PARAM_FAST);
} }
private static final int DEFAULT_MAX_QPS = 10;
@Provides
@Parameter("maxQps")
static int provideMaxQps(HttpServletRequest req) {
return extractOptionalIntParameter(req, "maxQps").orElse(DEFAULT_MAX_QPS);
}
@Provides
@Named("removeAllDomainContacts")
static RateLimiter provideRemoveAllDomainContactsRateLimiter(@Parameter("maxQps") int maxQps) {
return RateLimiter.create(maxQps);
}
} }

View File

@@ -30,6 +30,7 @@ import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.RateLimiter;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.flows.EppController; import google.registry.flows.EppController;
import google.registry.flows.EppRequestSource; import google.registry.flows.EppRequestSource;
@@ -48,6 +49,7 @@ import google.registry.request.Response;
import google.registry.request.auth.Auth; import google.registry.request.auth.Auth;
import google.registry.request.lock.LockHandler; import google.registry.request.lock.LockHandler;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.logging.Level; import java.util.logging.Level;
@@ -79,6 +81,7 @@ public class RemoveAllDomainContactsAction implements Runnable {
private final EppController eppController; private final EppController eppController;
private final String registryAdminClientId; private final String registryAdminClientId;
private final LockHandler lockHandler; private final LockHandler lockHandler;
private final RateLimiter rateLimiter;
private final Response response; private final Response response;
private final String updateDomainXml; private final String updateDomainXml;
private int successes = 0; private int successes = 0;
@@ -91,10 +94,12 @@ public class RemoveAllDomainContactsAction implements Runnable {
EppController eppController, EppController eppController,
@Config("registryAdminClientId") String registryAdminClientId, @Config("registryAdminClientId") String registryAdminClientId,
LockHandler lockHandler, LockHandler lockHandler,
@Named("removeAllDomainContacts") RateLimiter rateLimiter,
Response response) { Response response) {
this.eppController = eppController; this.eppController = eppController;
this.registryAdminClientId = registryAdminClientId; this.registryAdminClientId = registryAdminClientId;
this.lockHandler = lockHandler; this.lockHandler = lockHandler;
this.rateLimiter = rateLimiter;
this.response = response; this.response = response;
this.updateDomainXml = this.updateDomainXml =
readResourceUtf8(RemoveAllDomainContactsAction.class, "domain_remove_contacts.xml"); readResourceUtf8(RemoveAllDomainContactsAction.class, "domain_remove_contacts.xml");
@@ -146,7 +151,10 @@ public class RemoveAllDomainContactsAction implements Runnable {
.setMaxResults(BATCH_SIZE) .setMaxResults(BATCH_SIZE)
.getResultList()); .getResultList());
domainRepoIdsBatch.forEach(this::runDomainUpdateFlow); for (String domainRepoId : domainRepoIdsBatch) {
rateLimiter.acquire();
runDomainUpdateFlow(domainRepoId);
}
} while (!domainRepoIdsBatch.isEmpty()); } while (!domainRepoIdsBatch.isEmpty());
String msg = String msg =
String.format( String.format(

View File

@@ -23,9 +23,11 @@ import static google.registry.testing.DatabaseHelper.newDomain;
import static google.registry.testing.DatabaseHelper.persistActiveContact; import static google.registry.testing.DatabaseHelper.persistActiveContact;
import static google.registry.testing.DatabaseHelper.persistResource; import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.mockito.Mockito.mock;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import com.google.common.util.concurrent.RateLimiter;
import google.registry.flows.DaggerEppTestComponent; import google.registry.flows.DaggerEppTestComponent;
import google.registry.flows.EppController; import google.registry.flows.EppController;
import google.registry.flows.EppTestComponent.FakesAndMocksModule; import google.registry.flows.EppTestComponent.FakesAndMocksModule;
@@ -51,6 +53,7 @@ class RemoveAllDomainContactsActionTest {
new JpaTestExtensions.Builder().buildIntegrationTestExtension(); new JpaTestExtensions.Builder().buildIntegrationTestExtension();
private final FakeResponse response = new FakeResponse(); private final FakeResponse response = new FakeResponse();
private final RateLimiter rateLimiter = mock(RateLimiter.class);
private RemoveAllDomainContactsAction action; private RemoveAllDomainContactsAction action;
@BeforeEach @BeforeEach
@@ -69,7 +72,7 @@ class RemoveAllDomainContactsActionTest {
.eppController(); .eppController();
action = action =
new RemoveAllDomainContactsAction( new RemoveAllDomainContactsAction(
eppController, "NewRegistrar", new FakeLockHandler(true), response); eppController, "NewRegistrar", new FakeLockHandler(true), rateLimiter, response);
} }
@Test @Test