<?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: Leopoldo Benavente Cadena</title>
    <description>The latest articles on DEV Community by Leopoldo Benavente Cadena (@lbenavente).</description>
    <link>https://dev.to/lbenavente</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%2F1444451%2Fa3c6edd5-32d3-4436-9722-d6609109a69f.png</url>
      <title>DEV Community: Leopoldo Benavente Cadena</title>
      <link>https://dev.to/lbenavente</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lbenavente"/>
    <language>en</language>
    <item>
      <title>Building ResilienceLab: retries, circuit breakers, and tracing in .NET 10</title>
      <dc:creator>Leopoldo Benavente Cadena</dc:creator>
      <pubDate>Mon, 05 Oct 2026 22:45:12 +0000</pubDate>
      <link>https://dev.to/lbenavente/building-resiliencelab-retries-circuit-breakers-and-tracing-in-net-10-a4a</link>
      <guid>https://dev.to/lbenavente/building-resiliencelab-retries-circuit-breakers-and-tracing-in-net-10-a4a</guid>
      <description>&lt;p&gt;I wanted to build something useful and ended up creating &lt;strong&gt;ResilienceLab 0.5.0&lt;/strong&gt;, an experimental resilience library for .NET 10 with its own API and an MIT license. I developed it with coding agents, reviewing the contracts and checking the results.&lt;/p&gt;

&lt;p&gt;It includes retries for exceptions and generic results, cooperative timeouts, circuit breakers, fallback, rate limiting, hedging, and an HTTP adapter integrated with &lt;code&gt;IHttpClientFactory&lt;/code&gt;. Optional observability uses logs, metrics, and &lt;code&gt;ActivitySource&lt;/code&gt; traces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy order changes the behavior
&lt;/h2&gt;

&lt;p&gt;In the catalog API, I composed these layers from outside to inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fallback → Circuit breaker → Retry → Timeout → HTTP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The circuit counts one failed call after its retries are exhausted, while each attempt has its own timeout. Two additional retries allow up to three HTTP requests. The HTTP adapter has its retry budget disabled to avoid multiplying attempts.&lt;/p&gt;

&lt;p&gt;Fallback returns only the last valid catalog snapshot, within a bounded age. If the upstream service fails and there is no usable cache, the API returns &lt;code&gt;503&lt;/code&gt;. It does not invent a successful response.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I verified
&lt;/h2&gt;

&lt;p&gt;The library has &lt;strong&gt;194 passing Release tests&lt;/strong&gt;. The consumer API adds &lt;strong&gt;12 reproducible HTTP scenarios&lt;/strong&gt;, covering recovery, timeouts, circuit opening and recovery, cold and expired caches, and consumer cancellation without pending upstream handlers. I also made a real external request and verified that an OpenTelemetry Collector received traces and metrics over OTLP.&lt;/p&gt;

&lt;p&gt;These are bounded checks, not proof of production performance or long-term stability. A cooperative timeout requests cancellation and waits for the operation to finish. Hedging cancels and awaits losing attempts, which can delay completion if they ignore cancellation and can increase upstream load. The library is experimental and does not offer Polly API compatibility.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ResilienceLab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0.5.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ResilienceLab.Http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0.5.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a .NET 10 console app, install the core package, and replace &lt;code&gt;Program.cs&lt;/code&gt; with this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Net.Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ResilienceLab&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;attempts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Retry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ct&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ThrowIfCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;attempts&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
            &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromException&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpRequestException&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Recovered"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RetryOptions&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MaxRetries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;BaseDelay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMilliseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;MaxDelay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ShouldRetry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;HttpRequestException&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;; attempts: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simulates two transient failures and prints &lt;code&gt;Recovered; attempts: 3&lt;/code&gt;. The default full jitter randomizes the bounded delay; cancellation is never retried.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/lbe2014/resilience-lab" rel="noopener noreferrer"&gt;Source code and runnable examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lbe2014/resilience-lab/releases/tag/v0.5.0" rel="noopener noreferrer"&gt;Experimental v0.5.0 release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.nuget.org/packages/ResilienceLab/0.5.0" rel="noopener noreferrer"&gt;ResilienceLab on NuGet&lt;/a&gt; and &lt;a href="https://www.nuget.org/packages/ResilienceLab.Http/0.5.0" rel="noopener noreferrer"&gt;ResilienceLab.Http&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.lbe2014.com/articulos/resiliencialab-dotnet-resiliencia-observabilidad/" rel="noopener noreferrer"&gt;Full article on LBE Tech — in Spanish&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which retry, cancellation, or fallback behavior has been hardest to verify in your applications?&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>observability</category>
      <category>csharp</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
