Files
spring-boot-demo/resilience4j-circuit-breaker/docs/04-concurrency-limit.md
T
asmhatre 320733265f 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.
2026-09-18 08:40:50 +00:00

41 lines
3.0 KiB
Markdown

# 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).