Scalability & Architecture Patterns
Stateless Services & Session Management
A stateless service keeps nothing about a specific client between requests, which is what makes it safe to run behind a load balancer with any request landing on any instance. Session state has to live somewhere — the question is just where.
- Stateless means no per-client data held in server memory between requests — every request carries, or looks up, everything it needs
- Sticky sessions (session affinity at the load balancer) fake statelessness by always routing a client to the same instance — it works until that instance dies or gets overloaded
- Externalizing session state to a shared store (Redis, a database, a signed client-side token) is what makes any instance interchangeable
- JWTs push session state into the client itself (signed, not encrypted by default) — no server-side lookup, but revocation before expiry is hard
- Server-side sessions in a shared cache support instant revocation and richer session data, at the cost of a network hop and a dependency that must itself be highly available
- Statelessness is a prerequisite for horizontal scaling, rolling deploys, and autoscaling — a stateful instance cannot be killed and replaced without losing something
| Sticky sessions | Server-side (Redis/DB) | Client-side (JWT) | |
|---|---|---|---|
| Any instance can serve a request? | no — pinned to one | yes | yes |
| Instant revocation | n/a | yes — delete the entry | no — valid until expiry |
| Extra network hop | no | yes | no |
| Failure mode | lost session if the instance dies | store must be highly available | stale permissions until expiry |
public Claims verify(String token) {
return Jwts.parserBuilder()
.setSigningKey(publicKey)
.build()
.parseClaimsJws(token) // throws if signature or expiry is invalid
.getBody();
}