PushStack Docs
git-fs-s3

Retry & circuit breaker

createRetryStore's exponential backoff and per-instance circuit breaker.

createRetryStore wraps any ObjectStore with exponential-backoff retries and a circuit breaker. Place it directly above the network store, under the cache — see the production stack guide for why that order matters.

createRetryStore(store: ObjectStore, options?: RetryOptions): ObjectStore
ParameterTypeDescription
storeObjectStoreThe store to wrap.
options?RetryOptionsSee below.

Backoff options

OptionDefaultWhat it does
retries3Attempts after the first (total attempts = retries + 1).
initialDelayMs100Base delay, doubled every attempt.
maxDelayMs5000Upper bound on the backoff delay.
jitter0.3Random jitter added to each delay, as a fraction of it.
isRetryablesee belowDecide whether an error is worth retrying.

The store contract already maps "not found" to null rather than throwing (see the ObjectStore guide) — so any thrown error here is a genuine failure. The default isRetryable retries network faults, throttling, and HTTP 5xx/429 responses; anything else (a 4xx that isn't throttling, a programming error) fails immediately.

The circuit breaker

createRetryStore(store, {
  breaker: { threshold: 5, resetMs: 30_000 }, // defaults shown
});

After threshold consecutive retryable failures, the store stops even trying — every call fails fast with CircuitOpenError (code: "EUNAVAILABLE") for resetMs, then lets exactly one request through to probe whether the backend has recovered (concurrent calls keep failing fast until that probe settles). Any success resets the count, and errors your isRetryable rejects (a 403, a malformed request) never count — they say nothing about the backend's health.

Fail-fast during an outage matters as much as retrying during a blip — without a breaker, a downed backend means every single request in flight pays the full retry+backoff cost before failing, which can turn a backend outage into a self-inflicted request pile-up.

Set breaker: false to disable it entirely.

Observability

createRetryStore(store, {
  onRetry: (info) => console.warn('retrying', info),
});

onRetry fires on every retry attempt — wire it into your existing logging/ metrics pipeline rather than debugging retries blind.

On this page