Uzair Manan
Case Study
All projects

FluxBuy Drop engine

A flash-sale backend where hundreds of buyers hit a small stock count at the same second, against a payment provider that is slow and fails on purpose.

Role
Solo, backend
Timeline
Jun – Oct 2026
Stack
NestJS · Redis · PostgreSQL · BullMQ
Status
Complete, load-tested
The Problem

Many buyers few units

Sellers list a product with a fixed quantity and a drop time. At that second it becomes buyable by everyone at once. The CRUD around that is easy. What isn't: hundreds of requests reading and decrementing the same stock counter, and a payment step that takes one to four seconds and fails 20% of the time.

  • Nothing gets oversold, at any concurrency.
  • Nobody gets charged twice, including when a client retries.
  • Stock never disappears if a worker dies in the middle of a charge.
  • The mock payment provider runs in the same process, so after a crash there is nothing external to ask. Recovery can only apply a policy.
900
Concurrent
winners
0
Units
oversold
1
DB query per
cold burst
The Build

Off the hot path

  1. Stock lives in Redis

    A Lua script checks and decrements the stock key in one atomic step. Postgres is an async projection: a winning request touches it twice, one read and one insert, and never for stock. A sync worker writes the Redis counts back every 60 seconds.

  2. Payment is a queue, not a call

    The order is created as PENDING and the client gets an answer in milliseconds. A BullMQ worker charges the card, and on failure puts the stock back and notifies anyone on the restock waitlist.

  3. Every actor claims before it acts

    The payment worker and the cleanup worker can both decide the fate of the same order. Each one moves it to PROCESSING with a conditional update and only continues if that update matched a row.

  4. Built from scratch

    A Redis HTTP cache with stampede protection and stale-while-revalidate, a sliding-window rate limiter in one Lua script, and idempotent orders keyed by an idempotency key.

The Race

Two workers one order

Early versions of both workers wrote the final status unconditionally, keyed only by order id. Whichever ran second overwrote the first, which meant either stock restored twice or a real oversell.

The fix keeps the claim in Order.status. Nothing else needs to stay in sync with it, so losing Redis entirely still leaves a working state machine. A test script starves the payment queue so jobs sit past the cleanup window and the collision has to happen. At 400 buyers against 400 units, cleanup swept 127 orders mid-queue, the payment worker backed off on all 127, and the books closed at 192 left plus 208 paid.

const { count } = await prisma.order.updateMany({
  where: { id: order.id, status: 'PENDING' },
  data: { status: 'PROCESSING' },
});
if (count === 0) return; // cleanup got here first
Outcome

Where it landed

The first synchronous version held a row lock on the stock row inside a transaction and stopped at about 70 concurrent buyers. The current one has been run at 900 concurrent winners against 900 units with zero oversold. Every number on this page comes from a script in the repo.

What it doesn't do yet is written down in the README: locks are single-Redis with no Redlock quorum, the cache has no invalidation on write, and correctness is proven by load scripts rather than a test suite.