Add rate limiting for HTTP requests
The external REST APIs made from this lib module are rate limited to
8 per second. This can be changed with a config if needed.
Change-Id: I027e6e314f2b6591f940f319bc7c705376478db8
diff --git a/README.md b/README.md
index 697e66c..df14343 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,9 @@
fetch account details using /accounts/{account-id}/detail and
/accounts/{account-id}/external.ids REST APIs.
+The remote REST APIs fired from this lib module are rate limited to
+8 per second. This can be changed with a config if needed.
+
## Test scenarios:
#### Add new email
@@ -44,6 +47,10 @@
The entries in the accounts cache must be evicted after maxAge
duration reaches.
+#### API throttling
+The remote REST APIs must be throttled based on the requestsPerSecond
+gerrit setting.
+
# How to build
@@ -114,6 +121,17 @@
httpPassword = ***
```
+### remote-gerrit-account-cache.requestsPerSecond
+
+The maximum rate at which the remote REST APIs are fired.
+By default, set to 8 per second.
+
+Example:
+```
+[remote-gerrit-account-cache]
+ requestsPerSecond = 10
+```
+
## Section cache
### cache.accounts.maxAge
diff --git a/src/main/java/com/googlesource/gerrit/plugins/remotegerritaccountcache/APIRateLimiter.java b/src/main/java/com/googlesource/gerrit/plugins/remotegerritaccountcache/APIRateLimiter.java
new file mode 100644
index 0000000..a388d98
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/remotegerritaccountcache/APIRateLimiter.java
@@ -0,0 +1,42 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.remotegerritaccountcache;
+
+import static com.googlesource.gerrit.plugins.remotegerritaccountcache.AccountCacheImpl.Config.SECTION;
+
+import com.google.common.util.concurrent.RateLimiter;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.Optional;
+import org.eclipse.jgit.lib.Config;
+
+@Singleton
+public class APIRateLimiter {
+ private final RateLimiter limiter;
+
+ @Inject
+ APIRateLimiter(@GerritServerConfig Config config) {
+ limiter =
+ RateLimiter.create(
+ Double.parseDouble(
+ Optional.ofNullable(config.getString(SECTION, null, "requestsPerSecond"))
+ .orElse("8.0")));
+ }
+
+ public RateLimiter getLimiter() {
+ return limiter;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/remotegerritaccountcache/AccountCacheImpl.java b/src/main/java/com/googlesource/gerrit/plugins/remotegerritaccountcache/AccountCacheImpl.java
index a70e33a..03f0ee8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/remotegerritaccountcache/AccountCacheImpl.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/remotegerritaccountcache/AccountCacheImpl.java
@@ -245,6 +245,7 @@
private final GitRepositoryManager repoManager;
private final AllUsersName allUsersName;
private final HttpClient client;
+ private final APIRateLimiter rateLimiter;
@Inject
Loader(
@@ -256,7 +257,8 @@
Config config,
ExternalIdKeyFactory externalIdKeyFactory,
GitRepositoryManager repoManager,
- AllUsersName allUsersName) {
+ AllUsersName allUsersName,
+ APIRateLimiter rateLimiter) {
this.accountsUpdateFactory = accountsUpdateFactory;
this.accountIndexerProvider = accountIndexerProvider;
this.executor = executor;
@@ -265,6 +267,7 @@
this.externalIdKeyFactory = externalIdKeyFactory;
this.repoManager = repoManager;
this.allUsersName = allUsersName;
+ this.rateLimiter = rateLimiter;
this.client = getHttpClient();
}
@@ -336,6 +339,7 @@
}
protected AccountDetailInfo getAccountFromRemoteSite(Account.Id accountId) {
+ rateLimiter.getLimiter().acquire();
HttpRequest request =
HttpRequest.newBuilder().uri(config.getAccountDetailUri(accountId)).GET().build();
try {
@@ -354,6 +358,7 @@
public List<ExternalId> getExternalIdsFromRemoteSite(Account.Id accountId)
throws AccountNotFoundInRemoteGerritException {
+ rateLimiter.getLimiter().acquire();
HttpRequest request =
HttpRequest.newBuilder().uri(config.getExternalIdsUri(accountId)).GET().build();