feat(session): add NewManagerWithRedis constructor (RC-7)#4236
feat(session): add NewManagerWithRedis constructor (RC-7)#4236
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a Redis-backed session manager constructor to wire the Redis storage backend into the session.Manager lifecycle, without changing existing NewManager / NewTypedManager signatures.
Changes:
- Add
NewManagerWithRedis(ttl, factory, cfg)constructor that buildsRedisStorageand delegates toNewManagerWithStorage - Add unit tests covering
NewManagerWithRedissuccess/error paths and asserting existing constructors still useLocalStorage
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| pkg/transport/session/manager.go | Adds NewManagerWithRedis constructor that creates RedisStorage and returns a Manager via NewManagerWithStorage. |
| pkg/transport/session/manager_redis_test.go | Adds tests for Redis-backed manager construction, basic round-trip behavior, Stop behavior, and LocalStorage defaults for existing constructors. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4236 +/- ##
==========================================
+ Coverage 69.07% 69.20% +0.12%
==========================================
Files 477 478 +1
Lines 48074 48096 +22
==========================================
+ Hits 33207 33283 +76
+ Misses 12284 12230 -54
Partials 2583 2583 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
jhrozek
left a comment
There was a problem hiding this comment.
Review of the Redis session storage backend. Overall the implementation is well-structured — clean Storage interface compliance, good use of GETEX for TTL refresh, comprehensive tests with miniredis. A few items to consider, mostly around input validation and API completeness.
Generated with Claude Code
Wires the Redis storage backend into the session manager lifecycle. NewManagerWithRedis accepts a RedisConfig and TTL, constructs a RedisStorage via NewRedisStorage, and delegates to NewManagerWithStorage. Existing NewManager and NewTypedManager signatures are unchanged. Closes #4208
There was a problem hiding this comment.
Pull request overview
Adds Redis-backed session storage and a NewManagerWithRedis constructor so the session manager can be instantiated with a Redis backend (supporting horizontal scaling) while keeping existing NewManager / NewTypedManager callers unchanged.
Changes:
- Introduces
RedisStorage+RedisConfig(standalone & sentinel) and TLS support, with TTL-based (sliding window) key expiry behavior. - Adds
NewManagerWithRedis(ctx, ttl, factory, cfg)which constructs Redis storage and delegates toNewManagerWithStorage. - Expands/updates unit tests for Redis storage and manager behavior; updates storage interface docs to clarify TTL refresh semantics on
Load.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/transport/session/manager.go | Adds NewManagerWithRedis constructor delegating to NewManagerWithStorage. |
| pkg/transport/session/manager_redis_test.go | Adds unit tests covering Redis-backed manager construction and basic behavior. |
| pkg/transport/session/manager_test.go | Updates test IDs to UUID-looking constants (no functional change). |
| pkg/transport/session/redis_config.go | Adds Redis configuration types + default timeout constants. |
| pkg/transport/session/storage.go | Updates Storage.Load doc to allow backend TTL refresh behavior. |
| pkg/transport/session/storage_redis.go | Implements Redis-backed Storage with TLS config + TTL refresh on reads. |
| pkg/transport/session/storage_redis_test.go | Adds comprehensive Redis storage unit tests using miniredis. |
| pkg/transport/session/serialization.go | Removes “unused” comments now that serialization is used by Redis storage. |
Comments suppressed due to low confidence (1)
pkg/transport/session/storage_redis.go:108
- NewRedisStorage performs an eager Ping and fails construction if Redis is unreachable. The linked issue/testing strategy calls out that unreachable addresses should still allow construction (go-redis is typically lazily connected), with errors surfacing on first operation. If startup should not be coupled to Redis availability, consider making Ping optional (or removing it) and relying on operation-time errors; alternatively, update the issue/PR expectations and add tests for the intended fail-fast behavior.
if err := client.Ping(ctx).Err(); err != nil {
_ = client.Close()
return nil, fmt.Errorf("failed to connect to redis: %w", err)
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
Wires the Redis storage backend into the session manager lifecycle (RC-7), and establishes UUID as the canonical session ID format across all storage backends and proxy transports.
NewManagerWithRedis constructor (RC-7)
NewManagerWithRedis(ctx, ttl, factory, cfg)accepts aRedisConfigand constructs a Redis-backed*ManagerviaNewRedisStorage+NewManagerWithStoragectxis used for the initial Ping during construction (improvement over the original issue spec, which did not thread context through)NewManager,NewTypedManager, andNewManagerWithStoragesignatures are unchangedUUID enforcement (breaking API change — see compatibility notes below)
Managernow validates that all session IDs are UUID-format onAddWithID,AddSession,UpsertSession, andDeletevalidateSessionIDis an unexported helper that enforces this consistently across all storage backends (previouslyRedisStoragealone had per-operation UUID checks, creating a store-then-can't-load inconsistency)RedisStorageno longer does its own UUID checks; validation lives exclusively inManagerNon-UUID Mcp-Session-Id normalization in transparent proxy
Mcp-Session-Id; upstream servers may issue arbitrary stringsnormalizeSessionID(new, inpkg/transport/proxy/transparent) maps any non-UUID external session ID to a deterministic UUID v5 (RFC 4122 URL namespace), so the transparent proxy can store upstream-issued IDs in the UUID-only session manager without a reverse-mapping tableMcp-Session-Idfrom upstream responses or SSE stream dataFixes #4208
Type of change
Test plan
task test)task test-e2e)task lint-fix)Changes
pkg/transport/session/manager.goNewManagerWithRedis,validateSessionID; wire UUID validation into all manager methodspkg/transport/session/storage_redis.goUsernameACL field wiring; addKeyPrefixtrailing-colon validation; sliding-window TTL godocpkg/transport/session/redis_config.goUsernamefield for Redis 6+ ACL authpkg/transport/proxy/transparent/session_id.gonormalizeSessionID— UUID pass-through or deterministic UUID v5 derivationpkg/transport/proxy/transparent/transparent_proxy.gonormalizeSessionIDat allMcp-Session-Idcallsitespkg/transport/proxy/transparent/sse_response_processor.gonormalizeSessionIDbeforeAddWithIDnormalizeSessionIDDoes this introduce a user-facing change?
No direct user-facing change. Internal plumbing for Redis-backed horizontal scaling (THV-0047). The Redis constructor is not yet wired into the vMCP server (deferred to RC-8).
Special notes for reviewers
Compatibility:
Manager.AddWithID,AddSession,UpsertSession, andDeletenow reject non-UUID IDs with"invalid session ID format". All existing toolhive call sites generate UUIDs internally, so there is no runtime regression. The transparent proxy is the only place that ingests externally-sourced session IDs;normalizeSessionIDhandles that path.Sliding-window TTL:
RedisStorageusesGETEXon everyLoadto refresh the TTL, meaning sessions remain alive indefinitely while actively used. Revocation requires an explicitDelete. No absolute max lifetime is enforced; aMaxLifetime/CreatedAtcheck is the noted path forward if a hard cap is needed.