← All writing

Make a queued import safe to run twice

Laravel 13 / PHP 8.3+Sources checked 2026-09-14

A worker finishes a database write but dies before acknowledging the job. The queue can deliver the same job again. A handler that assumes one execution may create duplicate import rows or send the same notification twice.

Identify the operation, not the attempt

Give the import a stable operation key, such as the provider plus its event identifier. Store that key under a database unique constraint. Within an appropriate transaction, create or claim the operation and apply its database changes according to a deliberate duplicate policy.

provider event → stable operation key → unique claim
                                      → apply database change
retry of same event → existing claim → inspect completed state

A job's retry counter is not a substitute for the business operation key. A newly dispatched copy can have a fresh queue identity while representing the same upstream event.

External effects require another boundary

A local transaction cannot roll back an email already sent or a remote API write already accepted. Use the provider's idempotency mechanism where available, or persist an outbox/intended-effect record and process it with a clear delivery policy. Be honest about effects that remain at-least-once.

Test the same payload twice, a crash after the database commit, and a conflicting payload that reuses the same external key. The duplicate response should be predictable, while a mismatched replay should be visible for investigation. Queue uniqueness features can reduce duplicate dispatch; they do not by themselves prove that every side effect is exactly once.

Reference

Official documentation.