Retry
The retry subpath is a lazy-loaded retry orchestrator. It is intentionally
separate from the core entry so customers who never hit a transient
failure path do not pay for it. Bundle target: ≤ 1.5 kB gzip.
Install
Section titled “Install”import { runWithRetry } from '@syntarie/tracking/retry';
await runWithRetry(events, async (batch) => { const res = await fetch(host + '/events', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(batch[0]), }); return res.ok || res.status === 202;});The attempt callback returns true to signal “stop retrying”. Returning
false (or throwing) triggers the next backoff step.
Backoff schedule
Section titled “Backoff schedule”| Attempt | Delay before |
|---|---|
| 1 | 1 s |
| 2 | 2 s |
| 3 | 4 s |
| 4 | 8 s |
| 5 | 60 s (cap) |
Each delay carries a multiplicative ±25 % jitter to spread retries across clients (avoids the thundering-herd reconnect after a network blip). After 5 retries the orchestrator surrenders.
Exhaustion handler
Section titled “Exhaustion handler”await runWithRetry(events, attempt, { onExhausted: (events) => persistToBackup(events),});onExhausted lets you hand the unsent events to a fallback (e.g. the
offline queue) instead of dropping them.
Dedup key
Section titled “Dedup key”On the first attempt, the orchestrator stamps each event with a stable
dedup_key derived from (id, type, anon_id, ts). Subsequent retries reuse
the same key. The collector dedup window is 5 minutes — replays inside that
window are silently absorbed and the response is still 202. This is what
lets the retry module behave optimistically: a 202 returned to a retry of
a previously-accepted event is the right answer, not a double-write.
When to use this manually
Section titled “When to use this manually”In v1.0 the SDK transport already wraps runWithRetry internally for the
default send() pipeline. Direct use of runWithRetry is for advanced
cases:
- You have built a custom transport (e.g. WebSocket, Service Worker) and want the same backoff semantics.
- You are sending events outside the
send()flow (e.g. flushing a custom buffer onpagehide).
For 99 % of customers the default transport’s retry is enough.