Level 1 · Multiplying Machines
Session 12: Stateless Is a Requirement: Why Redis Is More Than a Cache
Imagine if you logged in at counter 1, then moved to counter 2, and the attendant at counter 2 had no idea who you were. Whose fault is that: yours, or the way the counters were designed?
1. The Core Problem: "Stateful" Breaks Horizontal Scaling
Horizontal scaling assumes that every server instance is identical and can be replaced at any moment.
But if the application stores user status data (state) in the process's local memory (the server's own RAM):
- The user logs in on Server A ──► The session is stored in Server A's RAM.
- The Load Balancer sends the second request to Server B ──► Server B doesn't have that session ──► The user gets kicked out (HTTP 401 Unauthorized).
┌──────────────┐
│ Load Balancer│
└──────┬───────┘
Request 1 │ Request 2
(Login) │ (Checkout)
┌─────────┴─────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Server A │ │ Server B │
│ [RAM: User1]│ │ [RAM: Empty]│
│ LOGIN OK │ │ 401 ERROR! │
└─────────────┘ └─────────────┘
To get around this, beginner architects are tempted to use a Sticky Session (Session 10). But as we learned, sticky sessions create a Single Point of Failure and break autoscaling.
2. Defining a Stateless Architecture
An application is called Stateless if: 1. No transaction data or user session is stored on the application server's local disk or RAM. 2. Any application instance can die, restart, or be replaced right this second without a single user getting disconnected or losing their shopping cart. 3. The application server acts only as a logic-processing tool (a pure Compute Engine).
State data is stored in a centralized shared storage layer: the Shared State Layer (Redis).
3. Enter Redis: Not Just a "Cache", but a Shared State Store
Many people think Redis is only for "making slow queries fast" (caching). Its real role in a distributed architecture is far more fundamental: serving as the source of truth for session state (Session Store) and for coordination across servers.
┌──────────────┐
│ Load Balancer│
└──────┬───────┘
┌───────────┴───────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Server A │ │ Server B │
│ (Stateless) │ │ (Stateless) │
└──────┬──────┘ └──────┬──────┘
│ │
└───────────┬───────────┘
▼
┌──────────────┐
│ Redis Shared │
│ Session Store│
└──────────────┘
Characteristics of a Redis Session Store:
- In-Memory Speed: Sub-millisecond latency (< 1 ms), with no load on the SQL database.
- Automatic TTL (Time-To-Live): Session data is deleted automatically when it expires (
EXPIRE session:123 3600), no manual cleanup cronjob needed. - Rich Data Structures: String, Hash (for user attributes), Set (for active tokens).
4. Distributed Lock: Avoiding Double Execution
When 10 application servers are running in parallel, what happens if two requests from the same user arrive in the same millisecond (say the user taps the "Pay Now" button twice)?
Without a Distributed Lock, both servers will happily deduct the user's balance!
The Redis Lock Mechanism (SETNX / Redlock):
# Server A tries to acquire the lock
acquired = redis.set("lock:order:user_123", "server_a", nx=True, ex=10)
if acquired:
try:
# Process the payment...
potong_saldo()
kirim_barang()
finally:
# Release the lock
redis.delete("lock:order:user_123")
else:
# Server B is rejected because Server A is holding the lock
return "Transaction is being processed, please wait."
nx=True(Not Exists): Only succeeds if the key doesn't exist yet.ex=10(Expire 10s): If Server A suddenly dies (crash/OOM) mid-processing, the lock is released automatically after 10 seconds so it doesn't deadlock forever.
5. Idempotency: The Last Safety Net
Even with locks and Redis, the internet is never perfect. A client can resend the same request because of a network timeout (retry).
Idempotency Key:
- The client includes a unique header with every data-mutating action: Idempotency-Key: uuid-v4-abc-123.
- The server checks in Redis:
- If the key has already completed ──► return the old response from Redis without executing against the database again.
- If not ──► store the key in Redis with status PROCESSING, execute, then store the result.
6. Comparison Summary
| Dimension | Stateful App (Bad for Scale) | Stateless + Redis (Cloud Standard) |
|---|---|---|
| Session Location | Server's local RAM / Disk | Centralized Redis Cluster |
| Autoscaling | Very hard (must be sticky) | Freely add/remove servers at any time |
| Server Crash | Users on that server get logged out | Zero impact on users (transparent) |
| New Deploy | Needs a long drain & carries risk | Instant rolling update |
| Concurrency Control | Local thread mutex (only 1 machine) | Redis Distributed Lock (across the cluster) |
7. Key Terms
Stateless: An application design where the server doesn't keep user state between requests.Shared State: A centralized storage layer accessed jointly by all worker nodes.Redis: A very-low-latency in-memory data store for sessions, cache, and locks.Distributed Lock: A mechanism for locking a shared resource across many independent servers.SETNX: The Redis "Set if Not Exists" command, the foundation of a distributed mutex.Idempotency: The property of an operation that gives identical results even when executed many times.