RFC-024: Distributed Session Store Pattern
Summary
Define a Distributed Session Store pattern that maintains session state (attributes, key-value pairs) across multiple regions and data centers. This pattern enables stateful interactions with Prism while supporting:
- Cross-region session access (user connects to different regions)
- Session attribute storage (metadata, preferences, context)
- Proto-typed key-value data per session
- Eventual consistency with configurable replication strategies
Motivation
Problem: Stateless Proxy Limits Session-Aware Applications
Currently, Prism proxy is stateless: each request is independent, with no session memory.
Challenges this creates:
- Multi-Request Workflows: Applications must pass all context in every request
- Cross-Region User Mobility: User connects to US region, then switches to EU region - no shared session state
- Large Session Context: Passing 10KB+ of session data on every request wastes bandwidth
- Session-Scoped Caching: No place to cache per-session data (parsed tokens, resolved policies, backend connections)
Example: Multi-step data pipeline
# Without session store: Pass full context every request
Request 1: Fetch user preferences → 200 OK (prefs: {theme: dark, lang: en})
Request 2: Query data + attach prefs → 200 OK (data: [...], need to send prefs again)
Request 3: Transform data + attach prefs → 200 OK (transformed: [...], prefs sent again)
# With session store: Store context once, reference session ID
Request 1: Create session → 200 OK (session_id: sess-abc123)
Request 2: Store prefs in session → 200 OK
Request 3: Query data (session_id) → Proxy retrieves prefs from session store
Request 4: Transform data (session_id) → Proxy retrieves prefs from session store
Use Cases
1. Cross-Region User Mobility
Scenario: User starts work in US region, travels to EU, continues work from EU region.
Without Session Store:
- User re-authenticates in EU region
- Previous session state (preferences, context) lost
- Application must re-fetch all state from backend
With Session Store:
- Session replicated to EU region (eventual consistency)
- User reconnects with same session_id
- EU proxy retrieves session state from EU replica
- Seamless continuation of work
2. Multi-Request Workflows
Scenario: Data ingestion pipeline with 5 steps, each step requires session context.
Without Session Store:
# Client must attach context to every request
context = {"user_id": "alice", "workspace": "project-x", "batch_id": "batch-123"}
step1(context) # 5KB context sent
step2(context) # 5KB context sent again
step3(context) # 5KB context sent again
step4(context) # 5KB context sent again
step5(context) # 5KB context sent again
# Total: 25KB bandwidth wasted
With Session Store:
# Client stores context once
session_id = create_session()
set_session_data(session_id, context) # 5KB context stored once
step1(session_id) # Just 16-byte session ID
step2(session_id) # Just 16-byte session ID
step3(session_id) # Just 16-byte session ID
step4(session_id) # Just 16-byte session ID
step5(session_id) # Just 16-byte session ID
# Total: 5KB + (5 × 16 bytes) = ~5KB bandwidth
3. Session-Scoped Backend Connections
Scenario: Plugin establishes expensive backend connection (Vault credentials, database connection pool) per session.
Without Session Store:
- Plugin must establish new connection for every request
- No way to share connection across requests from same session
- Vault token fetched on every request (~50ms latency)
With Session Store:
- Plugin stores backend connection handle in session store
- Subsequent requests reuse connection
- Vault token fetched once per session, cached in session store
4. Collaborative Editing / Real-Time Applications
Scenario: Multiple users editing shared document, need to track who is active.
With Session Store:
- Store active user sessions for document:
doc-123 → [sess-alice, sess-bob] - When user joins: Add session to document's active sessions
- When user disconnects: Remove session from document's active sessions
- Cross-region replication ensures all regions see active users
Design Principles
1. Region-Local Reads, Global Writes
Session store optimized for:
- Fast local reads: Proxy reads from local replica (<1ms P99)
- Asynchronous writes: Write to local replica, replicate to other regions (eventual consistency)
┌──────────────────────────────────────────────────────────────┐
│ Global Session Store │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐│
│ │ US Region │ │ EU Region │ │ APAC Region││
│ │ (Primary) │──────▶│ (Replica) │────▶│ (Replica) ││
│ │ │ │ │ │ ││
│ │ sess-abc123: │ │ sess-abc123: │ │ sess-abc123││
│ │ { │ │ { │ │ { ││
│ │ user: alice│ │ user: alice│ │ user: ... ││
│ │ prefs: {...│ │ prefs: {...│ │ ││
│ │ } │ │ } │ │ ││
│ └──────────────┘ └──────────────┘ └────────────┘│
│ │ ▲ ▲ │
│ │ │ │ │
│ └───────────────────────┴────────────────────┘ │
│ Replication Stream │
└──────────────────────────────────────────────────────────────┘
2. Proto-Typed Session Data
Session store supports structured data using protobuf:
message SessionData {
string session_id = 1;
// Session metadata
SessionMetadata metadata = 2;
// Key-value storage (proto Any type for flexibility)
map<string, google.protobuf.Any> data = 3;
// Session lifecycle
google.protobuf.Timestamp created_at = 4;
google.protobuf.Timestamp last_accessed = 5;
google.protobuf.Timestamp expires_at = 6;
}
message SessionMetadata {
string user_id = 1;
string region = 2; // Where session was created
string client_id = 3;
map<string, string> attributes = 4; // Unstructured metadata
}
Why protobuf?
- Type safety: Client and proxy agree on data structure
- Versioning: Forward/backward compatibility via protobuf field evolution
- Efficient encoding: Smaller payloads than JSON (30-50% reduction)
- Language-agnostic: Works with Go, Python, Rust, Java clients
3. Pluggable Replication Strategies
Session store pattern is backend-agnostic:
| Backend | Replication Strategy | Consistency | Use Case |
|---|---|---|---|
| Redis Cluster | Hash slot sharding | Eventual | Low latency, high throughput |
| PostgreSQL + pglogical | Logical replication | Strong (sync) | Strong consistency required |
| DynamoDB Global Tables | Multi-region replication | Eventual | AWS-native, auto-scaling |
| CockroachDB | Raft consensus | Serializable | Global distributed SQL |
| Cassandra | Multi-datacenter replication | Tunable | Massive scale, tunable consistency |
Configuration:
session_store:
backend: redis-cluster
replication:
strategy: eventual
regions: [us-west-2, eu-central-1, ap-southeast-1]
sync_interval: 100ms
ttl: 86400 # 24 hours
max_size: 1MB # Per session
4. Session Lifecycle Management
Sessions have expiration:
- TTL-based expiration: Session expires after inactivity (default: 24h)
- Explicit deletion: Client can delete session
- Automatic cleanup: Background job removes expired sessions from all replicas
Session Lifecycle:
1. Create → session_id assigned, metadata stored, TTL set
2. Access → last_accessed updated, TTL extended (sliding window)
3. Update → data modified, change replicated to other regions
4. Expire → TTL reached, session marked for deletion
5. Cleanup → Background job removes from all replicas
Architecture
Component Diagram
┌──────────────────────────────────────────────────────────────┐
│ Client Application │
└────────────────────────┬─────────────────────────────────────┘
│
│ gRPC (session_id in metadata)
▼
┌──────────────────────────────────────────────────────────────┐
│ Prism Proxy (Region: US) │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Session Interceptor │ │
│ │ - Extract session_id from metadata │ │
│ │ - Fetch session data from local store │ │
│ │ - Inject session data into request context │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Session Store Client (Plugin) │ │
│ │ - Get(session_id) → SessionData │ │
│ │ - Set(session_id, key, value) │ │
│ │ - Delete(session_id) │ │
│ └────────────────────┬───────────────────────────────────┘ │
└────────────────────────┼─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Session Store Backend (Redis Cluster) │
│ │
│ ┌──────────────┐ ┌─────── ───────┐ ┌────────────┐│
│ │ US Shard 1 │ │ US Shard 2 │ │ US Shard 3 ││
│ │ Hash Slot: │ │ Hash Slot: │ │ Hash Slot: ││
│ │ 0-5461 │ │ 5462-10922 │ │10923-16383 ││
│ └──────────────┘ └──────────────┘ └────────────┘│
│ │ │ │ │
│ └───────────────────────┴────────────────────┘ │
│ Replication to EU/APAC │
└──────────────────────────────────────────────────────────────┘