Resiliency (Resiliency v0.6.0)

Copy Markdown View Source

Resilience and concurrency toolkit for Elixir.

This library bundles six complementary modules that help your application handle failures, bound concurrency, and reduce tail latency — all with zero runtime dependencies.

Modules

Common API patterns

Most modules in this library follow a consistent workflow:

  1. Start — Add the module to your supervision tree (where applicable). Resiliency.CircuitBreaker, Resiliency.Hedged (adaptive mode), Resiliency.SingleFlight, and Resiliency.WeightedSemaphore require a running process. BackoffRetry, Race, AllSettled, Map, FirstOk, and stateless Hedged.run/2 are purely functional.

  2. Call — Wrap your operation in a zero-arity function and pass it to the module's primary entry point:

    # Retry
    Resiliency.BackoffRetry.retry(fn -> fetch(url) end, max_attempts: 5)
    
    # Hedged (stateless)
    Resiliency.Hedged.run(fn -> fetch(url) end, delay: 100)
    
    # Hedged (adaptive)
    Resiliency.Hedged.run(MyHedge, fn -> fetch(url) end)
    
    # SingleFlight
    Resiliency.SingleFlight.flight(MyFlights, "user:123", fn -> load_user(123) end)
    
    # Race
    Resiliency.Race.run([fn -> svc_a() end, fn -> svc_b() end])
    
    # WeightedSemaphore
    Resiliency.WeightedSemaphore.acquire(MySem, 3, fn -> bulk_insert(rows) end)
    
    # CircuitBreaker
    Resiliency.CircuitBreaker.call(MyBreaker, fn -> HttpClient.get(url) end)
  3. Handle the result — Every module returns {:ok, value} on success and {:error, reason} on failure, so pattern matching is uniform across the entire toolkit.

Combine modules for layered resilience — for example, wrap a hedged call inside a retry, or protect a retried call behind a weighted semaphore to avoid overwhelming a downstream service.