Delivery in10 minutes
Home · Indiranagar, BLR-07
milk, bread, eggs
Your basket
- Fresh milk500 ml₹32
- Whole wheat bread400 g₹52
- Farm eggs6 pieces₹100
Total₹184
- Copy 1memory: empty
- Copy 2memory: empty
- Copy 3memory: empty
- Copy 4not running
Delivery in10 minutes
Home · Indiranagar, BLR-07
milk, bread, eggs
Your basket
Total₹184
Delivery in10 minutes
Home · Indiranagar, BLR-07
milk, bread, eggs
Your basket
Total₹184
none yet
One Zepto checkout, built two ways: kept in one server’s memory, or in a database any copy can read. Start it and pay, then a crash, then the 8 pm sale.
Keep app servers stateless: put the state in a shared database or cache, so any copy can serve any request, crash, or scale. Keep state on a server only when you must, like an open connection.
| Difference | Stateful | Stateless |
|---|---|---|
| Where the state lives | In the server’s own memory | In a shared database or cache |
| Who can serve you | Only the copy holding your state | Any copy |
| Load balancer | Sticky sessions: each user pinned to a copy | Least busy copy, every request |
| When a copy dies | Everything in its memory is lost | Nothing is lost: another copy carries on |
| Adding copies | Helps only new users; pinned ones stay put | Every copy takes traffic at once |
| The cost | No extra trip: the state is right there | A read or write to the store on each step |
| Pick it for | What can’t leave a server, like an open WebSocket | App servers: checkouts, sessions, carts |
One Zepto checkout, built two ways. On the left, stateful servers: a copy keeps the checkout in its own memory. On the right, stateless servers: the copies keep nothing, and the checkout lives in a shared database. Let’s put both through three situations.
First, a checkout takes two requests: start it, then pay. The shopper starts one, and the load balancer sends it to copy 2, the least busy. Copy 2 keeps the checkout in its own memory.
Then the shopper taps Pay. The load balancer picks the least busy copy again, this time copy 1. But copy 1 has never seen this checkout, so the payment fails.
The fix is a sticky session: the load balancer pins this shopper to copy 2, the copy that remembers them. Now Pay reaches copy 2, and goes through.
On the right, copy 2 saves the checkout to the shared database, and keeps nothing itself.
When the shopper taps Pay, it goes to the least busy copy, copy 1. Copy 1 reads the checkout from the database, and the payment goes through. No pinning needed.
Next, a crash. The shopper starts a new checkout, on copy 2 again. Before they pay, copy 2 crashes, and everything in its memory goes with it.
They tap Pay. Copy 2 is gone, so the request goes to copy 1, which has never seen this checkout. The shopper has to start again.
On the right, copy 2 crashes too, but it held nothing. Pay goes to copy 1, which reads the checkout from the database, and the order goes through. Nothing is lost.
Finally, the 8 pm sale, and a fourth copy starts to help. But every shopper already checking out is pinned to the copy holding their checkout, so they can’t move. Only shoppers arriving now reach copy 4, and copies 1 to 3 overflow.
Our shopper is pinned to copy 1, and waits.
On the right, copy 4 takes requests at once. Any copy can serve any shopper, so the load evens out across all four, and our shopper’s order goes straight through.
So which should you pick? Keep app servers stateless: put the state in a shared database or cache, so any copy can serve any request, survive a crash, and scale. Keep state on a server only when you must, like an open WebSocket connection, and pin each user to it.