SwiftKart
The engine behind 10-minute grocery delivery
Dark-store inventory that never oversells, an order saga that survives failures, and rider dispatch with batching, all on a live map. Customers see what the nearest store has right now and get an honest ETA; operations get a city map, on-time rate and a flash-sale lab that proves the platform doesn't oversell.
- 300 buyers, 50 units, 0 refunds
- 86% on time
- Atomic cart reservation
- Fenced leader lease
§1
The problem
Quick commerce runs on thin margins. When 300 people tap Buy for 50 units, check-then-decrement code takes everyone's money and refunds most of them. A '10 minutes' promise that ignores the picking queue becomes a late order, and riders are the biggest cost.
§2
My role
Personal project, end to end: the inventory, order and dispatch services, the saga and reconciler, rider assignment and batching, the React store and ops console with a live map, and Kubernetes manifests.
§3
Architecture
§4
Key decisions
D/01
A whole cart in one Lua script
Check-and-decrement for every line is atomic, with Redis Cluster hash tags so a store's keys share a slot. Postgres stays the ledger and can never go negative, even if Redis is wrong.
D/02
Orchestrated saga with an outbox
One service owns the order state machine; reservations expire after 120 s through a sorted set any replica can sweep, and status only moves forward with optimistic locking.
D/03
Honest delivery promises
Promise = picking + queue ahead + handover + ride, with the clock starting at payment. Queue position comes from an atomic per-store Redis gauge, not a database count.
D/04
Dispatch on a GEO index
Rider candidates come from a Redis GEO radius query, scored by time-to-store minus a fairness bonus for idle time. Two drops within 1.2 km and a 4-minute detour share one trip.
D/05
One leader for the fleet
A fenced Redis lease with compare-and-extend renewal makes exactly one dispatch replica move riders, and fleet state is snapshotted so a new leader resumes seamlessly.
§5
Challenges
- The flash-sale lab: the atomic path told exactly 50 of 300 buyers 'reserved' with 0 refunds every run; the naive path told 174–300 and refunded 124–250. The ledger still refused to go negative, and the reconciler repaired the phantom stock.
- During a flash sale 50 checkouts in the same second each saw an empty queue and were promised 7 minutes (actual: 25). Joining a sorted set now returns each buyer's true position, so promises range from 7 to 25 minutes.
- The first flash sale after a deploy timed out on cold paths, and 300 simultaneous connections overflowed Tomcat's default backlog of 100. Services now warm up and size the accept queue for bursts.
- Kafka events arrive out of order (a rider assigned while still packing), so late events can't move an order backwards, and processed_events gives exactly-once effects.
§6
Stack
- Java 21
- Spring Boot 4
- Spring Cloud Gateway
- Kafka
- Redis (Lua, GEO, pub/sub)
- PostgreSQL
- React
- Leaflet
- Kubernetes
Next case study
TorrentShare
Peer-assisted artifact distribution for GPU fleets and MLOps pipelines