This repository demonstrates how inventory reservation can suffer from a double-booking race condition and provides the groundwork for iterating toward a safe solution using Next.js, PostgreSQL, and Redis.
Use this sandbox to observe lost updates under concurrent load and to experiment with mitigation techniques such as optimistic locking, transactional guards, and distributed locks.
- Race-condition reproduction – The
POSThandler insrc/app/api/buy-bad/route.tsintentionally reads, waits, and updates inventory to showcase lost updates. - Load generation –
src/app/api/api-test/route.tsfloods the bad purchase endpoint with concurrent requests to surface the issue. - Inventory model – The
inventoryTableschema is backed by Postgres and wired throughsrc/lib/db.ts. - Redis initializer –
src/app/api/create-inventory-key/route.tsseeds the cache viasrc/lib/redis.ts. - Health probe –
src/app/api/health/route.tsoffers a lightweight readiness check. - Baseline UI –
src/app/page.tsxandsrc/app/layout.tsxhost a minimal front end scaffold.
| Layer | Description |
|---|---|
| Next.js App Router | API routes live under src/app/api, while shared styling resides in src/app/globals.css. |
| Database | src/lib/db.ts instantiates Drizzle ORM against the connection defined in drizzle.config.ts. |
| Persistence | The inventory table structure is declared in src/db/schema.ts, and migrations target src/db/migrations. |
| Cache | src/lib/redis.ts connects to Redis using the URL provided at runtime. |
| Method | Path | Purpose |
|---|---|---|
GET |
/api/health |
Returns service liveness via src/app/api/health/route.ts. |
POST |
/api/buy-bad |
Reproduces the race by decrementing inventory without safeguards in src/app/api/buy-bad/route.ts. |
POST |
/api/buy-good |
Placeholder for mitigated logic in src/app/api/buy-good/route.ts. |
GET |
/api/api-test |
Launches 100 concurrent bad purchases via src/app/api/api-test/route.ts. |
GET |
/api/create-inventory-key |
Seeds Redis inventory keys in src/app/api/create-inventory-key/route.ts. |
- Node.js 18+
- PostgreSQL database reachable via
DATABASE_URL - Redis instance reachable via
REDIS_URL - PNPM (recommended) or any package manager referenced in package.json