Skip to content

feat(lru-memo-cache): add serializeCache and hydrateCache for SSR persistence - #1022

Closed
arkhindi12 wants to merge 1 commit into
conduit-protocol:mainfrom
arkhindi12:feat/lru-cache-serialize
Closed

arkhindi12 wants to merge 1 commit into
conduit-protocol:mainfrom
arkhindi12:feat/lru-cache-serialize

Conversation

@arkhindi12

Copy link
Copy Markdown
Contributor

Context

Cache memory is lost on server-side rendering re-hydration, so each SSR pass re-queries contract metadata the previous request already had.

Adds the two helpers the issue asks for in src/lru-memo-cache.ts:

  • serializeCache(): string — versioned JSON snapshot
  • LruMemoCache.hydrateCache<K, V>(json): LruMemoCache<K, V> — rebuild from a snapshot

Three details that matter for the SSR case

Expiries are stored as absolute epoch-ms deadlines, not remaining TTLs. A snapshot that sits in a transport for ten minutes must not hand every entry a fresh lease on life — otherwise hydration quietly serves stale contract metadata, which is the exact problem this is meant to solve.

Entries are written oldest → newest. Replaying them in that order restores LRU recency, so eviction after hydration still drops the coldest entry instead of an arbitrary one.

Hit/miss counters are not serialized. They describe the process that produced the snapshot; carrying them across would make metrics() report hits this cache never served. A hydrated cache starts at zero and measuredSpeedupPercent back at null.

Robustness

hydrateCache throws RangeError — rather than returning a half-built cache — when:

  • the input is not valid JSON, or not a plain object
  • version is not LRU_CACHE_SNAPSHOT_VERSION (currently 1)
  • maxEntries is not a positive integer, or ttlMs is present but invalid
  • entries is missing, or an entry has no key / a non-finite expiresAt

Entries already expired at hydration time are dropped. A snapshot claiming more entries than maxEntries keeps the newest ones, which is what replaying in order would have produced anyway.

Tests

src/tests/lru-memo-cache.test.ts — 9 new cases, 17 total in the file, all passing:

npx vitest run src/tests/lru-memo-cache.test.ts
 Test Files  1 passed (1)
      Tests  17 passed (17)

Covered: round-trip, recency order preserved through eviction, absolute expiry (400ms of a 1000ms TTL remains after a 600ms gap — not a fresh 1000ms), expired-in-transit entries dropped, default ttlMs carried across, metrics zeroed, unsupported version rejected, malformed snapshots rejected, over-long snapshot truncated to maxEntries.

tsc --noEmit reports no errors in lru-memo-cache.ts.

Note: the repo has pre-existing failures in custom-fetch*.test.ts, soroban-retry-after.test.ts, streams.test.ts and relayer-stress.test.ts (17 tests). Verified via git stash that they fail identically on a clean main, so they are unrelated to this change.

On assignment

I applied to this issue but haven't been assigned yet. Opening this as a draft so the approach is reviewable and the code is ready if you assign it — happy to close it without ceremony if you give this to someone else. If you'd rather review before any branch exists, say so and I'll take it down.

Closes #977

…sistence

Cache memory is lost on server-side rendering re-hydration, so every SSR
pass re-queries contract metadata that the previous request already had.

Adds a versioned JSON snapshot pair:

- serializeCache() writes entries oldest -> newest so replaying them
  restores LRU recency, and stores expiries as absolute epoch-ms deadlines
  so a snapshot in transit does not hand entries a fresh TTL.
- hydrateCache() rebuilds the cache with its original maxEntries/ttlMs,
  drops entries that expired while the snapshot was in transit, and
  rejects unknown versions or malformed input with RangeError.

Hit/miss counters are not serialized: they describe the process that
produced them, and carrying them over would make metrics() report hits
this cache never served.

Closes conduit-protocol#977
@arkhindi12

Copy link
Copy Markdown
Contributor Author

Closing this — #977 is already implemented on main (merged in #1007, which I opened on Oct 18). I didn't check before opening this branch, so it's a duplicate of my own work and now conflicts.

Sorry for the noise. Nothing to review here.

@arkhindi12 arkhindi12 closed this Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

src/lru-memo-cache.ts: export serializeCache and hydrateCache methods for SSR persistence

1 participant