<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fabián Guamán Flores</title>
    <description>The latest articles on DEV Community by Fabián Guamán Flores (@fabiangftech).</description>
    <link>https://dev.to/fabiangftech</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3583327%2Ffdbd13c6-8923-4747-a3e0-2fed19c75b19.png</url>
      <title>DEV Community: Fabián Guamán Flores</title>
      <link>https://dev.to/fabiangftech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fabiangftech"/>
    <language>en</language>
    <item>
      <title>resilience4cata 1.0.0-RC2: resilience annotations for Java, generated at compile time</title>
      <dc:creator>Fabián Guamán Flores</dc:creator>
      <pubDate>Thu, 05 Nov 2026 11:32:08 +0000</pubDate>
      <link>https://dev.to/fabiangftech/resilience4cata-100-rc2-resilience-annotations-for-java-generated-at-compile-time-4ig1</link>
      <guid>https://dev.to/fabiangftech/resilience4cata-100-rc2-resilience-annotations-for-java-generated-at-compile-time-4ig1</guid>
      <description>&lt;p&gt;&lt;strong&gt;resilience4cata 1.0.0-RC2 is on Maven Central, and its API is frozen.&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is
&lt;/h2&gt;

&lt;p&gt;resilience4cata is a resilience library for Java 21+:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retry, circuit breaker, rate limiter, timeout, bulkhead and fallback;&lt;/li&gt;
&lt;li&gt;plus hedging and an adaptive concurrency limit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You annotate an interface or a class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PriceClient&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Retry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxAttempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"100ms"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@CircuitBreaker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"prices"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Timeout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2s"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Fallback&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"unknownPrice"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="nf"&gt;unknownPrice&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ZERO&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At compile time, an annotation processor writes a plain Java class next to it, &lt;code&gt;PriceClient$Resilient&lt;/code&gt;, 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.&lt;/p&gt;

&lt;p&gt;Without a framework, you wrap any implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ResilienceRegistry&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ResilienceRegistry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;PriceClient&lt;/span&gt; &lt;span class="n"&gt;prices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Resilient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PriceClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpPriceClient&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Spring Boot, Micronaut or Quarkus, the integration makes the generated class the bean. Each integration also reads the configuration from &lt;code&gt;application.yml&lt;/code&gt; or &lt;code&gt;application.properties&lt;/code&gt; and adds health checks and metrics, plus a management endpoint in Spring Boot and Micronaut.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why compile time
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Call&lt;/th&gt;
&lt;th&gt;resilience4cata&lt;/th&gt;
&lt;th&gt;Resilience4j 2.4.0&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Retry, first attempt succeeds&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1.4 ns&lt;/strong&gt;, 0 B&lt;/td&gt;
&lt;td&gt;22.9 ns, 0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Circuit breaker, every call succeeds&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1.9 ns&lt;/strong&gt;, 0 B&lt;/td&gt;
&lt;td&gt;89.9 ns, 0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sync timeout&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;23.6 ns&lt;/strong&gt;, 0 B&lt;/td&gt;
&lt;td&gt;8907.6 ns, 183 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bulkhead, slots free&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;17.3 ns&lt;/strong&gt;, 0 B&lt;/td&gt;
&lt;td&gt;20.1 ns, 0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With four threads, the circuit breaker stays at 1.9 ns, and Resilience4j's goes to 849.5 ns. &lt;a href="https://resilience4cata.dev/en/benchmarks#release-candidate" rel="noopener noreferrer"&gt;The benchmark notes&lt;/a&gt; cover the setup, the fairness adjustments and the cases that are close.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed on the way to 1.0
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The last API changes.&lt;/strong&gt; Two small incompatible ones, and nullable result types, which help Kotlin and NullAway users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A written &lt;a href="https://resilience4cata.dev/en/reference/compatibility" rel="noopener noreferrer"&gt;compatibility promise&lt;/a&gt;.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four concurrency races fixed.&lt;/strong&gt; They were in the bulkhead, the adaptive limit, the rate limiter and the timeout. The concurrency stress tests (jcstress) now also pass on ARM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New in RC2: a missing annotation processor is an error.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'dev.resilience4cata:resilience4cata-all:1.0.0-RC2'&lt;/span&gt;
    &lt;span class="n"&gt;annotationProcessor&lt;/span&gt; &lt;span class="s1"&gt;'dev.resilience4cata:resilience4cata-processor:1.0.0-RC2'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Maven, the &lt;a href="https://resilience4cata.dev/en/start/installation" rel="noopener noreferrer"&gt;installation page&lt;/a&gt; has the setup; write &lt;code&gt;1.0.0-RC2&lt;/code&gt; where it shows 0.13.0. Then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;a href="https://resilience4cata.dev/en/start/quickstart" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt; takes about five minutes;&lt;/li&gt;
