Add resilience4j-circuit-breaker: Resilience4j 2.4.0 vs Spring Framework 7 core, on Boot 4.1
Companion module for the rewritten post 'Resilience4j Circuit Breaker in Spring Boot 4.1: What It's Still For', reworked around Framework 7 now shipping @Retryable/@ConcurrencyLimit in core. Covers what's still Resilience4j's job (circuit breaker, rate limiter, bulkhead's bounded wait, fallback methods, Actuator/Micrometer metrics), the off-by-one between maxAttempts and maxRetries, and two Boot-4.1 build breaks: spring-boot-starter-aop no longer exists (renamed to spring-boot-starter-aspectj, proven with Maven Central metadata and the renamed starter's own POM -- see resilience/docs/08-starter-aop-renamed-to-starter-aspectj.md in this same repo), and the resulting fix uses that renamed starter directly rather than assembling spring-aop + aspectjweaver by hand. Kept as its own module rather than a new top-level repository, alongside the existing resilience/ module for the sibling Framework-7 post.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
# 4. @ConcurrencyLimit vs Resilience4j's Bulkhead
|
||||
|
||||
[Previous: 03-spring-retryable.md](03-spring-retryable.md) | [README](../README.md) | Next: [05-production-checklist.md](05-production-checklist.md)
|
||||
|
||||
Source: [`ConcurrencyLimitedService.java`](../src/main/java/com/ankurm/resilience/springresilience/ConcurrencyLimitedService.java),
|
||||
[`R4jBulkheadService.java`](../src/main/java/com/ankurm/resilience/r4j/R4jBulkheadService.java).
|
||||
Tests: [`ConcurrencyLimitTest.java`](../src/test/java/com/ankurm/resilience/springresilience/ConcurrencyLimitTest.java).
|
||||
Transcripts: [`04a`](output/04a-concurrencylimit-block.txt), [`04b`](output/04b-concurrencylimit-reject.txt), [`04c`](output/04c-r4j-bulkhead-comparison.txt).
|
||||
|
||||
Both cap concurrent invocations of one method at 2. Both were hit with 4 concurrent callers,
|
||||
each holding its slot for 300ms, in the same test run.
|
||||
|
||||
## BLOCK: queue, no timeout
|
||||
|
||||
`04a-concurrencylimit-block.txt`: all 4 callers succeed. Sorted completion times were
|
||||
`[300, 300, 597, 600]` ms — two callers finish almost immediately, the other two only after a
|
||||
slot frees, for a total wall time of ~601ms instead of the ~300ms it would take with no limit
|
||||
at all. There is no attribute on `@ConcurrencyLimit` to bound how long a blocked caller waits;
|
||||
it queues until a slot opens, however long that takes.
|
||||
|
||||
## REJECT: fail fast, no queue
|
||||
|
||||
`04b-concurrencylimit-reject.txt`, `policy = ConcurrencyLimit.ThrottlePolicy.REJECT`: 2 callers
|
||||
succeed in ~300ms, and the other 2 are rejected in ~0ms — not made to wait at all. The exception
|
||||
is `org.springframework.resilience.InvocationRejectedException`, found with `javap` on
|
||||
`ConcurrencyLimitBeanPostProcessor$RejectingConcurrencyThrottleInterceptor.onAccessRejected` —
|
||||
it is not documented on the `@ConcurrencyLimit` annotation itself.
|
||||
|
||||
## What Resilience4j's Bulkhead adds: a bounded wait
|
||||
|
||||
`04c-r4j-bulkhead-comparison.txt` runs the identical 4-caller/300ms scenario against a
|
||||
Resilience4j `@Bulkhead(maxConcurrentCalls=2, maxWaitDuration=100ms)`. Two callers finish
|
||||
normally around 300ms; the other two wait up to `maxWaitDuration` (observed: 111ms and 117ms,
|
||||
close to the configured 100ms) and are rejected via the fallback method — a third option that
|
||||
sits between Spring's BLOCK (wait forever) and REJECT (never wait): **wait, but only briefly**.
|
||||
Neither `@ConcurrencyLimit` policy offers that middle ground.
|
||||
|
||||
<blockquote style="background:#f4f5f7;border:1px solid #e2e5ea;border-left:4px solid #b7bec9;border-radius:6px;padding:16px 20px;"><strong>Picking between them.</strong> If you want callers to queue briefly rather than either wait forever or fail instantly, Resilience4j's Bulkhead with a short <code>maxWaitDuration</code> is still the only one of the three that does it. If unlimited blocking is actually fine — e.g. limiting concurrent writers to a single-threaded resource — <code>@ConcurrencyLimit</code>'s BLOCK policy is one annotation and one less dependency.</blockquote>
|
||||
|
||||
Next: [05-production-checklist.md](05-production-checklist.md).
|
||||
Reference in New Issue
Block a user