Prevent duplicate task creation when producers retry submissions, without adding external systems or breaking S3-only simplicity.
Important: This provides idempotent submit, not exactly-once effects. It guarantees the same idempotency key maps to the same task ID.
- Exactly-once task execution (still at-least-once)
- Deduping retries caused by worker crashes
- Cross-system transactions
bucket/
├── tasks/{shard}/{task_id}.json
└── idempotency/{scope}/{key_hash}.json
idempotency/{scope}/{key_hash}.json:
{
"key": "charge-ORD-123",
"key_hash": "sha256:…",
"task_id": "uuid",
"task_key": "tasks/a/uuid.json",
"created_at": "2026-01-27T12:00:00Z",
"expires_at": "2026-02-26T12:00:00Z",
"task_type": "charge_customer",
"input_hash": "sha256:…"
}queue.submit("charge_customer", input)
.idempotency_key("charge-ORD-123")
.idempotency_ttl(Duration::days(30));await queue.submit(
"charge_customer",
input,
idempotency_key="charge-ORD-123",
idempotency_ttl_days=30,
)- Compute
key_hash = sha256(scope + ":" + key). - Attempt to create
idempotency/{scope}/{key_hash}.jsonusingIf-None-Match: *. - If create succeeds:
- Create task object with
If-None-Match. - Return new task.
- Create task object with
- If create fails (already exists):
- Read existing idempotency record.
- Return the referenced task (same task ID).
Guarantee: All submissions with the same {scope, key} return the same task.
- Default scope =
task_type - Optional scope =
queue(global) - Optional custom string for advanced use
To prevent accidental misuse:
- Store
input_hashin the idempotency record. - On reuse:
- If
input_hashdiffers, return a IdempotencyConflict error. - This avoids “same key, different payload” bugs.
- If
Idempotency records are not eternal. Default: 30 days.
Retention controlled by:
- TTL in record (
expires_at) - S3 lifecycle policy on
idempotency/
If record expired and still present, it is treated as stale and can be overwritten with CAS.
- Task creation fails after idempotency record created:
- Subsequent submits read the record, then fail to load task.
- Behavior: either return error or recreate task if record marked “pending.”
- Optional: two-phase status (
status: creating|ready) in idempotency record.
- 1 extra PUT on submit
- 1 extra GET on retry
Still cheaper than duplicate task creation + duplicate downstream effects.
- Safe retries for producers
- Prevents duplicate billing, emails, or jobs due to client retries
- Maintains S3-only simplicity