Migration & What’s Changed
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
Most services that need this page arrive from one of four places: a Hystrix command that still runs in production, a
Resilience4j 1.x dependency that was never upgraded, a Spring Boot 2 or 3 application that is moving to Boot 4, or a
@Retryable method that is still on the archived Spring Retry project. Each has a section below with a mapping table, a
before and after example and the traps found while running it. The page ends with the list of places where the official
readme.io documentation disagrees with the code, collected from the whole section so that you can see them in one place.
The page was written against Resilience4j 2.4.0, Spring Boot 4.1.x (4.1.1), Spring Framework 7.0.x (7.0.9) and Java 21, and every example was compiled and run on exactly those versions on 4 October 2026. Where the old stack is shown it is the version that was current for it: Hystrix 1.5.18, Spring Boot 2.7.18 with Resilience4j 2.3.0, Spring Boot 3.5.16 with Resilience4j 2.4.0, and Spring Retry 2.0.13. The vocabulary (breaker, bulkhead, retry storm) is in Resilience Fundamentals.
Choosing a migration path
Spring Cloud Netflix?"} H -- yes --> H1["Hystrix to Resilience4j
(concept and property tables)"] H1 --> R H -- no --> R{"Resilience4j 1.x?"} R -- yes --> R1["1.x to 2.x
(Java 17, Vavr, removed keys)"] R1 --> B R -- no --> B{"Spring Boot version?"} B -- "2.x" --> B2["spring-boot2 stops at 2.3.0
move to Boot 3 first"] B2 --> B3 B -- "3.x" --> B3["resilience4j-spring-boot3 2.4.0
works on Boot 3.5"] B3 --> B4["Boot 4: resilience4j-spring-boot4
+ spring-boot-starter-aspectj"] B -- "4.x" --> B4 B4 --> SR{"Spring Retry
@Retryable?"} SR -- yes --> SR1["Spring Framework 7 @Retryable
or Resilience4j Retry"] SR -- no --> D["Done: check the readme.io table
before trusting the old docs"] SR1 --> D
The order matters. Hystrix and Resilience4j 1.x are library migrations that you can do on the Spring Boot version you already have, and each is easier to test on a stable platform. The Boot 4 step changes the platform (Spring Framework 7, Jakarta EE 11, new starters), so it is better done last, on code that already speaks Resilience4j 2.x.
Source: RELEASENOTES.adoc at v2.4.0 for 2.0.0 to
2.3.0, and the GitHub release of v2.4.0 for 2.4.0, because
the notes file at the 2.4.0 tag stops at 2.3.0.
Hystrix to Resilience4j
Hystrix is in maintenance mode, with 1.5.18 as its last release, and its Spring integration (Spring Cloud Netflix Hystrix) is not part of the current Spring Cloud release train, whose circuit breaker support is Spring Cloud CircuitBreaker. The history and the design differences are in Resilience Fundamentals; this section is the mapping. The mapping is conceptual first: Hystrix is one command object that bundles a circuit breaker, an isolation strategy, a timeout and a fallback, and Resilience4j is five separate decorators that you combine.
Concepts
| Hystrix | Resilience4j | Where it is covered |
|---|---|---|
|
A decorated function or an annotated method. |
|
|
|
|
|
|
|
Group key, command key |
The instance name: one |
|
Thread pool key, |
A |
|
|
The semaphore |
|
Execution timeout |
|
|
Circuit breaker inside the command |
|
|
|
A direct call; a |
|
|
|
|
Retry (Hystrix has none; callers wrote their own) |
|
|
Rate limiting (none) |
|
|
|
There is no collapser. The |
|
|
|
|
Hystrix Dashboard, Turbine, |
Actuator endpoints, Micrometer meters, Prometheus and Grafana |
|
Archaius dynamic properties |
Spring |
Source: Resilience4j: Comparison to Netflix Hystrix, Hystrix: How it Works and the sections of this documentation linked in the table.
Properties
The defaults of the Hystrix column were printed from a running Hystrix 1.5.18 command (HystrixCommandProperties and
HystrixThreadPoolProperties); the Resilience4j defaults are the 2.4.0 constants. In Hystrix, properties are looked up as
hystrix.command.<commandKey>.<name> with a default fallback; the Resilience4j equivalent is an entry under
instances.<name> that inherits configs.default.
| Hystrix property | Default | Resilience4j property (module) | Default |
|---|---|---|---|
|
|
Choose the bulkhead type: |
- |
|
10 |
|
25 |
|
1000 ms, enabled |
|
1 s |
|
|
|
|
|
|
A |
none |
|
10 |
No equivalent: a fallback runs on the calling thread, unbounded |
- |
|
|
Not applying the decorator, or the |
|
|
20 |
|
100 |
|
50 |
|
50, and 100 |
|
5000 ms |
|
60 s |
|
|
The |
- |
|
|
The |
- |
|
10000 ms, 10 |
|
|
|
500 ms |
None: the failure rate is evaluated on every recorded call |
- |
Half-open: one trial request |
1 |
|
10 |
|
enabled |
None: use the Micrometer timers |
- |
|
|
None |
- |
|
10, 10 |
|
max(CPUs - 1, 1), CPUs |
|
-1 |
|
100 |
|
5 |
None: the queue rejects when it is full |
- |
|
1 minute |
|
20 ms |
Source: Hystrix: Configuration for the property names, the printed
defaults of Hystrix 1.5.18, and the v2.4.0 source of
CircuitBreakerConfig,
BulkheadConfig,
ThreadPoolBulkheadConfig and
TimeLimiterConfig.
The defaults are not the same, and that is where a migration changes behaviour without anyone touching the code. A Hystrix circuit with 20 requests in ten seconds is a Resilience4j breaker that, left on its defaults, needs 100 calls and stays open for 60 seconds. Set the Hystrix values explicitly to keep the old behaviour, as the example does, and then decide what you really want.
Behavioural differences that bite
-
Fallbacks. Hystrix runs the fallback on any failure, a timeout or a rejection. Resilience4j runs the
fallbackMethodof each annotation when that decorator throws, so a fallback on the inner decorator hides the failure from the outer one. See Fallbacks & Composition. -
Timeouts need a future. There is no blocking-method timeout in
@TimeLimiter; a Hystrix command timed out a blocking call by running it on another thread, and Resilience4j gives that to the thread-pool bulkhead plus the time limiter. -
Half-open. Hystrix lets one request through after the sleep window; Resilience4j lets
permittedNumberOfCallsInHalfOpenStatecalls through (10 by default) and compares their failure rate with the threshold. -
Slow calls. A Resilience4j breaker can open on slow calls (
slowCallDurationThreshold, default 60 s) even when nothing fails; Hystrix reacts only to errors and timeouts. -
Nothing is global. There is no
hystrix.command.defaultthat every call inherits unless you defineconfigs.default, and an instance name that is not configured takesconfigs.default, not Resilience4j’s built-in defaults (see Spring Boot Configuration). -
Metrics changed names. The Hystrix stream and the Hystrix dashboard have no counterpart; the meters and endpoints are in Observability.
The Bookshelf pricing call, before and after
The first block is a Hystrix command with the values of the table set explicitly. The second is the Resilience4j version of the same dependency protection: one annotated method, with the breaker and a semaphore bulkhead, and the configuration in YAML. Both were run against a gateway that fails every call.
package com.example.bookshelf;
class PriceCommand extends HystrixCommand<Long> {
private final String isbn;
PriceCommand(String isbn) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("pricing"))
.andCommandKey(HystrixCommandKey.Factory.asKey("price"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
.withExecutionIsolationSemaphoreMaxConcurrentRequests(10)
.withExecutionTimeoutInMilliseconds(1000)
.withCircuitBreakerRequestVolumeThreshold(20)
.withCircuitBreakerErrorThresholdPercentage(50)
.withCircuitBreakerSleepWindowInMilliseconds(5000)
.withMetricsRollingStatisticalWindowInMilliseconds(10000)));
this.isbn = isbn;
}
@Override protected Long run() { return pricing.priceCents(isbn); }
@Override protected Long getFallback() { return 0L; }
}
long cents = new PriceCommand("978-0-13-468599-1").execute();
Source: Hystrix: Configuration and the HystrixCommand API of 1.5.18.
Run: the first call returned the fallback 0; after 20 failed calls isCircuitBreakerOpen() was true, the next call was
short-circuited, and the backend had been invoked 21 times in total.
package com.example.bookshelf;
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.stereotype.Service;
@Service
public class PricingService {
private final PricingGateway gateway;
public PricingService(PricingGateway gateway) {
this.gateway = gateway;
}
@CircuitBreaker(name = "pricing", fallbackMethod = "noPrice")
@Bulkhead(name = "pricing")
public long priceCents(String isbn) {
return gateway.priceCents(isbn);
}
long noPrice(String isbn, Throwable cause) {
return 0L;
}
}
resilience4j:
circuitbreaker:
instances:
pricing:
failureRateThreshold: 50 # errorThresholdPercentage
minimumNumberOfCalls: 20 # requestVolumeThreshold
slidingWindowType: TIME_BASED # rollingStats.timeInMilliseconds
slidingWindowSize: 10 # seconds
waitDurationInOpenState: 5s # sleepWindowInMilliseconds
bulkhead:
instances:
pricing:
maxConcurrentCalls: 10 # semaphore maxConcurrentRequests
Source: Resilience4j: Spring Boot 2 & 3 for the annotations and the
property names, checked against the v2.4.0 CircuitBreakerConfigurationProperties and BulkheadConfigurationProperties.
Run on Spring Boot 4.1.1: the first call returned the fallback 0, after 20 failed calls the breaker was OPEN with a
failure rate of 100.0 and the backend had been invoked 20 times, and the next call returned the fallback without invoking
the backend. A second test read the configuration back (20 calls, 50 percent, TIME_BASED, a window of 10, 10 permitted
half-open calls, 10 concurrent calls). Note that the half-open value stayed at the Resilience4j default of 10, as the table says.
The timeout is the part that moved: Hystrix applied executionTimeoutInMilliseconds to every command, and the annotated
method above has none. Put the timeout on the HTTP client (spring.http.clients.connect-timeout and read-timeout on Spring
Boot 4), or make the method return a CompletionStage and add @TimeLimiter; both are shown in
TimeLimiter.
Resilience4j 1.x to 2.x
Resilience4j 2.0.0 was a deliberate break; its release notes list four things: Vavr removed as a dependency, Java 17 as the baseline, Kotlin 1.7.20 and Spring Boot 2.7. 2.0.1 added Spring Boot 3 and removed the deprecated configuration keys.
| Change | What to do | Version |
|---|---|---|
Java 17 baseline |
Build and run on Java 17 or later. The 2.4.0 jars read back as class-file major version 61, which is Java 17, so Java 21 and virtual threads are fine, and Java 11 and 8 are not. |
2.0.0 |
Vavr removed from the core modules |
Remove |
2.0.0 |
|
Use |
2.0.1 |
|
Use |
2.0.1 |
Retry |
Use |
2.0.1 |
Bulkhead |
Use |
2.0.1 |
|
Use them for Spring Boot 3 and Spring Framework 6 |
2.0.1 |
|
Available from YAML |
2.1.0 |
Apache Commons Configuration registries |
|
2.2.0 |
Source: RELEASENOTES.adoc at v2.4.0, sections 2.0.0, 2.0.1,
2.1.0 and 2.2.0, and the bytecode version of the 2.4.0 jars.
The removed keys are the dangerous part, because a Spring Boot application does not reject an unknown key: it ignores it. The test below binds the 1.x spelling and the 2.x spelling side by side:
@SpringBootTest(classes = BookshelfApplication.class, properties = {
"resilience4j.circuitbreaker.instances.legacy.ringBufferSizeInClosedState=4",
"resilience4j.circuitbreaker.instances.legacy.ringBufferSizeInHalfOpenState=2",
"resilience4j.retry.instances.legacy.maxRetryAttempts=7",
"resilience4j.circuitbreaker.instances.current.slidingWindowSize=4",
"resilience4j.circuitbreaker.instances.current.permittedNumberOfCallsInHalfOpenState=2",
"resilience4j.retry.instances.current.maxAttempts=7"
})
class RenamedPropertiesTest {
// legacy: slidingWindowSize 100, half-open 10, maxAttempts 3 (the 1.x keys did nothing)
// current: slidingWindowSize 4, half-open 2, maxAttempts 7
}
Source: RELEASENOTES.adoc at v2.4.0 (2.0.1,
"Removed deprecated configuration properties"). Run on Spring Boot 4.1.1 and Resilience4j 2.4.0: the instance with the 1.x keys
read back a sliding window of 100, 10 half-open calls and 3 attempts, which are the defaults; the instance with the 2.x keys
read back 4, 2 and 7. A service migrated without a search for these key names keeps running, with a breaker that waits for
100 calls instead of 4. Search the configuration of every environment, including Config Server and Kubernetes ConfigMaps.
After the key names, the code differences are small: Decorators, the registries, the event consumers and the aspects
behave as before. The things that changed in the Spring modules over the 2.x line are listed in
What changed in 2.3.0 and 2.4.0.
Spring Boot 2 to 3 to 4
The resilience4j.* property keys are identical on all three Boot lines. What changes is the starter, the AOP starter, the
platform and a few packages.
| Spring Boot | Resilience4j starter | AOP dependency for the annotations | Last Resilience4j release |
|---|---|---|---|
2.x (Framework 5) |
|
|
2.3.0; the module is not built from the 2.4.0 sources |
3.x (Framework 6) |
|
|
2.4.0 (verified on Boot 3.5.16) |
4.x (Framework 7) |
|
|
2.4.0 |
Source: Maven Central metadata and the v2.4.0 settings.gradle, and
mvn dependency:tree runs of the three poms below. The 2.4.0 sources still contain the -spring, -spring-boot2 and
-spring-cloud2 directories, but settings.gradle includes only spring6, spring-boot3 and spring-boot4; the three
legacy modules were last published in 2.3.0.
Before and after, in Maven
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
</parent>
<dependencies>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.16</version>
</parent>
<properties>
<resilience4j.version>2.4.0</resilience4j.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-bom</artifactId>
<version>${resilience4j.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
<dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-spring-boot3</artifactId></dependency>
</dependencies>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.1</version>
</parent>
<properties>
<java.version>21</java.version>
<resilience4j.version>2.4.0</resilience4j.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-bom</artifactId>
<version>${resilience4j.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aspectj</artifactId></dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot4</artifactId>
<version>${resilience4j.version}</version>
</dependency>
</dependencies>
Source: Resilience4j: Spring Boot 2 & 3 for the Boot 2 and 3 forms
(there is no Boot 4 page on readme.io), the published POM of
resilience4j-spring-boot4 and the
Spring Boot 4.0 Migration Guide. Run:
mvn dependency:tree resolved the Boot 2.7.18 pom (Resilience4j 2.3.0 modules, resilience4j-spring under the starter) and
the Boot 3.5.16 pom (2.4.0, resilience4j-spring6); the Boot 4.1.1 pom is the build that compiled and ran every Bookshelf
test on this page. Gradle users translate the same coordinates to implementation(…) and
mavenBom("io.github.resilience4j:resilience4j-bom:…"), as shown in
Getting Started.
What changes at each step
| Step | What happens | Evidence |
|---|---|---|
Boot 2 to Boot 3 |
Move to Resilience4j 2.0.1 or later with |
Release notes, 2.0.1 |
|
Spring Boot 4 retired the AOP starter; its last Central release is |
Run, see the table of pitfalls |
Version verifier |
Each starter ships an auto-configuration that fails fast when the Boot major version is wrong. |
Run on Boot 4.1.1 |
The BOM does not manage the Boot 4 starter |
|
|
Actuator and web are not transitive |
The Boot 4 starter declares its dependencies at runtime scope. Add |
Published POM |
Modular HTTP client starters |
With only |
Run on Boot 4.1.1 |
HTTP client timeouts |
The keys are |
Run on Boot 4.1.1 |
Health package moves |
|
|
Actuator security matcher |
|
|
HTTP service clients |
|
|
Refresh scope |
|
|
Spring Cloud CircuitBreaker |
5.0.3 still brings Resilience4j 2.3.0 and |
|
Endpoint JSON |
The |
Source: the runs recorded in the pages linked in the table, on Spring Boot 4.1.1 and 3.5.16, and the Spring Boot 4.0 Migration Guide.
A reasonable order for a Boot 2 service is: upgrade Resilience4j on Boot 2 only as far as 2.3.0, rename the removed
keys, move to Boot 3 and resilience4j-spring-boot3, confirm the tests, and only then move to Boot 4.
Spring Retry to Spring Framework 7
Spring Retry (org.springframework.retry:spring-retry) is archived at
spring-attic/spring-retry; the last release is 2.0.13 (8 June 2026). Spring
Framework 7 replaced its core with @Retryable, @EnableResilientMethods, RetryTemplate and RetryPolicy.
Spring Boot 3.5.16 still manages Spring Retry 2.0.13, Spring Boot 4.1.1 no longer does, and Framework 6.2 has no native
API at all, so this migration belongs to the Boot 4 step. The complete attribute-by-attribute table, the behaviour of
RetryTemplate, RetryListener and MethodRetryEvent, and the comparison with Resilience4j are in
Spring Framework Native Resilience; this is the part
you need to start.
| Spring Retry | Spring Framework 7 | What to watch |
|---|---|---|
|
|
Without it, |
|
|
The default of 3 is 3 invocations in Spring Retry and 4 in the Framework. |
|
|
Empty means every exception in both. |
|
|
Default fixed one second in both; the multiplier of 1.0 is fixed. |
|
|
The Framework jitter is an absolute duration added to the delay. |
|
nothing |
Catch the exception at the call site, or use a Resilience4j |
|
|
|
|
nothing in core |
Resilience4j |
Source: Spring Retry (archived) and the Spring Framework reference: Resilience features. The full table was checked against the Spring Retry 2.0.13 and Spring Framework 7.0.9 sources, and the two annotations below were run again for this page.
// Spring Retry 2.0.13: three invocations in total, then @Recover answers
@Configuration
@EnableRetry
class OldConfig { }
class OldCovers {
@org.springframework.retry.annotation.Retryable(retryFor = IOException.class, maxAttempts = 3,
backoff = @Backoff(delay = 10))
String fetch(String isbn) throws IOException { return http.fetch(isbn); }
@Recover
String fallback(IOException e, String isbn) { return "no-cover"; }
}
// Spring Framework 7: maxRetries = maxAttempts - 1, the recovery moves to the caller
@Configuration
@EnableResilientMethods
class NewConfig { }
class NewCovers {
@org.springframework.resilience.annotation.Retryable(includes = IOException.class, maxRetries = 2, delay = 10)
String fetch(String isbn) throws IOException { return http.fetch(isbn); }
}
Source: Spring Framework reference: Resilience features and
Spring Retry (archived). Run on Spring Boot 4.1.1 with @EnableRetry and
@EnableResilientMethods in the same context: each processed its own annotation, the old bean was invoked 3 times and
answered from @Recover with no-cover, and the new bean was invoked 3 times and threw the original IOException. Because
both can be active together, the migration can be done one bean at a time.
When a retry is the only thing you use, the Framework annotation is enough and removes a dependency. When you also need a
circuit breaker, back-off with jitter caps, a retry on a result, or metrics, move to the Resilience4j Retry instead, which
is described in Retry; do not mix both on one method, because native
@Retryable and Resilience4j @Retry multiply (3 by 3 gives 9 invocations).
What changed in 2.3.0 and 2.4.0
The 2.4.0 release notes are only in the GitHub release; the
RELEASENOTES.adoc at the tag stops at 2.3.0, so the first table is from the file and the second from the release and the
sources. The numbers are issue and pull request numbers.
| 2.3.0 change | Effect | Page |
|---|---|---|
Custom |
No internal API needed; a customizer can pass the application clock |
|
Lock-free sliding window (2241) and |
No virtual-thread pinning in the core modules |
|
"Ignore unknown exceptions" in the circuit breaker configuration (2200) and the circular |
In the Boot 3 and 4 starters a misspelled exception class in |
|
|
The annotation attribute is ignored by the Spring 6 aspect that both starters use |
|
RxJava 3 in the Spring aspects (2131); |
RxJava 3 return types work on annotated methods |
|
Exponential back-off or randomized wait from a base configuration (1404); randomize factor above 1.0 fix (2152) |
Back-off inheritance, with gaps documented on the page |
|
Feign 12.5 and later (2130) |
The capability-based API is the supported one |
| 2.4.0 change | Effect | Page |
|---|---|---|
|
The starter for Spring Boot 4 and Framework 7; not in the BOM |
|
|
Boot 2 applications stay on 2.3.0 |
|
Boot-version verifier in the Boot 3 and Boot 4 starters |
The wrong starter fails at start-up instead of misbehaving |
|
|
New Boot keys on the circuit breaker |
|
|
A builder flag only, not a Boot key; the state machine already let |
|
|
A configuration that used to be accepted can now fail |
|
|
A shared configuration key for annotations whose |
|
|
The key binds from YAML; in 2.3.0 it was silently ignored |
|
Customizers applied in the right order (PR 2321), Retry |
Configuration fixes in the Boot 3 and 4 starters |
|
|
Boot 4 starter only |
|
TimeLimiter success path when a stage completes with |
|
|
Bulkhead name in |
Better messages and safer endpoints |
|
|
With Spring Cloud CircuitBreaker, a |
|
|
Use |
Source: RELEASENOTES.adoc (2.3.0) and the
v2.4.0 GitHub release with the v2.4.0 sources. Each row was
run on the page it links; the 2.3.0 against 2.4.0 differences were measured by running the same code against both jars.
Where the readme.io documentation is stale
The Resilience4j user guide was last updated years before 2.4.0, and it has no page for Spring Boot 4 or for 2.4.0 at all. The table collects every disagreement found while writing this section. Where the page and the code differ, the section follows the v2.4.0 source and links the source next to the readme.io page. The last column is the page that explains the detail.
Setup, modules and examples
| Topic | readme.io says | v2.4.0 behaviour | Page |
|---|---|---|---|
Spring Boot 4 |
Not covered: the setup page uses |
|
|
Spring Cloud page |
The legacy page documents |
The module was dropped in 2.4.0 (last release 2.3.0); use Spring Cloud CircuitBreaker, whose 5.0.3 still pins Resilience4j 2.3.0 and |
|
Examples |
Vavr |
Vavr is gone from the core since 2.0.0, jCenter was shut down, and Gradle uses |
|
Cache page |
Written for 0.x: Vavr types and |
|
|
Undocumented modules |
No page for |
All three exist; |
|
Reactive libraries |
|
The 2.4.0 POMs declare Reactor and both RxJava majors at compile scope, and the Boot-managed version wins |
|
Unnamed breaker |
|
|
Properties and behaviour of the patterns
| Topic | readme.io says | v2.4.0 behaviour | Page |
|---|---|---|---|
Missing properties |
The tables omit |
Only |
|
|
An ignored exception is neither a failure nor a success, even if it is also recorded: ignore wins |
At run time ignore wins in 2.3.0 and 2.4.0, as the page says. The 2.4.0 flag |
|
Other circuit breaker keys |
No |
|
|
Durations |
|
A bare number is milliseconds, so this is 10 seconds; prefer a suffix such as |
|
Customizers |
Six types, for instance names |
Seven ( |
|
Boot-only keys |
Not listed: |
All are real; |
|
|
Not explained |
A circuit breaker named |
|
Removed 1.x keys |
Older pages and blog posts show |
Removed in 2.0.1 and silently ignored |
|
Retry |
No |
It exists in the builder and as a Boot key (a class name); the Boot back-off keys are silently ignored without a |
|
RateLimiter |
No |
Java-only; |
|
Bulkhead |
No |
|
|
TimeLimiter |
Programmatic API only; the blocking example |
Boot keys |
|
Aspect order wording |
"The higher value = the higher priority", with an example |
Spring |
|
Global fallback |
A fallback method with only the exception parameter is "global" |
It matches any annotated method of the same class (and its superclasses), not the application; resolution needs exact parameter types and an assignable return type, and ambiguity throws |
Health, metrics and dashboards
| Topic | readme.io says | v2.4.0 behaviour | Page |
|---|---|---|---|
Health indicator mapping |
|
|
|
Micrometer metric names |
The Micrometer page (written for 2.2.0) lists |
|
|
Grafana dashboard |
Old screenshots; the official JSON is the reference |
The JSON queries the missing |
|
Kotlin |
A |
The extension converts it and the runs show |
Other official pages that the section found stale
| Topic | Official page says | Actual behaviour | Page |
|---|---|---|---|
|
Lists |
Spring Framework 7.0.9 has |
|
Gateway MVC reference |
|
The source takes |
|
Release notes |
|
The file at the v2.4.0 tag has no 2.4.0 section; the notes are only in the GitHub release |
|
Canonical URLs |
|
They redirect or return 404 on 4 October 2026: use |
Source: the v2.4.0 sources and the runs referenced from each page, and Resilience4j: Spring Boot 2 & 3, CircuitBreaker, Retry, RateLimiter, Bulkhead, TimeLimiter, Cache, Micrometer, Grafana, the legacy Spring Cloud page and resilience4j-kotlin. The 2.4.0 sources are at github.com/resilience4j/resilience4j at v2.4.0.
Migration pitfalls
| Mistake | What you see | Fix |
|---|---|---|
Migrating to Boot 4 with |
The application starts; |
Use |
|
"Module 'io.github.resilience4j:resilience4j-spring-boot3' is only compatible with Spring Boot 3.x" |
Use |
Importing the BOM and leaving the starter without a version |
|
Pin |
Keeping the 1.x keys |
The breaker uses 100 calls and a window of 100; no error |
Rename to |
Copying Hystrix numbers without setting them |
The breaker needs 100 calls and stays open for 60 seconds |
Set |
|
No retry, no error |
Add the annotation, or use Resilience4j |
|
Four invocations instead of three |
|
Native |
Three times three, nine invocations |
Use one retry layer per call |
Trusting the readme.io aspect-order sentence |
The breaker records one failure per retried call instead of three |
Follow |
Source: the runs on this page and on the pages linked in the tables above. More general anti-patterns are collected in Production Tuning & Anti-patterns.
References
-
Resilience4j
RELEASENOTES.adocat v2.4.0 (2.0.0 to 2.3.0)