Back to path
IntermediateTradepost · Project 5 of 12 ~7h· 5 milestones

Make writes safe: transactions, locks and idempotency

Continues from the last build: Every request is authenticated and every row is scoped to its merchant.

A merchant ran a flash sale on a limited drop, 5 units of a sneaker.

Postgres row locking with SELECT FOR UPDATEatomic single-statement stock decrementswrapping multi-table writes in one db.transactiondesigning an idempotency key table with unique constraintsreplaying stored responses for safe retriesenforcing a state machine at the domain layer and the database layerwriting a concurrency reproduction scriptreading Postgres lock and deadlock output

What you'll build

A checkout endpoint that survives 20 concurrent requests against 5 units of stock (exactly 5 succeed, 15 get a clean 409), replays the exact same response for a retried Idempotency-Key, rejects a reused key with a different payload as 422, and enforces the order state machine in the database as well as in code so an invalid transition is a 409 no matter which code path tried it.

See how we teach, before you sign up

You don't just get code dumped on you. Every starter file and every solution is explained line-by-line, in plain English. Here's one real file from this project:

scripts/race-checkout.tstypescript
// Fires N concurrent checkout requests and reports how many succeeded.
const API_URL = process.env.API_URL ?? 'http://localhost:3000';
const PRODUCT_ID = process.env.PRODUCT_ID ?? '<seed-a-product-and-paste-its-id>';
const CONCURRENCY = 20;

async function checkout(i: number) {
  const res = await fetch(`${API_URL}/v1/orders`, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      authorization: `Bearer ${process.env.API_KEY}`,
    },
    body: JSON.stringify({
      customerId: 'race-test-customer',
      items: [{ productId: PRODUCT_ID, qty: 1 }],
    }),
  });
  return { i, status: res.status };
}

async function main() {
  const attempts = Array.from({ length: CONCURRENCY }, (_, i) => checkout(i));
  const results = await Promise.allSettled(attempts);
  const succeeded = results.filter(
    (r) => r.status === 'fulfilled' && r.value.status === 201,
  ).length;
  console.log(`succeeded: ${succeeded} / ${CONCURRENCY}`);
}

main();

Reading this file

  • const CONCURRENCY = 20;matches the brief's 20 concurrent requests against 5 units of stock
  • Promise.allSettled(attempts)allSettled, not all, so a rejected request does not stop you from counting the others
  • r.value.status === 201counts only true successes, a 409 or 422 is correctly excluded from the success tally

Run this unchanged before and after each fix milestone, it is your one source of truth for whether the bug is gone.

That's 1 of 7 explained code blocks in this single project.

The build, milestone by milestone

  1. 1

    Reproduce the oversell with a concurrency script

    5 guided steps

    You cannot prove a fix works if you never measured the bug. A race condition that you cannot reproduce on demand is a race condition you cannot verify you fixed. This milestone gives you the baseline number every later milestone is judged against.

  2. 2

    Fix the stock decrement with a lock or an atomic update

    6 guided steps

    Two statements can never be made atomic by adding more application code around them, only the database can guarantee that a read and a write happen without another transaction sneaking in between. This is the difference between an API that is correct under load and one that is correct in demos.

  3. 3

    Make the whole order write atomic

    6 guided steps

    An order with line items but no payment intent, or a payment intent with no order, is a support nightmare and sometimes a compliance one. The database's transaction boundary is the only thing that can promise all-or-nothing across four tables in one request.

  4. 4

    Add idempotency keys so retries are safe

    7 guided steps

    Storefronts and mobile clients retry on timeout, that is correct behavior on their end, but without an idempotency layer every retry is a brand new order to your database. The fix belongs in the same transaction as the order write, not as an in-memory cache that forgets everything on deploy or disagrees across two API instances.

  5. 5

    Enforce the order state machine in code and in the database

    6 guided steps

    Application-level validation is only as strong as every code path that writes to the table. A backfill script, a future teammate's one-off query, or a bug in a different route can all write a bad status directly. A database-level backstop makes the invalid state physically unrepresentable, which is a much stronger guarantee than 'we remembered to check'.

What's inside when you start

3 starter files, ready to clone
5 guided milestones
4 full reference solutions
7 code blocks explained line-by-line
5 "is it working?" checks
6 interview questions it prepares you for

You'll walk away with

scripts/race-checkout.ts reproducing the oversell with a measured before number and a measured after number of exactly 5
src/domain/stock.ts using an atomic UPDATE ... WHERE stock >= qty RETURNING for the stock decrement
the order-creation route wrapping stock reservation, order, order_items and payment inserts in one db.transaction, proven atomic by a kill-mid-write test
idempotency_keys table plus src/lib/idempotency.ts enforcing replay-on-match and 422-on-mismatch
src/domain/order-state.ts plus a matching Postgres trigger enforcing the pending -> paid -> fulfilled state machine on both code paths
tests/integration covering the concurrent-checkout, idempotency-replay and invalid-transition scenarios

This is portfolio-grade. Build it free.

Sign up to unlock every milestone step-by-step, the code skeletons, full reference solutions, and checkable tasks, with your progress saved as you build.

Start building