Commit 771b16f
committed
fix(events): handle P2002 slug conflicts from concurrent event creation
Root cause
----------
generateUniqueSlug() checked slug uniqueness with a findUnique() read
before calling event.create(). Two concurrent requests for an event with
the same name could both observe the slug as available, pass the pre-check,
and race to insert. The loser received Prisma error P2002 (unique constraint
on the slug column). The catch block in the POST handler treated every error
as a generic 500, so callers had no way to distinguish a transient slug
collision from a real database failure, and the request was not retried.
A secondary issue: generateUniqueSlug() used while(true) with no exit
bound, making an infinite loop possible if the slugExists callback always
returned true.
Fix
---
slug.ts
- Replace while(true) with a for loop bounded by MAX_SLUG_RETRIES (10).
- Add a final fallback attempt using a longer random suffix before
throwing, so the bound is always honored.
- Export MAX_SLUG_RETRIES so callers and tests can reference it.
event.ts
- Wrap slug generation + event.create in a for loop bounded by
MAX_CREATE_ATTEMPTS (5).
- Catch P2002 specifically: regenerate the slug and retry. Any other
error exits immediately as a 500 — no unnecessary retries.
- Replace the inline ad-hoc authentication preHandler on POST, join,
and leave with the standard preHandler: [app.authenticate] pattern
used by every other route plugin in the codebase. The previous
workaround left request.user unpopulated after a successful jwtVerify()
call, causing all authenticated routes to throw on (request.user as
any).id and return 500.
event.test.ts
- Fix buildApp() to decorate app.authenticate (sets request.user),
matching the preHandler contract used by the fixed routes.
- Add _count to all attendees-endpoint mocks (previously missing, causing
event._count.attendees to throw and return 500).
- Add 6 new tests covering: P2002 retry success on second attempt,
multiple consecutive P2002 retries, retry budget exhaustion → 500,
no retry on non-P2002 errors, concurrent same-name requests, and
the end-to-end regression scenario where a TOCTOU collision is
resolved by a suffixed slug on the retry attempt.
- Fix all pre-existing test failures (29 → 0 failures; 42 tests pass).
Closes #3111 parent 1b66bad commit 771b16f
3 files changed
Lines changed: 472 additions & 289 deletions
0 commit comments