Module P-9·16 min read

Storing sessions as Hashes vs JSON strings, sliding expiry with EXPIRE on each request, session invalidation and logout, multi-device session tracking, and the consistency trade-offs when reading sessions from replicas.

JJS
Written by Jatin Jain Saraf · Senior Software Engineer

P-9 — Session Management Patterns

Who this module is for: You store user sessions in Redis — or are planning to — and want to implement them correctly. This covers session structure (Hash vs JSON string), sliding expiry, concurrent device management, and the read-from-replica consistency trap that silently logs users out.


Why Redis for Sessions

HTTP is stateless. Sessions provide continuity: a session token in a cookie maps to server-side state (user ID, permissions, preferences). The session store must be:

  • Fast — checked on every request
  • Shared — accessible from every application server instance
  • Expirable — sessions must auto-expire when idle or after a fixed duration

Redis satisfies all three. A 1ms session lookup is imperceptible. Every application server connects to the same Redis instance. TTL handles expiry automatically.

The alternative — database-backed sessions (PostgreSQL, MySQL) — works but adds a database query to every request, and relational databases are optimized for complex queries, not millions of simple ID lookups.


Session Structure: Hash vs JSON String

Option A: JSON String

SET session:{token} '{"userId":1001,"email":"j@example.com","role":"engineer","permissions":["read","write"]}' EX 3600

Simple. One key, one value. But updating a single field requires:

  1. GET session:{token} → deserialize JSON
  2. Modify the field in application memory
  3. SET session:{token} {updated JSON} → re-serialize and overwrite

Under concurrent requests (two requests updating different session fields simultaneously), one overwrites the other's changes. Race condition.

Option B: Redis Hash

text

Individual field updates are atomic:

text

Multiple fields can be updated without touching others. No race condition for independent field updates.

The trade-off: Hashes require one more key + the field name overhead. For sessions with 5–10 fields, the overhead is minimal. For sessions storing complex nested objects, you may need to serialize sub-objects as JSON strings within Hash fields.

Recommendation: Use a Hash for session storage. It enables atomic per-field updates and makes session data self-describing (HGETALL returns field names, not just a JSON blob).

typescript

Session Expiry: Idle Timeout vs Absolute Timeout

Idle Timeout (Sliding Expiry)

The session expires if unused for N minutes. Activity resets the timer.

typescript

Call touchSession on every authenticated request. If it returns false, the session expired and the user should be logged out.

Absolute Timeout

The session expires at a fixed time after creation, regardless of activity. Implement by storing the absolute expiry timestamp in the session Hash and checking it on every access:

typescript
typescript

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.