&lt;li&gt;there are guides for &lt;a href="https://resilience4cata.dev/en/guides/spring" rel="noopener noreferrer"&gt;Spring Boot&lt;/a&gt;, &lt;a href="https://resilience4cata.dev/en/guides/micronaut" rel="noopener noreferrer"&gt;Micronaut&lt;/a&gt; and &lt;a href="https://resilience4cata.dev/en/guides/quarkus" rel="noopener noreferrer"&gt;Quarkus&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;if you use Resilience4j today, start with the &lt;a href="https://resilience4cata.dev/en/migrate/from-resilience4j" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Feedback wanted
&lt;/h2&gt;

&lt;p&gt;The most useful feedback is what you would want changed after 1.0 but could not change, because it would break compatibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a name, default or annotation attribute that surprised you;&lt;/li&gt;
&lt;li&gt;an error message that didn't tell you what to do;&lt;/li&gt;
&lt;li&gt;a framework setup that didn't work as documented;&lt;/li&gt;
&lt;li&gt;something you had to work around.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please open an &lt;a href="https://github.com/fabiangftech/resilience4cata/issues" rel="noopener noreferrer"&gt;issue&lt;/a&gt; or a &lt;a href="https://github.com/fabiangftech/resilience4cata/discussions" rel="noopener noreferrer"&gt;discussion&lt;/a&gt;. "I tried it and it just worked" helps too.&lt;/p&gt;

&lt;p&gt;The code is Apache-2.0 on &lt;a href="https://github.com/fabiangftech/resilience4cata" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>opensource</category>
      <category>performance</category>
      <category>springboot</category>
    </item>
    <item>
      <title>A 1.9 ns circuit breaker: resilience generated at compile time</title>
      <dc:creator>Fabián Guamán Flores</dc:creator>
      <pubDate>Tue, 27 Oct 2026 16:40:45 +0000</pubDate>
      <link>https://dev.to/fabiangftech/a-19-ns-circuit-breaker-resilience-generated-at-compile-time-api</link>
      <guid>https://dev.to/fabiangftech/a-19-ns-circuit-breaker-resilience-generated-at-compile-time-api</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://resilience4cata.dev/en/blog/compile-time-resilience" rel="noopener noreferrer"&gt;resilience4cata.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with decorators
&lt;/h2&gt;

&lt;p&gt;Retries, circuit breakers, timeouts and bulkheads usually sit on the call path as runtime decorators. You wrap a &lt;code&gt;Supplier&lt;/code&gt; or a &lt;code&gt;Callable&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;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. A real service decorates dozens of methods, and those shared call sites stop being monomorphic.&lt;/p&gt;

&lt;p&gt;I wanted to know how cheap the same guarantees could be if the code were written for one method at a time, the way you would write it by hand if you had the patience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let the compiler write it
&lt;/h2&gt;

