Skip to content

feat: never-evicting per-entry store behind the API cache - #638

Merged
tkuhn merged 1 commit into
masterfrom
feat/api-cache-entry-store
Aug 25, 2026
Merged

feat: never-evicting per-entry store behind the API cache#638
tkuhn merged 1 commit into
masterfrom
feat/api-cache-entry-store

Conversation

@tkuhn

@tkuhn tkuhn commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #570. The persistent cache only captured what was still in the in-memory caches at save time, so their 24h idle expiry propagated into the snapshot: space and maintained-resource pages not visited for a day of runtime came back from a restart with a blank loading state instead of their previous content (user pages mostly escaped this because their few pages are visited often and their cache keys are stable).

What this adds

  • Per-entry store: a directory next to the snapshot file (<apiCacheFile>.d/, default ~/.nanopub/nanodash-api-cache.ser.d/) with one small file per query response or map, named by a SHA-256 of the cache id. Every successful fetch overwrites its entry (atomic tmp+move). This tier never evicts — content that once arrived stays available until a re-fetch replaces it.
  • Read-through on memory miss: retrieveStaleResponse, retrieveResponseSync, retrieveResponseAsync, and retrieveMap fall through to the store and re-insert the entry with its original timestamp, so the existing stale-while-revalidate logic serves the stored content immediately while re-querying in the background. No display-layer changes needed.
  • Backfill at startup: the store is populated from the snapshot file, so content saved by versions from before the store existed carries over.
  • The in-memory caches keep their bounded size and 24h idle expiry; the 5-min snapshot file stays as the bulk warm-boot path (and remains the only persistence for nanopubs and views).
  • Test isolation: surefire now sets NANODASH_API_CACHE_FILE=none — tests starting a WicketApplication were reading and writing the developer's real snapshot in ~/.nanopub, which would become permanent pollution with a never-evicting store.

Verified

  • Full test suite passes (1184 tests, 8 new: store round-trip, read-through without API calls, backfill on load, corrupt-file deletion, if-absent semantics, disabled-store no-ops).
  • E2E on an isolated instance: with the snapshot file deleted and only the store present, space, maintained-resource, and user pages all rendered their full content with no spinners, while background refreshes updated the served entries and rewrote their store files.

Trade-off: the store grows monotonically (a few KB per distinct query+params ever issued); housekeeping can be layered on later without touching the read/write paths. Not addressed here: the multi-ref representative-root flip still makes a space page cold once per flip, since the new root_np cache key has never been fetched — possible follow-up is a seed fallback to other known ref roots.

🤖 Generated with Claude Code

The persistent cache (issue #570) only captured what was still in the
in-memory caches at save time, so their 24h idle expiry propagated into
the snapshot: pages not visited for a day of runtime came back from a
restart with a blank loading state instead of their previous content.

A per-entry store (a directory of one file per query response or map,
next to the snapshot file) now forms a persistent tier that never
evicts: every successful fetch overwrites its entry, and a memory miss
in the retrieval paths falls through to the store, re-inserting the
entry with its original timestamp so the usual staleness logic serves
the stored content right away while re-querying in the background. The
store is backfilled from the snapshot file at startup; the in-memory
caches keep their bounded size and idle expiry.

Surefire now disables cache persistence, as tests starting a
WicketApplication were reading and writing the developer's real
snapshot in ~/.nanopub — permanent pollution with a store that never
evicts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@tkuhn
tkuhn merged commit 0272e41 into master Aug 25, 2026
7 checks passed
@tkuhn
tkuhn deleted the feat/api-cache-entry-store branch August 25, 2026 08:05
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 5.12.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant