Compare

Stateful vs Stateless servers

StatefulEach copy keeps your checkout

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

Place order · ₹184
Load balancerleast busy
  • Copy 1memory: empty
  • Copy 2memory: empty
  • Copy 3memory: empty
  • Copy 4not running
StatelessCopies keep nothing

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

Place order · ₹184
Load balancerleast busy
  • Copy 1keeps nothing
  • Copy 2keeps nothing
  • Copy 3keeps nothing
  • Copy 4not running
Databasecheckouts

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.

Stateful or Stateless servers?

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.

DifferenceStatefulStateless
Where the state livesIn the server’s own memoryIn a shared database or cache
Who can serve youOnly the copy holding your stateAny copy
Load balancerSticky sessions: each user pinned to a copyLeast busy copy, every request
When a copy diesEverything in its memory is lostNothing is lost: another copy carries on
Adding copiesHelps only new users; pinned ones stay putEvery copy takes traffic at once
The costNo extra trip: the state is right thereA read or write to the store on each step
Pick it forWhat can’t leave a server, like an open WebSocketApp servers: checkouts, sessions, carts

Stateful vs Stateless servers, explained step by step

1. Start, then pay

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.

2. A copy crashes

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.

3. Sale at 8 pm

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.

In short

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.