Skip to content

Commit 5c86608

Browse files
authored
Restore bytecode compatibility for limiter builders (#197)
* Restore bytecode compatibility for limiter builders * Make AbstractLimiter.bypassResolver and AbstractLimiter.Bulder::bypassLimitResolverInternal final
1 parent 7cd750a commit 5c86608

File tree

9 files changed

+86
-54
lines changed

9 files changed

+86
-54
lines changed

concurrency-limits-core/src/main/java/com/netflix/concurrency/limits/limiter/AbstractLimiter.java

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,34 +31,6 @@ public abstract class AbstractLimiter<ContextT> implements Limiter<ContextT> {
3131
public static final String ID_TAG = "id";
3232
public static final String STATUS_TAG = "status";
3333

34-
/**
35-
* Constructs a new builder with a list of bypass resolvers.
36-
* If the predicate condition in any of the resolver is satisfied,
37-
* the call is bypassed without increasing the limiter inflight count
38-
* and affecting the algorithm.
39-
*/
40-
public abstract static class BypassLimiterBuilder<BuilderT extends BypassLimiterBuilder<BuilderT, ContextT>, ContextT> extends Builder<BuilderT> {
41-
42-
private final Predicate<ContextT> ALWAYS_FALSE = (context) -> false;
43-
private Predicate<ContextT> bypassResolver = ALWAYS_FALSE;
44-
45-
/**
46-
* Add a chainable bypass resolver predicate from context. Multiple resolvers may be added and if any of the
47-
* predicate condition returns true the call is bypassed without increasing the limiter inflight count and
48-
* affecting the algorithm. Will not bypass any calls by default if no resolvers are added.
49-
* @param shouldBypass Predicate condition to bypass limit
50-
* @return Chainable builder
51-
*/
52-
public BuilderT bypassLimitResolver(Predicate<ContextT> shouldBypass) {
53-
if (this.bypassResolver == ALWAYS_FALSE) {
54-
this.bypassResolver = shouldBypass;
55-
} else {
56-
this.bypassResolver = bypassResolver.or(shouldBypass);
57-
}
58-
return self();
59-
}
60-
}
61-
6234
public abstract static class Builder<BuilderT extends Builder<BuilderT>> {
6335
private static final AtomicInteger idCounter = new AtomicInteger();
6436

@@ -68,6 +40,9 @@ public abstract static class Builder<BuilderT extends Builder<BuilderT>> {
6840
protected String name = "unnamed-" + idCounter.incrementAndGet();
6941
protected MetricRegistry registry = EmptyMetricRegistry.INSTANCE;
7042

43+
private final Predicate<Object> ALWAYS_FALSE = (context) -> false;
44+
private Predicate<Object> bypassResolver = ALWAYS_FALSE;
45+
7146
public BuilderT named(String name) {
7247
this.name = name;
7348
return self();
@@ -89,6 +64,26 @@ public BuilderT metricRegistry(MetricRegistry registry) {
8964
}
9065

9166
protected abstract BuilderT self();
67+
68+
/**
69+
* Add a chainable bypass resolver predicate from context. Multiple resolvers may be added and if any of the
70+
* predicate condition returns true the call is bypassed without increasing the limiter inflight count and
71+
* affecting the algorithm. Will not bypass any calls by default if no resolvers are added.
72+
*
73+
* Due to the builders not having access to the ContextT, it is the duty of subclasses to ensure that
74+
* implementations are type safe.
75+
*
76+
* @param shouldBypass Predicate condition to bypass limit
77+
* @return Chainable builder
78+
*/
79+
protected final BuilderT bypassLimitResolverInternal(Predicate<?> shouldBypass) {
80+
if (this.bypassResolver == ALWAYS_FALSE) {
81+
this.bypassResolver = (Predicate<Object>) shouldBypass;
82+
} else {
83+
this.bypassResolver = bypassResolver.or((Predicate<Object>) shouldBypass);
84+
}
85+
return self();
86+
}
9287
}
9388

9489
private final AtomicInteger inFlight = new AtomicInteger();
@@ -99,7 +94,7 @@ public BuilderT metricRegistry(MetricRegistry registry) {
9994
private final MetricRegistry.Counter ignoredCounter;
10095
private final MetricRegistry.Counter rejectedCounter;
10196
private final MetricRegistry.Counter bypassCounter;
102-
private Predicate<ContextT> bypassResolver = (context) -> false;
97+
private final Predicate<ContextT> bypassResolver;
10398

10499
private volatile int limit;
105100

@@ -108,9 +103,8 @@ protected AbstractLimiter(Builder<?> builder) {
108103
this.limitAlgorithm = builder.limit;
109104
this.limit = limitAlgorithm.getLimit();
110105
this.limitAlgorithm.notifyOnChange(this::onNewLimit);
111-
if (builder instanceof BypassLimiterBuilder) {
112-
this.bypassResolver = ((BypassLimiterBuilder) builder).bypassResolver;
113-
}
106+
this.bypassResolver = (Predicate<ContextT>) builder.bypassResolver;
107+
114108
builder.registry.gauge(MetricIds.LIMIT_NAME, this::getLimit);
115109
this.successCounter = builder.registry.counter(MetricIds.CALL_NAME, ID_TAG, builder.name, STATUS_TAG, "success");
116110
this.droppedCounter = builder.registry.counter(MetricIds.CALL_NAME, ID_TAG, builder.name, STATUS_TAG, "dropped");

concurrency-limits-core/src/main/java/com/netflix/concurrency/limits/limiter/AbstractPartitionedLimiter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public abstract class AbstractPartitionedLimiter<ContextT> extends AbstractLimit
3737
private static final Logger LOG = LoggerFactory.getLogger(AbstractPartitionedLimiter.class);
3838
private static final String PARTITION_TAG_NAME = "partition";
3939

40-
public abstract static class Builder<BuilderT extends AbstractLimiter.BypassLimiterBuilder<BuilderT, ContextT>, ContextT> extends AbstractLimiter.BypassLimiterBuilder<BuilderT, ContextT> {
40+
public abstract static class Builder<BuilderT extends AbstractLimiter.Builder<BuilderT>, ContextT> extends AbstractLimiter.Builder<BuilderT> {
4141
private List<Function<ContextT, String>> partitionResolvers = new ArrayList<>();
4242
private final Map<String, Partition> partitions = new LinkedHashMap<>();
4343
private int maxDelayedThreads = 100;

concurrency-limits-core/src/main/java/com/netflix/concurrency/limits/limiter/SimpleLimiter.java

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,6 @@
2323
import java.util.concurrent.Semaphore;
2424

2525
public class SimpleLimiter<ContextT> extends AbstractLimiter<ContextT> {
26-
27-
public static class BypassLimiterBuilder<ContextT> extends AbstractLimiter.BypassLimiterBuilder<BypassLimiterBuilder<ContextT>, ContextT> {
28-
public SimpleLimiter<ContextT> build() {
29-
return new SimpleLimiter<>(this);
30-
}
31-
32-
@Override
33-
protected BypassLimiterBuilder<ContextT> self() {
34-
return this;
35-
}
36-
}
37-
3826
public static class Builder extends AbstractLimiter.Builder<Builder> {
3927
public <ContextT> SimpleLimiter<ContextT> build() {
4028
return new SimpleLimiter<>(this);
@@ -46,10 +34,6 @@ protected Builder self() {
4634
}
4735
}
4836

49-
public static <ContextT> BypassLimiterBuilder<ContextT> newBypassLimiterBuilder() {
50-
return new BypassLimiterBuilder<>();
51-
}
52-
5337
public static Builder newBuilder() {
5438
return new Builder();
5539
}
@@ -58,6 +42,7 @@ public static Builder newBuilder() {
5842

5943
public SimpleLimiter(AbstractLimiter.Builder<?> builder) {
6044
super(builder);
45+
6146
this.inflightDistribution = builder.registry.distribution(MetricIds.INFLIGHT_NAME);
6247
this.semaphore = new AdjustableSemaphore(getLimit());
6348
}

concurrency-limits-core/src/test/java/com/netflix/concurrency/limits/limiter/AbstractPartitionedLimiterTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ public void testBypassPartitionedLimiter() {
173173
.partition("batch", 0.1)
174174
.partition("live", 0.9)
175175
.limit(FixedLimit.of(10))
176-
.bypassLimitResolver(new ShouldBypassPredicate())
176+
.bypassLimitResolverInternal(new ShouldBypassPredicate())
177177
.build();
178178

179179
Assert.assertTrue(limiter.acquire("batch").isPresent());
@@ -200,7 +200,7 @@ public void testBypassSimpleLimiter() {
200200

201201
SimpleLimiter<String> limiter = (SimpleLimiter<String>) TestPartitionedLimiter.newBuilder()
202202
.limit(FixedLimit.of(10))
203-
.bypassLimitResolver(new ShouldBypassPredicate())
203+
.bypassLimitResolverInternal(new ShouldBypassPredicate())
204204
.build();
205205

206206
int inflightCount = 0;

concurrency-limits-core/src/test/java/com/netflix/concurrency/limits/limiter/SimpleLimiterTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ public void testReleaseLimit() {
4848

4949
@Test
5050
public void testSimpleBypassLimiter() {
51-
SimpleLimiter<String> limiter = SimpleLimiter.<String>newBypassLimiterBuilder()
51+
SimpleLimiter<String> limiter = SimpleLimiter.<String>newBuilder()
5252
.limit(FixedLimit.of(10))
53-
.bypassLimitResolver((context) -> context.equals("admin"))
53+
.bypassLimitResolverInternal((context) -> context.equals("admin"))
5454
.build();
5555

5656
for (int i = 0; i < 10; i++) {
@@ -68,7 +68,7 @@ public void testSimpleBypassLimiter() {
6868

6969
@Test
7070
public void testSimpleBypassLimiterDefault() {
71-
SimpleLimiter<String> limiter = SimpleLimiter.<String>newBypassLimiterBuilder()
71+
SimpleLimiter<String> limiter = SimpleLimiter.<String>newBuilder()
7272
.limit(FixedLimit.of(10))
7373
.build();
7474

concurrency-limits-grpc/src/main/java/com/netflix/concurrency/limits/grpc/client/GrpcClientLimiterBuilder.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.netflix.concurrency.limits.limiter.AbstractPartitionedLimiter;
2020
import com.netflix.concurrency.limits.limiter.BlockingLimiter;
2121
import io.grpc.CallOptions;
22+
import java.util.function.Predicate;
2223

2324
/**
2425
* Builder to simplify creating a {@link Limiter} specific to GRPC clients.
@@ -34,6 +35,18 @@ public GrpcClientLimiterBuilder partitionByCallOption(CallOptions.Key<String> op
3435
return partitionResolver(context -> context.getCallOptions().getOption(option));
3536
}
3637

38+
/**
39+
* Add a chainable bypass resolver predicate from context. Multiple resolvers may be added and if any of the
40+
* predicate condition returns true the call is bypassed without increasing the limiter inflight count and
41+
* affecting the algorithm. Will not bypass any calls by default if no resolvers are added.
42+
*
43+
* @param shouldBypass Predicate condition to bypass limit
44+
* @return Chainable builder
45+
*/
46+
public GrpcClientLimiterBuilder bypassLimitResolver(Predicate<GrpcClientRequestContext> shouldBypass) {
47+
return bypassLimitResolverInternal(shouldBypass);
48+
}
49+
3750
/**
3851
* Bypass limit if the request's full method name matches the specified gRPC method's full name.
3952
* @param fullMethodName The full method name to check against the {@link GrpcClientRequestContext}'s method.

concurrency-limits-grpc/src/main/java/com/netflix/concurrency/limits/grpc/server/GrpcServerLimiterBuilder.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.netflix.concurrency.limits.limiter.AbstractPartitionedLimiter;
1919
import io.grpc.Attributes;
2020
import io.grpc.Metadata;
21+
import java.util.function.Predicate;
2122

2223
public class GrpcServerLimiterBuilder extends AbstractPartitionedLimiter.Builder<GrpcServerLimiterBuilder, GrpcServerRequestContext> {
2324
/**
@@ -44,6 +45,19 @@ public GrpcServerLimiterBuilder partitionByAttribute(Attributes.Key<String> attr
4445
return partitionResolver(context -> context.getCall().getAttributes().get(attribute));
4546
}
4647

48+
49+
/**
50+
* Add a chainable bypass resolver predicate from context. Multiple resolvers may be added and if any of the
51+
* predicate condition returns true the call is bypassed without increasing the limiter inflight count and
52+
* affecting the algorithm. Will not bypass any calls by default if no resolvers are added.
53+
*
54+
* @param shouldBypass Predicate condition to bypass limit
55+
* @return Chainable builder
56+
*/
57+
public GrpcServerLimiterBuilder bypassLimitResolver(Predicate<GrpcServerRequestContext> shouldBypass) {
58+
return bypassLimitResolverInternal(shouldBypass);
59+
}
60+
4761
/**
4862
* Bypass limit if the request's full method name matches the specified gRPC method's full name.
4963
* @param fullMethodName The full method name to check against the {@link GrpcServerRequestContext}'s method.

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.security.Principal;
2323
import java.util.Optional;
2424
import java.util.function.Function;
25+
import java.util.function.Predicate;
2526

2627
/**
2728
* Builder to simplify creating a {@link Limiter} specific to a Servlet filter. By default,
@@ -73,6 +74,18 @@ public ServletLimiterBuilder partitionByPathInfo(Function<String, String> pathTo
7374
return partitionResolver(request -> Optional.ofNullable(request.getPathInfo()).map(pathToGroup).orElse(null));
7475
}
7576

77+
/**
78+
* Add a chainable bypass resolver predicate from context. Multiple resolvers may be added and if any of the
79+
* predicate condition returns true the call is bypassed without increasing the limiter inflight count and
80+
* affecting the algorithm. Will not bypass any calls by default if no resolvers are added.
81+
*
82+
* @param shouldBypass Predicate condition to bypass limit
83+
* @return Chainable builder
84+
*/
85+
public ServletLimiterBuilder bypassLimitResolver(Predicate<HttpServletRequest> shouldBypass) {
86+
return bypassLimitResolverInternal(shouldBypass);
87+
}
88+
7689
/**
7790
* Bypass limit if the value of the provided header name matches the specified value.
7891
* @param name The name of the header to check.

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.netflix.concurrency.limits.Limiter;
1919
import com.netflix.concurrency.limits.limiter.AbstractPartitionedLimiter;
2020

21+
import java.util.function.Predicate;
2122
import javax.servlet.http.HttpServletRequest;
2223
import java.security.Principal;
2324
import java.util.Optional;
@@ -73,6 +74,18 @@ public ServletLimiterBuilder partitionByPathInfo(Function<String, String> pathTo
7374
return partitionResolver(request -> Optional.ofNullable(request.getPathInfo()).map(pathToGroup).orElse(null));
7475
}
7576

77+
/**
78+
* Add a chainable bypass resolver predicate from context. Multiple resolvers may be added and if any of the
79+
* predicate condition returns true the call is bypassed without increasing the limiter inflight count and
80+
* affecting the algorithm. Will not bypass any calls by default if no resolvers are added.
81+
*
82+
* @param shouldBypass Predicate condition to bypass limit
83+
* @return Chainable builder
84+
*/
85+
public ServletLimiterBuilder bypassLimitResolver(Predicate<HttpServletRequest> shouldBypass) {
86+
return bypassLimitResolverInternal(shouldBypass);
87+
}
88+
7689
/**
7790
* Bypass the limit if the value of the provided header name matches the specified value.
7891
* @param name The name of the header to check.

0 commit comments

Comments
 (0)