Drop RoutingStorage and Touch: GETEX already handles TTL refresh#4294
Drop RoutingStorage and Touch: GETEX already handles TTL refresh#4294
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cbae45d38a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Adds a new two-tier session.Storage implementation that fronts a remote storage backend (e.g., Redis) with a bounded in-memory LRU to reduce remote round-trips while preserving remote durability/authority.
Changes:
- Introduce
RoutingStorageimplementing thesession.Storageinterface with local LRU + remote fallback. - Add a comprehensive unit test suite covering cache hits/misses, eviction, delete semantics, remote failure behavior, and constructor panics.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| pkg/transport/session/storage_routing.go | New two-tier storage implementation using hashicorp/golang-lru for local caching and a delegated remote Storage. |
| pkg/transport/session/storage_routing_test.go | Unit tests validating caching behavior, remote delegation, eviction, and concurrency sanity. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4294 +/- ##
==========================================
+ Coverage 68.85% 69.10% +0.25%
==========================================
Files 473 477 +4
Lines 47899 48135 +236
==========================================
+ Hits 32979 33264 +285
- Misses 12279 12287 +8
+ Partials 2641 2584 -57 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 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 8 out of 8 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Adds RoutingStorage, a two-tier session storage that serves reads from a bounded in-memory LRU cache and falls back to a remote Storage (Redis) on cache misses. Store writes to remote first, only promoting to the local cache on success. DeleteExpired delegates entirely to remote, which owns the TTL source of truth; the local cache self-corrects on subsequent Loads. Uses github.com/hashicorp/golang-lru/v2 (already a transitive dependency) for a thread-safe generic LRU. NewRoutingStorage panics on maxLocalEntries <= 0 so that callers apply a sensible default (e.g. 1000) before calling.
jerm-dro
left a comment
There was a problem hiding this comment.
Do we need the LRU cache?
The original RFC I proposed mentioned LRU caching, but after seeing this implementation I've reconsidered. The PR correctly identifies that we must refresh Redis TTLs on access — something the RFC didn't account for. That constraint changes the calculus.
Every Load cache hit still calls remote.Touch (Redis EXPIRE), so every request pays a Redis RTT regardless. The LRU only saves deserializing the session payload, but sessions are small and GET and EXPIRE are both O(1) — the RTT dominates.
That means we're taking on significant complexity for a marginal saving: a new Touch method on the Storage interface (breaking change for all implementations), cross-replica staleness edge cases, LRU eviction/recovery logic, and a generated mock.
Suggestion: drop the LRU and have RedisStorage.Load use GETEX instead of GET. GETEX atomically returns the data and refreshes the TTL — the TTL refresh becomes an internal Redis concern, invisible to callers. No Touch, no interface change, no cache invalidation bugs. The RoutingStorage, mock, and Touch additions can all be removed.
If we later find Redis RTT is a bottleneck, we can add a local TTL cache in front, but that would be a different design (no per-hit remote calls, bounded staleness via local expiry, GETEX on cache miss).
RedisStorage.Load already uses GETEX which atomically returns the session data and refreshes the Redis TTL in a single round-trip. That makes RoutingStorage and Touch redundant: - Every LRU cache hit in RoutingStorage still called remote.Touch (Redis EXPIRE), paying a Redis RTT regardless. The LRU only saved deserializing a small session payload — negligible versus the RTT. - Touch on the Storage interface is a breaking change for all implementors and leaks a Redis-specific concern onto the interface. Remove: RoutingStorage, Touch from Storage interface, Touch from LocalStorage and RedisStorage, the generated mock, and the golang-lru/v2 direct dependency. GETEX already handles TTL refresh as an internal Redis concern, invisible to callers. No interface changes needed. Closes #4210 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Closing — no changes needed. |
Summary
The RFC for horizontal proxyrunner scaling (THV-0047) originally proposed an LRU cache in front of Redis to reduce hot-path round-trips. After reviewing the implementation, that approach was reconsidered.
RedisStorage.Loadalready usesGETEX, which atomically returns the session data and refreshes the Redis TTL in a single command. That makes every piece of the proposed design unnecessary:remote.Touch(RedisEXPIRE) to prevent stale reads after TTL expiry. The LRU only saved deserializing a small session payload, which is negligible compared to the RTT.Touchon theStorageinterface is a breaking change for all implementors and leaks a Redis-specific TTL concern onto a shared interface.RoutingStorageadded cross-replica staleness edge cases, LRU eviction/recovery logic, and a generated mock — all complexity with no measurable benefit.This PR removes those additions.
GETEXhandles TTL refresh as an internal Redis concern, invisible to callers. No interface changes are needed.Closes #4210
Type of change
Test plan
task test)task lint-fix)Does this introduce a user-facing change?
No.
Special notes for reviewers
If Redis RTT becomes a measurable bottleneck in the future, a local TTL cache is still the right approach — but the design would look different: no per-hit remote calls, bounded staleness via local expiry,
GETEXon cache miss. That is a separate decision backed by profiling data.