Skip to content

fix(events): handle P2002 slug conflicts from concurrent event creation#406

Open
Ridanshi wants to merge 1 commit into
Dev-Card:mainfrom
Ridanshi:fix/event-slug-p2002-race
Open

fix(events): handle P2002 slug conflicts from concurrent event creation#406
Ridanshi wants to merge 1 commit into
Dev-Card:mainfrom
Ridanshi:fix/event-slug-p2002-race

Conversation

@Ridanshi
Copy link
Copy Markdown
Contributor

Closes #311

Problem

Event creation currently performs slug uniqueness checks using a read-before-write pattern.

A slug is first checked for availability and then later used during event creation. Under concurrent requests creating events with the same name, multiple requests can observe the slug as available and attempt insertion simultaneously.

This creates a race condition where one request succeeds and the other fails with a Prisma P2002 unique constraint violation. The failure is currently surfaced as a generic 500 Internal Server Error.

Root Cause

The issue stems from a classic time-of-check vs time-of-use (TOCTOU) race condition.

Current flow:

  1. Generate candidate slug.
  2. Check whether slug already exists.
  3. Attempt event creation.

Under concurrent execution:

  1. Multiple requests generate the same slug.
  2. Multiple requests observe the slug as available.
  3. Multiple requests attempt insertion.
  4. One succeeds.
  5. Others receive P2002 unique constraint errors.

Additionally:

  • Event creation did not explicitly handle Prisma P2002 errors.
  • Slug generation used an unbounded retry loop.
  • Several related event route tests exposed reliability issues in authentication and attendee-count handling.

Solution

Implemented deterministic handling for concurrent slug creation conflicts.

Event Creation

  • Added explicit handling for Prisma P2002 unique constraint violations.
  • Retry event creation when a slug collision occurs.
  • Ensure concurrent requests receive predictable behavior instead of generic 500 responses.

Slug Generation

  • Removed unbounded retry behavior.
  • Added bounded retry logic to prevent infinite loops.
  • Preserved existing slug-generation behavior for normal usage.

Route Reliability Improvements

  • Standardized authentication handling across event routes.
  • Fixed authenticated request processing so user information is available consistently.
  • Hardened attendee-count handling for mocked and partial event responses.

Changes

apps/backend/src/routes/event.ts

  • Added explicit Prisma P2002 handling.
  • Added retry-on-conflict creation flow.
  • Improved authentication consistency.
  • Improved event route error handling.

Slug generation flow

  • Added bounded retry protection.
  • Improved collision handling under concurrent requests.

Test Suite

  • Updated event route tests.
  • Added regression coverage for concurrent insert scenarios.
  • Added conflict-handling tests.
  • Added retry-path validation.
  • Added authentication and attendee-count coverage.

Tests

Event test suite results:

  • Previous state: 7 passing / 29 failing
  • Current state: 42 passing / 0 failing

New coverage includes:

  • Normal event creation
  • Existing slug collisions
  • Prisma P2002 handling
  • Retry behavior
  • Concurrent same-name event creation
  • Bounded retry exhaustion
  • Authentication paths
  • Attendee route handling
  • Regression coverage for the original race condition

Impact

Low risk.

The fix preserves existing slug behavior while making event creation resilient under concurrent requests.

Users creating events normally will see no behavioral changes.

Under concurrent creation scenarios, slug conflicts are now handled deterministically rather than returning unexpected 500 responses.

Verification

  • Concurrent same-name event creation no longer returns generic 500 errors.
  • Prisma uniqueness conflicts are handled explicitly.
  • Slug uniqueness guarantees are preserved.
  • Retry behavior is bounded.
  • Regression tests reproduce and verify the original failure scenario.
  • Event creation remains reliable under concurrent usage patterns.

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 Dev-Card#311
@Harxhit Harxhit added the gssoc:approved Required label for every approved PR. Gives the base +50 points and enables contribution tracking. label May 30, 2026
@Prince2301p
Copy link
Copy Markdown

Hi,
I would like to work on resolving this issue. Please assign it to me. I’m a participant in GSSoC 2026 and would be happy to contribute.
Looking forward to collaborating with you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved Required label for every approved PR. Gives the base +50 points and enables contribution tracking.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Concurrent event creation can trigger Prisma P2002 slug conflicts and return 500 errors

3 participants