DEV Community

Korum: Detect-and-Refund vs. Prevent - A Concurrency Tradeoff

Why I Didn't Try to Make Double-Booking "Impossible" - I Made It Cheap to Reverse

A common instinct when building any capacity-constrained booking system (event slots, appointment times, seats) is to reach for a hard database constraint that makes overbooking structurally impossible. I want to walk through why I didn't do that for Korum, and what I built instead.

The setup

Korum coordinates casual sports matches. A match has a fixed capacity. When it's full, further joins should fail cleanly.

What "impossible" would actually require

Making concurrent overbooking truly impossible under high contention generally means serializing writes at the database level:

  • A unique constraint on a (match_id, slot_number) pair with slot numbers assigned atomically; or
  • Row-level locking on the match capacity row during the write.

Both are legitimate approaches. Both also add real complexity: lock contention under load, or careful slot-assignment logic that has to be right in every code path that touches it.

What I built instead

Application code performs a capacity check before creating a payment hold - a soft check, not a guarantee. The actual enforcement happens through database routines that confirm a paid participant atomically; if an overflow case slips through the soft check anyway, it's caught at confirmation time and the payment is marked for refund review, rather than silently accepted as a valid booking.

In plain terms: overbooking isn't prevented from ever occurring - it's detected immediately after, and reversed, rather than left for someone to discover manually later (e.g., a captain finding 12 confirmed players for an 11-player match on Sunday morning).

Why this tradeoff, specifically

The cost of a failure matters as much as the probability of it. In this domain, the failure mode of "briefly overbooked, then refunded" is low-cost: an apology, a refund, and a fix, versus the engineering cost of building and maintaining stricter locking for what's a genuinely rare race condition given realistic traffic patterns for a weekend-sports coordination app.

I'd build this differently for something with a higher failure cost - a single-seat airline booking, say, where "sorry, we double-sold your seat" isn't an acceptable outcome even rarely. For Korum, detect-and-refund is the right tradeoff.

The other half: idempotent webhook handling

Payment confirmations arrive via a Razorpay webhook, and webhooks aren't guaranteed exactly-once - network retries can cause the same event to fire more than once. The handler verifies incoming signatures and writes confirmations in a way designed to be safe under repetition: a duplicate delivery of the same event doesn't produce a duplicate effect.

Curious how others have reasoned about prevention vs. detection-and-recovery tradeoffs for similar capacity-constrained systems - particularly where the "acceptable failure cost" line sits for you.

Comments

No comments yet. Start the discussion.