madrob commented on a change in pull request #1686:
URL: https://github.com/apache/lucene-solr/pull/1686#discussion_r466588870
##########
File path:
solr/core/src/test/org/apache/solr/servlet/TestRequestRateLimiter.java
##########
@@ -102,31 +103,101 @@ public Boolean call() throws Exception {
try {
future.get();
} catch (Exception e) {
- assertTrue("Not true " + e.getMessage(),
e.getMessage().contains("non ok status: 429, message:Too Many Requests"));
+ assertThat(e.getMessage(), containsString("non ok status: 429,
message:Too Many Requests"));
}
}
MockRequestRateLimiter mockQueryRateLimiter = (MockRequestRateLimiter)
rateLimitManager.getRequestRateLimiter(SolrRequest.SolrRequestType.QUERY);
- assertTrue("Incoming request count did not match. Expected == 25
incoming " + mockQueryRateLimiter.incomingRequestCount.get(),
- mockQueryRateLimiter.incomingRequestCount.get() == 25);
+ assertEquals(mockQueryRateLimiter.incomingRequestCount.get(),25);
Review comment:
nit: swap the parameters. assertEquals(expected, actual)
##########
File path: solr/core/src/java/org/apache/solr/servlet/RateLimitManager.java
##########
@@ -92,17 +100,28 @@ public boolean handleRequest(HttpServletRequest request)
throws InterruptedExcep
* For each request rate limiter whose type that is not of the type of the
request which got rejected,
* check if slot borrowing is enabled. If enabled, try to acquire a slot.
* If allotted, return else try next request type.
+ *
+ * @lucene.gexperimental -- Can cause slots to be blocked if a request
borrows a slot and is itself long lived.
Review comment:
s/gexperimental/experimental
##########
File path:
solr/core/src/test/org/apache/solr/servlet/TestRequestRateLimiter.java
##########
@@ -102,31 +103,101 @@ public Boolean call() throws Exception {
try {
future.get();
} catch (Exception e) {
- assertTrue("Not true " + e.getMessage(),
e.getMessage().contains("non ok status: 429, message:Too Many Requests"));
+ assertThat(e.getMessage(), containsString("non ok status: 429,
message:Too Many Requests"));
}
}
MockRequestRateLimiter mockQueryRateLimiter = (MockRequestRateLimiter)
rateLimitManager.getRequestRateLimiter(SolrRequest.SolrRequestType.QUERY);
- assertTrue("Incoming request count did not match. Expected == 25
incoming " + mockQueryRateLimiter.incomingRequestCount.get(),
- mockQueryRateLimiter.incomingRequestCount.get() == 25);
+ assertEquals(mockQueryRateLimiter.incomingRequestCount.get(),25);
assertTrue("Incoming accepted new request count did not match. Expected
5 incoming " + mockQueryRateLimiter.acceptedNewRequestCount.get(),
mockQueryRateLimiter.acceptedNewRequestCount.get() < 25);
assertTrue("Incoming rejected new request count did not match. Expected
20 incoming " + mockQueryRateLimiter.rejectedRequestCount.get(),
mockQueryRateLimiter.rejectedRequestCount.get() > 0);
- assertTrue("Incoming total processed requests count did not match.
Expected " + mockQueryRateLimiter.incomingRequestCount.get() + " incoming "
- + (mockQueryRateLimiter.acceptedNewRequestCount.get() +
mockQueryRateLimiter.rejectedRequestCount.get()),
- (mockQueryRateLimiter.acceptedNewRequestCount.get() +
mockQueryRateLimiter.rejectedRequestCount.get()) ==
mockQueryRateLimiter.incomingRequestCount.get());
+ assertEquals(mockQueryRateLimiter.acceptedNewRequestCount.get() +
mockQueryRateLimiter.rejectedRequestCount.get(),
Review comment:
(expected, actual)
##########
File path:
solr/core/src/test/org/apache/solr/servlet/TestRequestRateLimiter.java
##########
@@ -102,31 +103,101 @@ public Boolean call() throws Exception {
try {
future.get();
} catch (Exception e) {
- assertTrue("Not true " + e.getMessage(),
e.getMessage().contains("non ok status: 429, message:Too Many Requests"));
+ assertThat(e.getMessage(), containsString("non ok status: 429,
message:Too Many Requests"));
}
}
MockRequestRateLimiter mockQueryRateLimiter = (MockRequestRateLimiter)
rateLimitManager.getRequestRateLimiter(SolrRequest.SolrRequestType.QUERY);
- assertTrue("Incoming request count did not match. Expected == 25
incoming " + mockQueryRateLimiter.incomingRequestCount.get(),
- mockQueryRateLimiter.incomingRequestCount.get() == 25);
+ assertEquals(mockQueryRateLimiter.incomingRequestCount.get(),25);
assertTrue("Incoming accepted new request count did not match. Expected
5 incoming " + mockQueryRateLimiter.acceptedNewRequestCount.get(),
mockQueryRateLimiter.acceptedNewRequestCount.get() < 25);
assertTrue("Incoming rejected new request count did not match. Expected
20 incoming " + mockQueryRateLimiter.rejectedRequestCount.get(),
mockQueryRateLimiter.rejectedRequestCount.get() > 0);
- assertTrue("Incoming total processed requests count did not match.
Expected " + mockQueryRateLimiter.incomingRequestCount.get() + " incoming "
- + (mockQueryRateLimiter.acceptedNewRequestCount.get() +
mockQueryRateLimiter.rejectedRequestCount.get()),
- (mockQueryRateLimiter.acceptedNewRequestCount.get() +
mockQueryRateLimiter.rejectedRequestCount.get()) ==
mockQueryRateLimiter.incomingRequestCount.get());
+ assertEquals(mockQueryRateLimiter.acceptedNewRequestCount.get() +
mockQueryRateLimiter.rejectedRequestCount.get(),
+ mockQueryRateLimiter.incomingRequestCount.get());
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ @Test
+ public void testSlotBorrowing() throws Exception {
+ CloudSolrClient client = cluster.getSolrClient();
+ client.setDefaultCollection(SECOND_COLLECTION);
+
+ CollectionAdminRequest.createCollection(SECOND_COLLECTION, 1,
1).process(client);
+ cluster.waitForActiveCollection(SECOND_COLLECTION, 1, 1);
+
+
+ SolrDispatchFilter solrDispatchFilter =
cluster.getJettySolrRunner(0).getSolrDispatchFilter();
+
+ RequestRateLimiter.RateLimiterConfig queryRateLimiterConfig = new
RequestRateLimiter.RateLimiterConfig(SolrRequest.SolrRequestType.QUERY,
+ true, 1, DEFAULT_SLOT_ACQUISITION_TIMEOUT_MS, 5 /* allowedRequests */,
true /* isSlotBorrowing */);
+ RequestRateLimiter.RateLimiterConfig indexRateLimiterConfig = new
RequestRateLimiter.RateLimiterConfig(SolrRequest.SolrRequestType.UPDATE,
+ true, 1, DEFAULT_SLOT_ACQUISITION_TIMEOUT_MS, 5 /* allowedRequests */,
true /* isSlotBorrowing */);
+ // We are fine with a null FilterConfig here since we ensure that
MockBuilder never invokes its parent
+ RateLimitManager.Builder builder = new MockBuilder(null /*dummy
FilterConfig */, new MockRequestRateLimiter(queryRateLimiterConfig, 5), new
MockRequestRateLimiter(indexRateLimiterConfig, 5));
+ RateLimitManager rateLimitManager = builder.build();
+
+ solrDispatchFilter.replaceRateLimitManager(rateLimitManager);
+
+ for (int i = 0; i < 100; i++) {
+ SolrInputDocument doc = new SolrInputDocument();
+
+ doc.setField("id", i);
+ doc.setField("text", "foo");
+ client.add(doc);
+ }
+
+ client.commit();
+
+ ExecutorService executor =
ExecutorUtil.newMDCAwareCachedThreadPool("threadpool");
+ List<Callable<Boolean>> callableList = new ArrayList<>();
+ List<Future<Boolean>> futures;
+
+ try {
Review comment:
given that this pattern is repeated between the tests, can we pull it
into a separate method?
##########
File path: solr/core/src/java/org/apache/solr/servlet/RateLimitManager.java
##########
@@ -38,9 +41,14 @@
* rate limiting is being done for a specific request type.
*/
public class RateLimitManager {
+ private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
public final static int DEFAULT_CONCURRENT_REQUESTS=
(Runtime.getRuntime().availableProcessors()) * 3;
public final static long DEFAULT_SLOT_ACQUISITION_TIMEOUT_MS = -1;
private final Map<String, RequestRateLimiter> requestRateLimiterMap;
+
+ // IMPORTANT: The slot from the corresponding rate limiter should be
acquired before adding the request
+ // to this map. Subsequently, the request should be deleted from the map
before the slot is released.
private final Map<HttpServletRequest, RequestRateLimiter> activeRequestsMap;
Review comment:
maybe declare this as a ConcurrentMap just to really drive the point
home?
##########
File path: solr/core/src/java/org/apache/solr/servlet/RequestRateLimiter.java
##########
@@ -53,6 +53,8 @@ public boolean handleRequest() throws InterruptedException {
* Whether to allow another request type to borrow a slot from this request
rate limiter. Typically works fine
* if there is a relatively lesser load on this request rate limiter's type
compared to the others (think of skew).
* @return true if allow, false otherwise
+ *
+ * @lucene.experimental -- Can cause slots to be blocked if a request
borrows a slot and is itself long lived.
*/
public boolean allowSlotBorrowing() {
synchronized (this) {
Review comment:
Also need to restore interrupted status.
##########
File path: solr/core/src/java/org/apache/solr/servlet/RequestRateLimiter.java
##########
@@ -53,6 +53,8 @@ public boolean handleRequest() throws InterruptedException {
* Whether to allow another request type to borrow a slot from this request
rate limiter. Typically works fine
* if there is a relatively lesser load on this request rate limiter's type
compared to the others (think of skew).
* @return true if allow, false otherwise
+ *
+ * @lucene.experimental -- Can cause slots to be blocked if a request
borrows a slot and is itself long lived.
*/
public boolean allowSlotBorrowing() {
synchronized (this) {
Review comment:
This is suspicious, given that we don't synchronize on the instance when
trying to acquire permits directly (non-borrowed). I understand that the goal
is to prevent a race between checking that there are enough sufficient permits
and acquiring one of the extras... but the java docs for `available permits`
also suggests that it is for debugging or testing only, not a real concurrency
construct. The proper way to do this might be two semaphores - one for
dedicated slots and one for shared slots. Incoming requests for this limiter
can take from the shared slots first and then if those run out then they can go
to the reserved slots. Borrow requests can only go to the share slot semaphore.
Would need to track which pool we took from then, which makes this less ideal.
##########
File path:
solr/core/src/test/org/apache/solr/servlet/TestRequestRateLimiter.java
##########
@@ -102,31 +103,101 @@ public Boolean call() throws Exception {
try {
future.get();
} catch (Exception e) {
- assertTrue("Not true " + e.getMessage(),
e.getMessage().contains("non ok status: 429, message:Too Many Requests"));
+ assertThat(e.getMessage(), containsString("non ok status: 429,
message:Too Many Requests"));
}
}
MockRequestRateLimiter mockQueryRateLimiter = (MockRequestRateLimiter)
rateLimitManager.getRequestRateLimiter(SolrRequest.SolrRequestType.QUERY);
- assertTrue("Incoming request count did not match. Expected == 25
incoming " + mockQueryRateLimiter.incomingRequestCount.get(),
- mockQueryRateLimiter.incomingRequestCount.get() == 25);
+ assertEquals(mockQueryRateLimiter.incomingRequestCount.get(),25);
assertTrue("Incoming accepted new request count did not match. Expected
5 incoming " + mockQueryRateLimiter.acceptedNewRequestCount.get(),
mockQueryRateLimiter.acceptedNewRequestCount.get() < 25);
assertTrue("Incoming rejected new request count did not match. Expected
20 incoming " + mockQueryRateLimiter.rejectedRequestCount.get(),
mockQueryRateLimiter.rejectedRequestCount.get() > 0);
- assertTrue("Incoming total processed requests count did not match.
Expected " + mockQueryRateLimiter.incomingRequestCount.get() + " incoming "
- + (mockQueryRateLimiter.acceptedNewRequestCount.get() +
mockQueryRateLimiter.rejectedRequestCount.get()),
- (mockQueryRateLimiter.acceptedNewRequestCount.get() +
mockQueryRateLimiter.rejectedRequestCount.get()) ==
mockQueryRateLimiter.incomingRequestCount.get());
+ assertEquals(mockQueryRateLimiter.acceptedNewRequestCount.get() +
mockQueryRateLimiter.rejectedRequestCount.get(),
+ mockQueryRateLimiter.incomingRequestCount.get());
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ @Test
+ public void testSlotBorrowing() throws Exception {
+ CloudSolrClient client = cluster.getSolrClient();
+ client.setDefaultCollection(SECOND_COLLECTION);
+
+ CollectionAdminRequest.createCollection(SECOND_COLLECTION, 1,
1).process(client);
+ cluster.waitForActiveCollection(SECOND_COLLECTION, 1, 1);
+
+
+ SolrDispatchFilter solrDispatchFilter =
cluster.getJettySolrRunner(0).getSolrDispatchFilter();
+
+ RequestRateLimiter.RateLimiterConfig queryRateLimiterConfig = new
RequestRateLimiter.RateLimiterConfig(SolrRequest.SolrRequestType.QUERY,
+ true, 1, DEFAULT_SLOT_ACQUISITION_TIMEOUT_MS, 5 /* allowedRequests */,
true /* isSlotBorrowing */);
+ RequestRateLimiter.RateLimiterConfig indexRateLimiterConfig = new
RequestRateLimiter.RateLimiterConfig(SolrRequest.SolrRequestType.UPDATE,
+ true, 1, DEFAULT_SLOT_ACQUISITION_TIMEOUT_MS, 5 /* allowedRequests */,
true /* isSlotBorrowing */);
+ // We are fine with a null FilterConfig here since we ensure that
MockBuilder never invokes its parent
+ RateLimitManager.Builder builder = new MockBuilder(null /*dummy
FilterConfig */, new MockRequestRateLimiter(queryRateLimiterConfig, 5), new
MockRequestRateLimiter(indexRateLimiterConfig, 5));
+ RateLimitManager rateLimitManager = builder.build();
+
+ solrDispatchFilter.replaceRateLimitManager(rateLimitManager);
+
+ for (int i = 0; i < 100; i++) {
+ SolrInputDocument doc = new SolrInputDocument();
+
+ doc.setField("id", i);
+ doc.setField("text", "foo");
+ client.add(doc);
+ }
+
+ client.commit();
+
+ ExecutorService executor =
ExecutorUtil.newMDCAwareCachedThreadPool("threadpool");
+ List<Callable<Boolean>> callableList = new ArrayList<>();
+ List<Future<Boolean>> futures;
+
+ try {
+ for (int i = 0; i < 25; i++) {
+ callableList.add(() -> {
+ try {
+ QueryResponse response = client.query(new SolrQuery("*:*"));
+
+ if (response.getResults().getNumFound() > 0) {
+ assertEquals(100, response.getResults().getNumFound());
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e.getMessage());
+ }
+
+ return true;
+ });
+ }
+
+ futures = executor.invokeAll(callableList);
+
+ for (Future<?> future : futures) {
+ try {
+ future.get();
Review comment:
`assertTrue(future.get());` for when it doesn't throw an exception?
##########
File path:
solr/core/src/test/org/apache/solr/servlet/TestRequestRateLimiter.java
##########
@@ -102,31 +103,101 @@ public Boolean call() throws Exception {
try {
future.get();
} catch (Exception e) {
- assertTrue("Not true " + e.getMessage(),
e.getMessage().contains("non ok status: 429, message:Too Many Requests"));
+ assertThat(e.getMessage(), containsString("non ok status: 429,
message:Too Many Requests"));
}
}
MockRequestRateLimiter mockQueryRateLimiter = (MockRequestRateLimiter)
rateLimitManager.getRequestRateLimiter(SolrRequest.SolrRequestType.QUERY);
- assertTrue("Incoming request count did not match. Expected == 25
incoming " + mockQueryRateLimiter.incomingRequestCount.get(),
- mockQueryRateLimiter.incomingRequestCount.get() == 25);
+ assertEquals(mockQueryRateLimiter.incomingRequestCount.get(),25);
assertTrue("Incoming accepted new request count did not match. Expected
5 incoming " + mockQueryRateLimiter.acceptedNewRequestCount.get(),
mockQueryRateLimiter.acceptedNewRequestCount.get() < 25);
assertTrue("Incoming rejected new request count did not match. Expected
20 incoming " + mockQueryRateLimiter.rejectedRequestCount.get(),
mockQueryRateLimiter.rejectedRequestCount.get() > 0);
- assertTrue("Incoming total processed requests count did not match.
Expected " + mockQueryRateLimiter.incomingRequestCount.get() + " incoming "
- + (mockQueryRateLimiter.acceptedNewRequestCount.get() +
mockQueryRateLimiter.rejectedRequestCount.get()),
- (mockQueryRateLimiter.acceptedNewRequestCount.get() +
mockQueryRateLimiter.rejectedRequestCount.get()) ==
mockQueryRateLimiter.incomingRequestCount.get());
+ assertEquals(mockQueryRateLimiter.acceptedNewRequestCount.get() +
mockQueryRateLimiter.rejectedRequestCount.get(),
+ mockQueryRateLimiter.incomingRequestCount.get());
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ @Test
+ public void testSlotBorrowing() throws Exception {
+ CloudSolrClient client = cluster.getSolrClient();
+ client.setDefaultCollection(SECOND_COLLECTION);
+
+ CollectionAdminRequest.createCollection(SECOND_COLLECTION, 1,
1).process(client);
+ cluster.waitForActiveCollection(SECOND_COLLECTION, 1, 1);
+
+
+ SolrDispatchFilter solrDispatchFilter =
cluster.getJettySolrRunner(0).getSolrDispatchFilter();
+
+ RequestRateLimiter.RateLimiterConfig queryRateLimiterConfig = new
RequestRateLimiter.RateLimiterConfig(SolrRequest.SolrRequestType.QUERY,
+ true, 1, DEFAULT_SLOT_ACQUISITION_TIMEOUT_MS, 5 /* allowedRequests */,
true /* isSlotBorrowing */);
+ RequestRateLimiter.RateLimiterConfig indexRateLimiterConfig = new
RequestRateLimiter.RateLimiterConfig(SolrRequest.SolrRequestType.UPDATE,
+ true, 1, DEFAULT_SLOT_ACQUISITION_TIMEOUT_MS, 5 /* allowedRequests */,
true /* isSlotBorrowing */);
+ // We are fine with a null FilterConfig here since we ensure that
MockBuilder never invokes its parent
+ RateLimitManager.Builder builder = new MockBuilder(null /*dummy
FilterConfig */, new MockRequestRateLimiter(queryRateLimiterConfig, 5), new
MockRequestRateLimiter(indexRateLimiterConfig, 5));
+ RateLimitManager rateLimitManager = builder.build();
+
+ solrDispatchFilter.replaceRateLimitManager(rateLimitManager);
+
+ for (int i = 0; i < 100; i++) {
+ SolrInputDocument doc = new SolrInputDocument();
+
+ doc.setField("id", i);
+ doc.setField("text", "foo");
+ client.add(doc);
+ }
+
+ client.commit();
+
+ ExecutorService executor =
ExecutorUtil.newMDCAwareCachedThreadPool("threadpool");
+ List<Callable<Boolean>> callableList = new ArrayList<>();
+ List<Future<Boolean>> futures;
+
+ try {
+ for (int i = 0; i < 25; i++) {
+ callableList.add(() -> {
+ try {
+ QueryResponse response = client.query(new SolrQuery("*:*"));
+
+ if (response.getResults().getNumFound() > 0) {
+ assertEquals(100, response.getResults().getNumFound());
+ }
Review comment:
if numFound == 0, then as you said this means the request was throttled?
So we should be in the catch block at that point? I don't think we need the
conditional then, right?
##########
File path: solr/core/src/java/org/apache/solr/servlet/RateLimitManager.java
##########
@@ -92,17 +100,28 @@ public boolean handleRequest(HttpServletRequest request)
throws InterruptedExcep
* For each request rate limiter whose type that is not of the type of the
request which got rejected,
* check if slot borrowing is enabled. If enabled, try to acquire a slot.
* If allotted, return else try next request type.
+ *
+ * @lucene.gexperimental -- Can cause slots to be blocked if a request
borrows a slot and is itself long lived.
*/
private RequestRateLimiter trySlotBorrowing(String requestType) {
for (Map.Entry<String, RequestRateLimiter> currentEntry :
requestRateLimiterMap.entrySet()) {
RequestRateLimiter requestRateLimiter = currentEntry.getValue();
- if
(requestRateLimiter.getRateLimiterConfig().requestType.equals(requestType)) {
+ if
(requestRateLimiter.getRateLimiterConfig().requestType.toString().equals(requestType))
{
Review comment:
Add a comment something like // Can't borrow from ourselves
##########
File path:
solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java
##########
@@ -723,14 +716,35 @@ private HttpEntityEnclosingRequestBase fillContentStream(
}
}
- // When raising an error using HTTP sendError, mime types can be mismatched
+ // When raising an error using HTTP sendError, mime types can be mismatched.
This is specifically true when
+ // SolrDispatchFilter uses the sendError mechanism since the expected MIME
type of response is not HTML but
+ // HTTP sendError generates a HTML output, which can lead to mismatch
private boolean isUnmatchedErrorCode(String mimeType, int httpStatus) {
if (mimeType.equalsIgnoreCase("text/html") &&
UNMATCHED_ACCEPTED_ERROR_CODES.contains(httpStatus)) {
return true;
}
return false;
}
+
+ private Header[] buildRequestSpecificHeaders(@SuppressWarnings({"rawtypes"})
final SolrRequest request) {
+ Header[] contextHeaders = new Header[2];
+
+ contextHeaders[0] = new
BasicHeader(CommonParams.SOLR_REQUEST_CONTEXT_PARAM, getContext().toString());
+ contextHeaders[1] = new BasicHeader(CommonParams.SOLR_REQUEST_TYPE_PARAM,
request.getRequestType());
+
+ return contextHeaders;
+ }
+
+ private void populateHeadersInResult(HttpGet result, Header[]
contextHeaders) {
+ result.addHeader(contextHeaders[0]);
+ result.addHeader(contextHeaders[1]);
+ }
+
+ private void populateHeadersInRequestBase(HttpEntityEnclosingRequestBase
postOrPut, Header[] contextHeaders) {
+ postOrPut.addHeader(contextHeaders[0]);
+ postOrPut.addHeader(contextHeaders[1]);
+ }
Review comment:
combine these into `populateHeaders(HttpMessage, Header[])`
##########
File path: solr/core/src/java/org/apache/solr/servlet/RateLimitManager.java
##########
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.servlet;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.http.HttpServletRequest;
+import java.lang.invoke.MethodHandles;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.solr.client.solrj.SolrRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static
org.apache.solr.common.params.CommonParams.SOLR_REQUEST_CONTEXT_PARAM;
+import static
org.apache.solr.common.params.CommonParams.SOLR_REQUEST_TYPE_PARAM;
+
+/**
+ * This class is responsible for managing rate limiting per request type. Rate
limiters
+ * can be registered with this class against a corresponding type. There can
be only one
+ * rate limiter associated with a request type.
+ *
+ * The actual rate limiting and the limits should be implemented in the
corresponding RequestRateLimiter
+ * implementation. RateLimitManager is responsible for the orchestration but
not the specifics of how the
+ * rate limiting is being done for a specific request type.
+ */
+public class RateLimitManager {
+ private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public final static int DEFAULT_CONCURRENT_REQUESTS=
(Runtime.getRuntime().availableProcessors()) * 3;
+ public final static long DEFAULT_SLOT_ACQUISITION_TIMEOUT_MS = -1;
+ private final Map<String, RequestRateLimiter> requestRateLimiterMap;
+
+ // IMPORTANT: The slot from the corresponding rate limiter should be
acquired before adding the request
+ // to this map. Subsequently, the request should be deleted from the map
before the slot is released.
+ private final Map<HttpServletRequest, RequestRateLimiter> activeRequestsMap;
+
+ public RateLimitManager() {
+ this.requestRateLimiterMap = new HashMap<>();
+ this.activeRequestsMap = new ConcurrentHashMap<>();
+ }
+
+ // Handles an incoming request. The main orchestration code path, this
method will
+ // identify which (if any) rate limiter can handle this request. Internal
requests will not be
+ // rate limited
+ // Returns true if request is accepted for processing, false if it should be
rejected
+ public boolean handleRequest(HttpServletRequest request) throws
InterruptedException {
+ String requestContext = request.getHeader(SOLR_REQUEST_CONTEXT_PARAM);
Review comment:
Did we give up on using the authentication plugin info to determine if
this is an internal request or not?
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]