resilience4cata 1.0.0-RC2 is on Maven Central, and its API is frozen. Unless feedback shows that something has to change, 1.0.0 will ship with this same API. After that, compatibility is a promise for the whole major version. So this is the moment when your opinion can still change things.
What it is
resilience4cata is a resilience library for Java 21+:
- retry, circuit breaker, rate limiter, timeout, bulkhead and fallback;
- plus hedging and an adaptive concurrency limit.
You annotate an interface or a class:
public interface PriceClient {
@Retry(maxAttempts = 3, delay = "100ms")
@CircuitBreaker(name = "prices")
@Timeout("2s")
@Fallback(method = "unknownPrice")
BigDecimal price(String sku) throws IOException;
default BigDecimal unknownPrice(String sku, Exception failure) {
return BigDecimal.ZERO;
}
}
At compile time, an annotation processor writes a plain Java class next to it, PriceClient$Resilient, that calls each policy directly. It needs no proxy, aspect or agent. Mistakes such as an invalid duration or a fallback with the wrong signature are compile errors, each with a documented error code.
Without a framework, you wrap any implementation:
ResilienceRegistry registry = ResilienceRegistry.builder().build();
PriceClient prices = Resilient.of(PriceClient.class, new HttpPriceClient(), registry);
In Spring Boot, Micronaut or Quarkus, the integration makes the generated class the bean. Each integration also reads the configuration from application.yml or application.properties and adds health checks and metrics, plus a management endpoint in Spring Boot and Micronaut.
Why compile time
Decorators and interceptors cost something on every call, the successful ones included. Generated code calls the policies directly, and a successful call allocates nothing. Measured on the release candidate, on AWS Graviton3 with one thread, against Resilience4j 2.4.0 configured the same way:
| Call | resilience4cata | Resilience4j 2.4.0 |
|---|---|---|
| Retry, first attempt succeeds | 1.4 ns, 0 B | 22.9 ns, 0 B |
| Circuit breaker, every call succeeds | 1.9 ns, 0 B | 89.9 ns, 0 B |
| Sync timeout | 23.6 ns, 0 B | 8907.6 ns, 183 B |
| Bulkhead, slots free | 17.3 ns, 0 B | 20.1 ns, 0 B |
With four threads, the circuit breaker stays at 1.9 ns, and Resilience4j's goes to 849.5 ns. The benchmark notes cover the setup, the fairness adjustments and the cases that are close.
What changed on the way to 1.0
- The last API changes. Two small incompatible ones, and nullable result types, which help Kotlin and NullAway users.
- A written compatibility promise. It covers what stays compatible within a major version: the Java API, code generated by an older processor, configuration keys, metric names, endpoint JSON and error codes.
- Four concurrency races fixed. They were in the bulkhead, the adaptive limit, the rate limiter and the timeout. The concurrency stress tests (jcstress) now also pass on ARM.
- New in RC2: a missing annotation processor is an error. Before, beans silently ran unprotected. Now Spring Boot and Micronaut fail at startup, Quarkus fails at build time, and the message names the dependency to add.
Try it
dependencies {
implementation 'dev.resilience4cata:resilience4cata-all:1.0.0-RC2'
annotationProcessor 'dev.resilience4cata:resilience4cata-processor:1.0.0-RC2'
}
For Maven, the installation page has the setup; write 1.0.0-RC2 where it shows 0.13.0. Then:
- the quickstart takes about five minutes;
- there are guides for Spring Boot, Micronaut and Quarkus;
- if you use Resilience4j today, start with the migration guide.
Feedback wanted
The most useful feedback is what you would want changed after 1.0 but could not change, because it would break compatibility:
- a name, default or annotation attribute that surprised you;
- an error message that didn't tell you what to do;
- a framework setup that didn't work as documented;
- something you had to work around.
Please open an issue or a discussion. "I tried it and it just worked" helps too.
The code is Apache-2.0 on GitHub.
Top comments (0)