<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Qasim Lak</title>
    <description>The latest articles on DEV Community by Qasim Lak (@qasimlak).</description>
    <link>https://dev.to/qasimlak</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4136066%2Ff3e10a11-dd99-49d7-b52c-e3ef6e90a08f.png</url>
      <title>DEV Community: Qasim Lak</title>
      <link>https://dev.to/qasimlak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/qasimlak"/>
    <language>en</language>
    <item>
      <title>Why "Check Then Write" Booking Logic Breaks Under Real Traffic (And How to Fix It in PostgreSQL)</title>
      <dc:creator>Qasim Lak</dc:creator>
      <pubDate>Tue, 22 Sep 2026 18:06:34 +0000</pubDate>
      <link>https://dev.to/qasimlak/why-check-then-write-booking-logic-breaks-under-real-traffic-and-how-to-fix-it-in-postgresql-4n2f</link>
      <guid>https://dev.to/qasimlak/why-check-then-write-booking-logic-breaks-under-real-traffic-and-how-to-fix-it-in-postgresql-4n2f</guid>
      <description>&lt;p&gt;Most booking and reservation systems check-availability-then-write as two separate steps. It looks fine in every demo. It breaks the first time two people click "Confirm" within the same second — which, if your product has any real usage, happens constantly during peak times.&lt;/p&gt;

&lt;p&gt;I ran into this building a booking engine for Pakistani wedding venues, where the cost of a bug isn't a support ticket — it's two families showing up for the same hall on the same night.&lt;/p&gt;

&lt;p&gt;The race condition, concretely&lt;br&gt;
Time 0ms:   Request A checks hall availability → sees "free"&lt;br&gt;
Time 5ms:   Request B checks hall availability → sees "free"&lt;br&gt;
Time 10ms:  Request A writes booking → succeeds&lt;br&gt;
Time 12ms:  Request B writes booking → also succeeds&lt;/p&gt;

&lt;p&gt;Both bookings look valid. Nothing crashed. You just sold the same slot twice.&lt;/p&gt;

&lt;p&gt;Why this still catches people in 2026&lt;/p&gt;

&lt;p&gt;Modern stacks (Next.js Server Actions, edge functions, serverless) make it easier to accidentally spread a "check" and a "write" across different requests or even different regions, which makes this race condition more likely, not less, compared to a single monolithic server handling one request at a time.&lt;/p&gt;

&lt;p&gt;The fix: push the guarantee into the database, not the app&lt;/p&gt;

&lt;p&gt;Application-level checks (if (available) { book() }) can't prevent this — by definition, two parallel requests both pass the check before either writes. The fix has to live where writes are serialized:&lt;/p&gt;

&lt;p&gt;Row-level locking (SELECT ... FOR UPDATE) so the second transaction waits for the first to finish before it even reads.&lt;br&gt;
A unique constraint on (hall_id, date, timeslot) as your last line of defense — even if your locking logic has a bug, the database itself refuses the duplicate.&lt;br&gt;
Idempotency keys on the write endpoint, so a retried request (flaky network, double-click) can't create a duplicate on its own.&lt;/p&gt;

&lt;p&gt;This is the same category of problem seat-selection systems and ticketing platforms solve, just at smaller scale.&lt;/p&gt;

&lt;p&gt;I'm Qasim Lak, a software engineer and web developer in Islamabad, currently building production-style systems around booking concurrency, network telemetry, and offline-first mobile sync. More at qasimlak.me.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>database</category>
      <category>sql</category>
    </item>
  </channel>
</rss>
