sync-retry
Sync Retry
Minimal, transport-aware retry wrapper. Prefer retry(fn) with defaults in almost all call sites.
Decision Guide
- One-shot Redis/network call:
retry(() => call())— defaults are sufficient. - Blocking stream loop (queue/topic/ephemeral reader): use
attempts: Number.POSITIVE_INFINITY+signalso the loop retries indefinitely until aborted. - Business/domain error: do NOT wrap with retry. Retry is only for transient transport errors.
Gotchas
retryIfdefault (isRetryableTransportError) matches error codes (ECONNRESET,ETIMEDOUT,ECONNREFUSED,ENOTFOUND,EPIPE,EHOSTUNREACH,ECONNABORTED) and message substrings (connection,socket,broken pipe,network,clusterdown,tryagain,loading). Any error not matching is thrown immediately.signal.abortedis checked before each attempt — no wasted work after cancellation.- Library internals (queue/topic/ephemeral
stream({ wait: true })) already use retry with unbounded attempts. Do not double-wrap those calls. attempts: 1means no retry — only one try. The default is8.
Browser
More from valentinkolb/sync
sync-job
Use this skill when implementing durable background jobs with @valentinkolb/sync job: defining typed process handlers with ctx.step/ctx.heartbeat/ctx.signal, submit/join/cancel flows, idempotent submission via key, retries with exponential backoff, lease timeouts, and per-job event streams for audit. Also use when choosing between job (durable execution with state) vs queue (simple work distribution). Also works in the browser via `@valentinkolb/sync/browser` with in-memory state — same API, no Redis needed.
9sync-ephemeral
Use this skill when implementing short-lived typed state with @valentinkolb/sync ephemeral: TTL-based key/value with upsert/touch/remove, snapshot-plus-cursor reconciliation for cache hydration, streaming upsert/touch/delete/expire events, capacity/payload limits, and presence-style use cases where entries should naturally expire. Also works in the browser via `@valentinkolb/sync/browser` with in-memory state — same API, no Redis needed.
9sync-scheduler
Use this skill when implementing distributed cron scheduling with @valentinkolb/sync scheduler: registering idempotent schedules across pods, leader-fenced dispatch via job.submit, misfire policy (skip/catch_up_one/catch_up_all), triggerNow for manual dispatch, unregister, metrics/health, and multi-pod leader election. Depends on sync-job for execution and sync-mutex for leader lock. Also works in the browser via `@valentinkolb/sync/browser` for single-tab cron scheduling — same API, no Redis needed.
9sync-topic
Use this skill when implementing event streams with @valentinkolb/sync topic: publishing typed events with idempotency, consumer-group processing with commit for at-least-once delivery, live replay from any cursor, retention tuning, and multi-tenant stream isolation. Also use when choosing between topic (pub/sub events) vs queue (work distribution). Also works in the browser via `@valentinkolb/sync/browser` as an in-memory event bus — same API, no Redis needed.
9sync-mutex
Use this skill when working with @valentinkolb/sync distributed locks: exclusive critical sections across pods with withLock/withLockOrThrow, manual acquire/release, lease extension for long work, retry tuning, owner-safe release via Lua, and LockError handling. Also works in the browser via `@valentinkolb/sync/browser` with an in-memory store for single-tab lock coordination.
9sync-ratelimit
Use this skill when working with @valentinkolb/sync rate limiting in Bun/TypeScript: creating per-identifier sliding-window limiters, choosing check() vs checkOrThrow(), handling RateLimitError with resetIn/Retry-After headers, tuning window size, and reasoning about Redis key layout. Also works in the browser via `@valentinkolb/sync/browser` with an in-memory store — same API, no Redis needed.
9