Two quick votes on the same time slot: the second is a 500 (upsert race) #844

Closed
opened 2026-09-26 15:32:56 +00:00 by claude-bot · 1 comment
Collaborator

Found in lane 7's journey runs (J2 keyboard, and once on desktop): two PUT /api/…/timeslots/{id}/vote for the same slot and user arrive together. Both try to insert, the second hits uq_votes_slot_user, and vote_service.upsert_vote lets the IntegrityError through as a 500. The grid then shows a save failure.

The fix:

  • make upsert_vote race-safe with a Postgres INSERT … ON CONFLICT (slot_id, user_id) DO UPDATE, or catch the IntegrityError and retry as an update;
  • a test with two concurrent upserts, both succeeding with the last write winning;
  • optionally, the grid ignores a second press while one is saving.

Lane 4's code (#730).

Found in lane 7's journey runs (J2 keyboard, and once on desktop): two `PUT /api/…/timeslots/{id}/vote` for the same slot and user arrive together. Both try to insert, the second hits `uq_votes_slot_user`, and `vote_service.upsert_vote` lets the IntegrityError through as a **500**. The grid then shows a save failure. **The fix:** - make `upsert_vote` race-safe with a Postgres `INSERT … ON CONFLICT (slot_id, user_id) DO UPDATE`, or catch the IntegrityError and retry as an update; - a test with two concurrent upserts, both succeeding with the last write winning; - optionally, the grid ignores a second press while one is saving. Lane 4's code (#730).
Author
Collaborator

Fixed by #847, merged 2026-09-26.

The fix: upsert_vote is now a single INSERT … ON CONFLICT ON CONSTRAINT uq_votes_slot_user DO UPDATE, so the second writer updates the first one's row.

The test: a concurrency test drives two real sessions over separate connections, and both succeed. Before the fix it failed with the exact IntegrityError.

The optional frontend guard (ignoring a second press while a save is in flight) wasn't needed, since the server is now race-safe.

Fixed by #847, merged 2026-09-26. **The fix:** `upsert_vote` is now a single `INSERT … ON CONFLICT ON CONSTRAINT uq_votes_slot_user DO UPDATE`, so the second writer updates the first one's row. **The test:** a concurrency test drives two real sessions over separate connections, and both succeed. Before the fix it failed with the exact IntegrityError. The optional frontend guard (ignoring a second press while a save is in flight) wasn't needed, since the server is now race-safe.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Quest-Board#844
No description provided.