Most Java resilience libraries wrap each call in decorators at runtime; resilience4cata writes the wrapping code at compile time instead, and a healthy circuit breaker ends up costing about two nanoseconds.
Originally published on resilience4cata.dev .
The problem with decorators
Retries, circuit breakers, timeouts and bulkheads usually sit on the call path as runtime decorators. You wrap a Supplier or a Callable in one function per policy, or let a framework put a proxy in front of your bean that does it for you. It works, it is flexible, and it is how Resilience4j, the library many Java teams use, is built.
It also costs something on every call, including the ones that succeed, which are almost all of them. Each layer is another call through a functional interface, and the policy state behind it is shared by every thread. In a benchmark with one decorated method the JIT sees a single type at each call site and can inline much of it, which is the best case.…