ESC
Type to search...
S
Soli Docs

Retry & CircuitBreaker

Resilience helpers for flaky outbound calls: exponential-backoff retries in Soli, and a per-name circuit breaker with a process-global state machine.

Retry.with_backoff(block, opts?)

Runs the block up to attempts times, sleeping between attempts with exponential backoff. Re-raises the last error when every attempt fails.

Method Description
attemptsTotal tries including the first (default 3).
base_delaySeconds before the first retry (default 0.5).
max_delayBackoff cap in seconds (default 8).
factorMultiplier per attempt (default 2).
let body = Retry.with_backoff(fn() {
  HTTP.post_json(webhook_url, payload)
}, { "attempts": 3, "base_delay": 0.5 });

Retry.within(block, opts?)

Keep retrying until a deadline passes rather than a fixed count — useful for boot work that must succeed within N seconds.

Method Description
deadlineGive up after this many seconds (default 10).
Retry.within(fn() { Solidb.ping() }, { "deadline": 10 });

CircuitBreaker state machine

After threshold consecutive failures the named circuit trips open and refuses calls for reset_after seconds — an Int, or a Float for sub-second cool-downs — then allows one probe (half-open). Concurrent callers are refused until that probe reports back, and a probe that never reports is retried after another reset_after. A failed probe re-opens it immediately; a success closes it and resets the count.

Method Description
allow(name)True when a call may proceed.
record_success(name)Reset failures and close.
record_failure(name)Count a failure; trips at the threshold.
state(name)"closed", "open", or "half_open".
configure(name, opts){"threshold": 5, "reset_after": 30} defaults.
reset(name)Forget all state (ops/testing).
if CircuitBreaker.allow("stripe") {
  match HTTP.post_json(url, body) rescue null {
    null => CircuitBreaker.record_failure("stripe"),
    r => { CircuitBreaker.record_success("stripe"); return r; }
  }
} else {
  return fallback_response();
}

Scope

State is process-global: every worker thread and both engines see the same circuit. Single-process by design — cross-process coordination belongs to the job system’s atomic claiming.

CircuitBreaker.configure("stripe", { "threshold": 3, "reset_after": 60 });
CircuitBreaker.state("stripe");   # "closed"