Skip to content

Idempotent Handlers

Chronos guarantees at-least-once delivery. That means your handler may run more than once for the same job:

  • A timeout occurs but your handler actually completed (late result report)
  • A network partition prevents the result from reaching Chronos
  • Push delivery: your endpoint processed the request but responded too slowly

If your handler charges a credit card, sends an email, or creates a record, running it twice without protection produces duplicates.

FieldScopeUse when
ctx.jobIdSame across retriesSide effects that must happen once per job (payments, emails, record creation)
ctx.executionIdUnique per attemptPer-attempt tracking (logging, metrics, attempt-specific records)

Use jobId by default. It stays the same across retries, so if a retry fires after a side effect already succeeded, downstream systems detect the duplicate and skip it.

Use executionId only when each retry attempt should be treated as distinct (per-attempt logs, metrics, or audit records).

Pass the job ID to APIs that support idempotency:

chronos.worker.handle<{ customerId: string; amount: number }>('charge', async (ctx) => {
await stripe.charges.create({
amount: ctx.payload.amount,
currency: 'usd',
customer: ctx.payload.customerId,
idempotency_key: ctx.jobId,
});
return { charged: true };
});

Stripe (and many other payment APIs) will reject duplicate charges with the same idempotency key. Because jobId stays the same across retries, a timed-out attempt that actually succeeded won’t cause a second charge.

Not every handler needs dedup. If the operation is naturally idempotent, you’re already safe:

  • Read-only operations: fetching data, generating reports from current state
  • Full-state overwrites: UPDATE users SET last_seen = now() WHERE id = ?
  • Upserts: INSERT ... ON CONFLICT DO UPDATE with the same data
  • Deleting by ID: deleting something that’s already gone is a no-op

The rule: if running twice produces the same end state with no extra side effects, you’re fine without explicit dedup.