Skip to content

Commit 771b16f

Browse files
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 #311
1 parent 1b66bad commit 771b16f

3 files changed

Lines changed: 472 additions & 289 deletions

File tree

0 commit comments

Comments
 (0)