6. Pluggable Session Storage Architecture
Date: 2025-10-13Status
AcceptedCategory
Core ArchitectureContext
Production authentication systems require stateful session management to track user sessions, enable session revocation, and provide sliding expiration windows. However, different deployment environments have different requirements:- Development: Fast in-memory storage with no dependencies
- Production: Persistent, distributed storage for horizontal scaling
- Testing: Fast, isolated storage that resets between tests
- Production Redis dependency even in development
- Inability to test session logic without Redis
- Vendor lock-in to specific storage backend
- Difficulty adding new storage backends (e.g., PostgreSQL, DynamoDB)
- Horizontal Scaling: Multiple server instances sharing session state
- Session Revocation: Immediate logout across all instances
- Concurrent Session Limits: Prevent account sharing/credential stuffing
- Sliding Expiration: Extend session on activity (UX improvement)
- Bulk Operations: Revoke all user sessions (security incident response)
Decision
We will implement a pluggable session storage architecture with an abstract base class and multiple concrete implementations.Architecture
Factory Pattern
Configuration
Consequences
Positive Consequences
- Flexibility: Easy to switch backends via configuration
- Development Speed: No Redis dependency for local development
- Testability: Fast, isolated tests with in-memory storage
- Production Ready: Redis backend supports distributed deployments
- Extensibility: New backends (PostgreSQL, DynamoDB) add easily
- No Vendor Lock-in: Abstract interface prevents Redis coupling
Negative Consequences
- Complexity: Multiple implementations to maintain
- Testing Burden: Each backend requires separate test coverage
- Feature Parity: Must ensure features work across all backends
- Interface Leakage: Backend-specific features may not fit abstraction
Neutral Consequences
- Performance Variance: InMemory faster than Redis (acceptable trade-off)
- Learning Curve: Developers must understand factory pattern
Alternatives Considered
1. Redis Only (Direct Integration)
Description: Require Redis for all environments, use redis-py directly Pros:- Simpler implementation (single code path)
- No abstraction overhead
- Full access to Redis features (pub/sub, streams)
- Requires Redis for development (slow setup)
- Cannot test without Redis (integration tests only)
- Vendor lock-in to Redis
- Higher barrier to entry for contributors
2. JWT-Only Stateless Sessions
Description: Store all session data in JWT token, no server-side storage Pros:- No storage dependency
- Perfect horizontal scaling (no shared state)
- Simple implementation
- Cannot revoke sessions (security risk)
- No concurrent session limits (security risk)
- Large tokens (performance impact)
- Sensitive data in client-side storage
3. Database-Backed Sessions (PostgreSQL)
Description: Store sessions in existing PostgreSQL database Pros:- No new infrastructure (reuse existing DB)
- Transactional guarantees
- SQL query capabilities
- Slower than Redis (no caching)
- Database load for high-traffic applications
- Requires database schema migrations
- No expiration mechanism (manual cleanup)
4. Filesystem-Based Sessions
Description: Store sessions as files on disk Pros:- No external dependencies
- Simple implementation
- Persistent across restarts
- Not distributed (fails on multiple instances)
- Slow (disk I/O)
- Manual cleanup required
- File locking complexity
Implementation Details
InMemorySessionStore
- Dictionary-based storage (O(1) lookups)
- Background cleanup thread for expired sessions
- Not persistent (resets on restart)
- Thread-safe with asyncio locks
RedisSessionStore
- Automatic expiration with Redis TTL
- Distributed (multiple instances share state)
- Persistence with AOF/RDB snapshots
- Sliding window via
EXPIREcommand updates - Concurrent limit enforcement with atomic operations
Concurrent Session Limit Enforcement
Sliding Expiration Window
Performance Characteristics
| Operation | InMemory | Redis (Local) | Redis (Network) |
|---|---|---|---|
| Create | < 1ms | ~2ms | ~10ms |
| Get | < 1ms | ~1ms | ~5ms |
| Update | < 1ms | ~2ms | ~10ms |
| Delete | < 1ms | ~1ms | ~5ms |
| List User Sessions | < 1ms | ~5ms | ~20ms |
Integration Points
AuthMiddleware (src/mcp_server_langgraph/auth/middleware.py):
- Accepts
session_store: SessionStoreparameter - Defaults to
InMemorySessionStore()for backward compatibility - Calls session operations: create, get, refresh, delete, list, revoke
src/mcp_server_langgraph/core/config.py):
session_backend: “memory” or “redis”redis_url,redis_password,redis_sslsession_ttl_seconds,session_sliding_window,session_max_concurrent
src/mcp_server_langgraph/auth/session.py:632-689):
Testing Strategy
Unit Tests (tests/test_session.py):
- InMemorySessionStore: 17/17 tests passing (100%)
- RedisSessionStore interface: 3/9 tests passing (needs Redis mock improvements)
- Factory function: 5/5 tests passing (100%)
- End-to-end session lifecycle with Redis (1/2 tests)
- Concurrent session limit enforcement
- Sliding window expiration
Migration Path
Existing deployments: No migration required (defaults to InMemory) Enabling Redis:- Deploy Redis instance
- Set
SESSION_BACKEND=redis - Configure Redis connection settings
- Restart application (existing InMemory sessions lost - acceptable)
Future Enhancements
- PostgreSQL session backend (for applications already using PostgreSQL)
- DynamoDB backend (for AWS serverless deployments)
- Session encryption at rest (PII protection)
- Session analytics (login patterns, security monitoring)
References
- Implementation:
src/mcp_server_langgraph/auth/session.py:1-731 - Tests:
tests/test_session.py:1-687 - AuthMiddleware:
src/mcp_server_langgraph/auth/middleware.py - Configuration:
src/mcp_server_langgraph/core/config.py - Related ADRs: