Skip to content

Commit ec425e4

Browse files
committed
Introduce paritionByRequest and bypassLimitByPredicate functions
1 parent 5c86608 commit ec425e4

File tree

3 files changed

+84
-0
lines changed

3 files changed

+84
-0
lines changed

concurrency-limits-servlet-jakarta/src/main/java/com/netflix/concurrency/limits/servlet/jakarta/ServletLimiterBuilder.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ public ServletLimiterBuilder partitionByParameter(String name) {
6464
return partitionResolver(request -> Optional.ofNullable(request.getParameter(name)).orElse(null));
6565
}
6666

67+
/**
68+
* Partition the limit by the request instance. Percentages of the limit are partitioned to named
69+
* groups. Group membership is derived from the provided mapping function.
70+
* @param requestToGroup Mapping function from request to a named group.
71+
* @return Chainable builder
72+
*/
73+
public ServletLimiterBuilder partitionByRequest(Function<HttpServletRequest, String> requestToGroup) {
74+
return partitionResolver(request -> Optional.ofNullable(request).map(requestToGroup).orElse(null));
75+
}
76+
6777
/**
6878
* Partition the limit by the full path. Percentages of the limit are partitioned to named
6979
* groups. Group membership is derived from the provided mapping function.
@@ -142,6 +152,16 @@ public ServletLimiterBuilder bypassLimitByMethod(String method) {
142152
return bypassLimitResolver((context) -> method.equals(context.getMethod()));
143153
}
144154

155+
/**
156+
* Bypass limit if the predicate function returns true.
157+
* @param predicate The predicate function to which {@link HttpServletRequest } instance is passed.
158+
* If the predicate return true, the limit will be bypassed.
159+
* @return Chainable builder
160+
*/
161+
public ServletLimiterBuilder bypassLimitByPredicate(Function<HttpServletRequest, Boolean> predicate) {
162+
return bypassLimitResolver((context) -> predicate.apply(context));
163+
}
164+
145165
@Override
146166
protected ServletLimiterBuilder self() {
147167
return this;

concurrency-limits-servlet-jakarta/src/test/java/com/netflix/concurrency/limits/ConcurrencyLimitServletFilterTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public void beforeEachTest() {
4040
limiter = Mockito.spy(new ServletLimiterBuilder()
4141
.bypassLimitByMethod("GET")
4242
.bypassLimitByPathInfo("/admin/health")
43+
.bypassLimitByPredicate(ctx -> ctx.getMethod().equals("PATCH"))
4344
.named(testName.getMethodName())
4445
.metricRegistry(spectatorMetricRegistry)
4546
.build());
@@ -130,6 +131,24 @@ public void testDoFilterBypassCheckPassedForPath() throws ServletException, IOEx
130131
verifyCounts(0, 0, 0, 0, 1);
131132
}
132133

134+
@Test
135+
public void testDoFilterBypassCheckPassedForPredicate() throws ServletException, IOException {
136+
137+
ConcurrencyLimitServletFilter filter = new ConcurrencyLimitServletFilter(limiter);
138+
139+
MockHttpServletRequest request = new MockHttpServletRequest();
140+
request.setMethod("PATCH");
141+
request.setPathInfo("/admin/patch");
142+
MockHttpServletResponse response = new MockHttpServletResponse();
143+
MockFilterChain filterChain = new MockFilterChain();
144+
145+
filter.doFilter(request, response, filterChain);
146+
147+
assertEquals("Request should be passed to the downstream chain", request, filterChain.getRequest());
148+
assertEquals("Response should be passed to the downstream chain", response, filterChain.getResponse());
149+
verifyCounts(0, 0, 0, 0, 1);
150+
}
151+
133152
@Test
134153
public void testDoFilterBypassCheckFailed() throws ServletException, IOException {
135154

concurrency-limits-servlet-jakarta/src/test/java/com/netflix/concurrency/limits/GroupServletLimiterTest.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,44 @@ public void nullPathDoesNotMatchesGroup() {
154154
Mockito.verify(pathToGroup, Mockito.times(0)).get(Mockito.<String>any());
155155
}
156156

157+
@Test
158+
public void requestMatchesGroup() {
159+
Map<String, String> requestMethodToGroup = Mockito.spy(new HashMap<>());
160+
requestMethodToGroup.put("PATCH", "live");
161+
162+
Limiter<HttpServletRequest> limiter = new ServletLimiterBuilder()
163+
.limit(VegasLimit.newDefault())
164+
.partitionByRequest(request -> requestMethodToGroup.get(request.getMethod()))
165+
.partition("live", 0.8)
166+
.partition("batch", 0.2)
167+
.build();
168+
169+
HttpServletRequest request = createMockRequestWithType("PATCH");
170+
Optional<Listener> listener = limiter.acquire(request);
171+
172+
Assert.assertTrue(listener.isPresent());
173+
Mockito.verify(requestMethodToGroup, Mockito.times(1)).get("PATCH");
174+
}
175+
176+
@Test
177+
public void requestDoesNotMatchesGroup() {
178+
Map<String, String> requestMethodToGroup = Mockito.spy(new HashMap<>());
179+
requestMethodToGroup.put("PATCH", "live");
180+
181+
Limiter<HttpServletRequest> limiter = new ServletLimiterBuilder()
182+
.limit(VegasLimit.newDefault())
183+
.partitionByRequest(request -> requestMethodToGroup.get(request.getMethod()))
184+
.partition("live", 0.8)
185+
.partition("batch", 0.2)
186+
.build();
187+
188+
HttpServletRequest request = createMockRequestWithType("PUT");
189+
Optional<Listener> listener = limiter.acquire(request);
190+
191+
Assert.assertTrue(listener.isPresent());
192+
Mockito.verify(requestMethodToGroup, Mockito.times(1)).get("PUT");
193+
}
194+
157195
private HttpServletRequest createMockRequestWithPrincipal(String name) {
158196
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
159197
Principal principal = Mockito.mock(Principal.class);
@@ -169,4 +207,11 @@ private HttpServletRequest createMockRequestWithPathInfo(String name) {
169207
Mockito.when(request.getPathInfo()).thenReturn(name);
170208
return request;
171209
}
210+
211+
private HttpServletRequest createMockRequestWithType(String type) {
212+
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
213+
214+
Mockito.when(request.getMethod()).thenReturn(type);
215+
return request;
216+
}
172217
}

0 commit comments

Comments
 (0)