Topic 8.4
Distributed Session Management
In one line
Storing sessions in Redis makes app servers stateless: any instance can serve any user, deployments don't log people out, and you can revoke sessions centrally. Design for TTL, logout everywhere, session fixation, concurrent device limits, and what happens when Redis fails.
Think of it like this
A hotel that keeps guest records at the front desk instead of in each room's drawer. Guests can switch rooms, any receptionist can help them, and if a key card is stolen, the desk can cancel all of that guest's cards at once.
Key ideas
- 01
Architecture: browser → load balancer → any app instance → Redis session store. No sticky sessions needed, so scaling and rolling deploys are easy.
- 02
Data:
session:{id}as a hash (user ID, roles, created, last seen, device) with an idle TTL, plus an absolute lifetime field. Session IDs must be long random values (at least 128 bits) sent inHttpOnly,Secure,SameSitecookies. - 03
Logout everywhere: a per-user index set
user:{id}:sessions; to revoke, delete every session key in it. Spring Session keeps a similar principal-name index when using the indexed repository. - 04
Session fixation: always create a new session ID at login (and on privilege changes), deleting the pre-login session, so an attacker can't plant a known session ID on a victim.
- 05
Concurrency and devices: limit active sessions per user by checking
SCARDon the index at login and removing the oldest (keep a sorted set by login time instead of a set if order matters). - 06
Failure behaviour: if Redis is lost, everyone is logged out. Decide if that's acceptable; if not, use replicas with automatic failover and AOF. JWTs are an alternative for stateless auth but make revocation harder (you end up keeping a denylist in Redis anyway).
Code & diagrams
Works when the index and sessions share a slot (standalone or a per-user hash tag); in Cluster without tags, delete per key in a pipeline.
-- KEYS[1] = user:{42}:sessions ; session keys are {42}:session:<id>
local ids = redis.call('SMEMBERS', KEYS[1])
for _, id in ipairs(ids) do
redis.call('UNLINK', '{' .. ARGV[1] .. '}:session:' .. id)
end
redis.call('UNLINK', KEYS[1])
return #ids@Configuration
@EnableRedisIndexedHttpSession(maxInactiveIntervalInSeconds = 1800, redisNamespace = "app:session")
public class SessionConfig { }
// Log out all sessions of the current user:
@Autowired FindByIndexNameSessionRepository<? extends Session> sessions;
public void logoutEverywhere(String username) {
sessions.findByPrincipalName(username).keySet().forEach(sessions::deleteById);
}Interview problem
The problem
Sessions for 100M users across many app servers
Design session management for an app with 100M registered users, 20M concurrent sessions, multiple devices per user, "log out all devices", a maximum of 5 devices, and zero forced logouts during deployments.
You're given
- 20M concurrent sessions
- Up to 5 devices per user
- Idle timeout 30 min, max lifetime 7 days
- Logout everywhere
The interviewer follows up
Sessions in Redis or JWTs?
When it breaks
Session index sets without TTL or cleanup
What you see
Expired session IDs pile up in user:{id}:sessions forever; device limits count ghosts and users get logged out of real devices.
Fix & prevent
Remove stale IDs lazily (check EXISTS when reading the index) and set a TTL on the index at least as long as the maximum session lifetime.
Explain it without notes
What is session fixation, and how does rotating the session ID at login prevent it?
Practice
Implement a 3-device limit with a sorted set and test logging in from 4 "devices".
Trade-offs
- ↔
Redis sessions are revocable and simple, at the cost of a lookup per request and a dependency on Redis availability.
Done when you can
I can design stateless session storage with TTLs, device limits and logout everywhere.
I rotate session IDs at login and understand the failure impact of losing Redis.