NorthwindNorthwind
← All posts

Designing an Idempotent Job Queue

At-least-once delivery means your jobs will run twice. Design for it instead of hoping it won't happen.

Maya Chen, Raj Patel · 1 min read
Share

Every durable queue we've used promises at-least-once delivery. That's a polite way of saying "sometimes twice." If your job isn't idempotent, retries corrupt data.

The idempotency key

Give each job a stable key and record completion atomically with the work:

async function runOnce(jobKey: string, work: () => Promise<void>) {
  const inserted = await db.query(
    "INSERT INTO job_runs (key) VALUES ($1) ON CONFLICT DO NOTHING",
    [jobKey],
  );
  if (inserted.rowCount === 0) return; // already ran
  await work();
}

The database row that proves a job ran is more valuable than the job's result. Write it in the same transaction as the side effect, or you've built a race.

Share

More to read

Related posts