&lt;p&gt;resilience4cata is a set of annotations plus an annotation processor. You annotate an interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;StockClient&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Retry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxAttempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@CircuitBreaker&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;stock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;warehouse&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At compile time the processor writes &lt;code&gt;StockClient$Resilient&lt;/code&gt;, a plain class that implements the interface. This is what it generates (abridged, package names shortened):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;stock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;p0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;RetryPolicy&lt;/span&gt; &lt;span class="n"&gt;r4c$retry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retry0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;r4c$start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r4c$retry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;r4c$attempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;r4c&lt;/span&gt;&lt;span class="n"&gt;$circuitBreaker0&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;r4c$e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;r4c$delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r4c$retry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onFailure&lt;/span&gt;&lt;span class="o"&gt;(++&lt;/span&gt;&lt;span class="n"&gt;r4c$attempts&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r4c$e&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r4c$start&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r4c$delay&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;r4c$retry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pause&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r4c$delay&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;r4c$e&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;r4c$circuitBreaker0&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;p0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;CircuitBreakerPolicy&lt;/span&gt; &lt;span class="n"&gt;r4c$cb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;circuitBreaker0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;r4c$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r4c$cb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;acquire&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r4c$token&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;r4c$cb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rejection&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;r4c$started&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r4c$cb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startTimer&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;r4c$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delegate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;r4c$cb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r4c$token&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r4c$started&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r4c$result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;r4c$e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;r4c$cb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onFailure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r4c$token&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r4c$started&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r4c$e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;r4c$e&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each protected method becomes straight-line code: a loop for the retry, a &lt;code&gt;try&lt;/code&gt; block for the breaker, one &lt;code&gt;final&lt;/code&gt; field per policy. There are no lambdas, no proxies and no reflection on the call path, and every call site is monomorphic because every method has its own. At runtime you wrap your implementation once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;StockClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Resilient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StockClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpStockClient&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The policies behind those fields follow a small protocol (&lt;code&gt;acquire&lt;/code&gt;, &lt;code&gt;onSuccess&lt;/code&gt;, &lt;code&gt;onFailure&lt;/code&gt;) designed so that the success path does as little as possible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No allocation.&lt;/strong&gt; Rejections throw one preallocated, stackless exception per policy, and events are only created when someone listens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No clock read unless a feature needs it.&lt;/strong&gt; The breaker only times calls when you configure a slow-call threshold.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No shared write when nothing changes.&lt;/strong&gt; While a breaker is closed and has only seen fast successes, a call reads one field and writes nothing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last rule did the most. Under contention, writing a cache line that other cores are reading costs far more than any lock-free trick saves. On an Apple M1 with four threads, the breaker's first version cost 244 ns per successful call; skipping writes while the window holds only fast successes brought it to 10 ns, and reading a single field while healthy to about 3 ns (the full history is on the &lt;a href="https://resilience4cata.dev/en/benchmarks" rel="noopener noreferrer"&gt;benchmarks page&lt;/a&gt;, section "Circuit breaker, 1 and 4 threads").&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;p&gt;Every comparison uses equivalent configuration in both libraries, and each adjustment made for fairness is written in the benchmark's Javadoc.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Measured:&lt;/strong&gt; AWS &lt;code&gt;c7g.xlarge&lt;/code&gt; (Graviton3), JDK 21, resilience4cata 0.1.0 vs Resilience4j 2.4.0, time and bytes allocated per call. Raw results: &lt;a href="https://resilience4cata.dev/en/benchmarks" rel="noopener noreferrer"&gt;benchmarks page&lt;/a&gt;, section "MVP completion (2026-10-24, AWS Graviton3)".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Threads&lt;/th&gt;
&lt;th&gt;resilience4cata&lt;/th&gt;
&lt;th&gt;Resilience4j 2.4.0&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Retry, first attempt succeeds&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1.2 ns&lt;/strong&gt;, 0 B&lt;/td&gt;
&lt;td&gt;22.9 ns, 0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry, fails once then succeeds&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;6.5 ns&lt;/strong&gt;, 0 B&lt;/td&gt;
&lt;td&gt;61.6 ns, 16 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Circuit breaker, every call succeeds&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1.9 ns&lt;/strong&gt;, 0 B&lt;/td&gt;
&lt;td&gt;89.2 ns, 0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Circuit breaker, every call succeeds&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1.9 ns&lt;/strong&gt;, 0 B&lt;/td&gt;
&lt;td&gt;832 ns, 2 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bulkhead, full (every call rejected)&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;2.3 ns&lt;/strong&gt;, 0 B&lt;/td&gt;
&lt;td&gt;139 ns, 416 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bulkhead, slots free&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;20.5 ns, 0 B&lt;/td&gt;
&lt;td&gt;20.4 ns, 0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry + circuit breaker&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;2.3 ns&lt;/strong&gt;, 0 B&lt;/td&gt;
&lt;td&gt;175 ns, 288 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The table also shows where the approach does not help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Some cases are a tie.&lt;/strong&gt; A bulkhead with free slots takes and gives back a permit with one atomic operation in both libraries: 20.5 ns against 20.4 ns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contention still costs hundreds of nanoseconds.&lt;/strong&gt; With four threads and one call in ten failing, the breaker costs 274 ns (Resilience4j: 910 ns), because every call advances one shared window cursor. Shared counters cost the bulkhead and the rate limiter in the same way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async calls allocate.&lt;/strong&gt; Every async layer returns a new future in both libraries; only the sync paths are allocation-free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Some rows are not like for like.&lt;/strong&gt; Resilience4j's retry calls &lt;code&gt;Thread.sleep(0)&lt;/code&gt; between attempts, so its row uses a variant without it (its best case). Its &lt;code&gt;TimeLimiter&lt;/code&gt; needs a &lt;code&gt;Future&lt;/code&gt;, so a synchronous timeout means another thread; that comparison is on the &lt;a href="https://resilience4cata.dev/en/performance" rel="noopener noreferrer"&gt;performance page&lt;/a&gt;, with that caveat.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Does a nanosecond matter for a call that goes over the network? Rarely on its own. It matters when resilience wraps calls that are usually cheap (a cache, a local service, an in-memory queue), when the same code runs millions of times per second, or when you would rather not think about whether a policy is too expensive to add.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs you
&lt;/h2&gt;

&lt;p&gt;Compile-time generation has trade-offs, and they are deliberate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The annotation processor is mandatory.&lt;/strong&gt; There is no reflection fallback: if the generated class is missing, &lt;code&gt;Resilient.of&lt;/code&gt; fails at once and names the dependency to add.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mistakes are compile errors.&lt;/strong&gt; An invalid duration, an unknown attribute or a fallback with the wrong signature fails the build with a code such as &lt;code&gt;R4C-ATR-001&lt;/code&gt;, documented in the &lt;a href="https://resilience4cata.dev/en/errors" rel="noopener noreferrer"&gt;error reference&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration is resolved once.&lt;/strong&gt; Values can come from annotations, properties files, environment variables or Spring, Micronaut and Quarkus configuration, but they are fixed when the registry is built: there is no hot reload, by &lt;a href="https://resilience4cata.dev/en/design" rel="noopener noreferrer"&gt;design&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is young.&lt;/strong&gt; The current version is 0.8; until 1.0 the API can still change.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Two lines in Gradle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'dev.resilience4cata:resilience4cata-all:0.10.0'&lt;/span&gt;
&lt;span class="n"&gt;annotationProcessor&lt;/span&gt; &lt;span class="s1"&gt;'dev.resilience4cata:resilience4cata-processor:0.10.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the &lt;a href="https://resilience4cata.dev/en/start/quickstart" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt; protects a flaky call in about five minutes. There are integrations for &lt;a href="https://resilience4cata.dev/en/guides/spring" rel="noopener noreferrer"&gt;Spring Boot&lt;/a&gt;, &lt;a href="https://resilience4cata.dev/en/guides/micronaut" rel="noopener noreferrer"&gt;Micronaut&lt;/a&gt; and &lt;a href="https://resilience4cata.dev/en/guides/quarkus" rel="noopener noreferrer"&gt;Quarkus&lt;/a&gt;, a &lt;a href="https://resilience4cata.dev/en/guides/testing" rel="noopener noreferrer"&gt;testkit&lt;/a&gt; with a manual clock, and a &lt;a href="https://resilience4cata.dev/en/migrate/from-resilience4j" rel="noopener noreferrer"&gt;guide for migrating from Resilience4j&lt;/a&gt;. The code is Apache-2.0 licensed on &lt;a href="https://github.com/fabiangftech/resilience4cata" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;; issues and discussions are welcome.&lt;/p&gt;

</description>
      <category>java</category>
      <category>performance</category>
      <category>opensource</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
