mpyw/laravel-retry-on-duplicate-key
Automatically retries database operations when a duplicate key error occurs in Laravel. Wrap Eloquent firstOrCreate, updateOrCreate, or insert calls in a retryable transaction to safely handle race conditions and unique constraint conflicts under concurrent writes.
upsert(), queue-level retry with backoff).SQLSTATE[23000]/duplicate-key 500s as a recurring cause of failed signups or failed checkouts, this is a candidate mitigation to size against alternatives.Consider it when:
firstOrCreate-style flows, SELECT then INSERT, or INSERT ... ON DUPLICATE KEY UPDATE under contention) spread across many repositories, and you want a low-touch, drop-in mitigation while a proper refactor is scheduled.Look elsewhere, or build it yourself, when:
$job->release() with backoff and a dedupe table, not an ORM wrapper.upsert(), insertOrIgnore() plus a follow-up update, or ON CONFLICT DO NOTHING with a conditional UPDATE. Prefer the atomic operation over retrying a non-atomic one.A useful gate before adopting: grep production logs for unique-constraint exceptions over the last 30 days. If the count is near zero, this is a theoretical problem — close the ticket and revisit if it becomes real. If the count is material and concentrated in specific endpoints, fix those endpoints properly; a package-wide retry wrapper is a blunt instrument for a surgical problem.
For executives (one paragraph): This is a very small, free, MIT-licensed utility that papers over a specific class of database race condition — two simultaneous requests trying to create the same record, where one fails with a duplicate-key error. It is not a platform investment and it should not be on the roadmap. My recommendation is not to adopt it as a dependency: the repository is archived, has a single-digit user base and no maintainer, and taking it on creates an upgrade blocker and a supply-chain review item in exchange for roughly thirty lines of code we can write and own ourselves. The value it points at is real — failed signups and failed checkouts caused by concurrency are expensive and embarrassing — but the fix belongs in our own schema and retry logic, where we can measure it. Cost to replicate in-house: under a day of engineering. Cost to adopt and later unwind: a dependency audit, a framework-upgrade blocker, and an unclear owner.
For engineering leads: The package wraps upsert-style operations and retries them when a unique-key constraint is violated, up to a configurable limit. It does not make the underlying operation atomic, does not resolve deadlocks, does not deduplicate on business keys, and does not emit telemetry — so retries silently absorb contention signals you probably want to see. Before we consider it, I want a short spike: (1) quantify current duplicate-key exception volume from logs, (2) identify the top three offending write paths, (3) compare the three real options — atomic upsert(), an idempotency-key table, or an in-house retry decorator with metrics and a documented backoff. My expectation is that option three, written and owned by us, wins on every axis except time-to-first-commit, and even that gap is small. If the spike shows the problem is concentrated and low-volume, we schedule targeted fixes and skip the dependency entirely.
How can I help you explore Laravel packages today?