In Postgres, I want to be able to confirm users' RSVPs for an event that has limited capacity. How do I do this in a way that protects against race conditions?
I have a table event_attendance
with columns event_id
, user_id
, rsvp_date
, and confirmed
. I want to insert a row into this table for a given user, so that only the first N
users can RSVP.
Only if the inserted row is one of the first N
rows matching where event_id = $eventId
should the confirmed
column be set to true
.
I'm not sure if this needs a transaction, but I would love to do it in a more lightweight atomic way, using something like insert ... limit ($N - (select count(id) from event_acceptance where event_id = $eventId))
or similar (I really don't know what I'm doing here...).
Also, I need to be able to do an upsert (insert ... on conflict)
, because the attendance in the real application is a bit more complex than this (confirmed
is not a boolean, it is null
(not going), 'interested'
, or 'going'
, and I need to be able to update this record, subject to the capacity constraints of the event, whether or not there is an existing event_attendance
record).
And finally, it would be great if I could do a single update in time O(log N)
, not time O(N)
, i.e. use an index to determine what the current total row count is that matches the given where
clause, rather than a linear count.