Repository navigation
feat(frontend): cache administration page (#250) - #430
Merged
Merged
Conversation
Add Administration -> Cache for site administrators, wired to the existing /api/cache endpoints the SPA never called: entry counts and size, hit rate and access time, recommendations with their priority, and the refresh, clean up, reset counters and clear actions. Clear all and Reset counters ask for confirmation; clean up only drops expired entries and does not. The page is built from /api/cache/stats and /recommendations only. The performance and health endpoints are left unused because hit_rate_percentage is a 0..1 fraction, memory_usage_mb is on the wrong scale and health.memory_usage is the entry count; this is documented in the API contract. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Cache::stats() read active/expired counts from two AtomicU64 counters that drifted and underflowed: - cleanup() ran expired_entries.fetch_sub(n), but time-expired entries were never counted there, so the counter wrapped to u64::MAX. The background maintenance thread calls cleanup() every TTL, so this happened on its own. - get() on an expired entry subtracted from active_entries unconditionally, and reset_counters() zeroed both counters while the entries stayed, so the next expired read wrapped active_entries as well. - total_entries = active + expired then overflowed (a panic in debug builds, a silently wrong sum in release). Count active and expired entries from the map in stats(), in the same pass that already sums the size, and drop the counters. reset_counters() now only zeroes hits, misses and access time, as the API documents, and the entries keep being counted. Regression tests cover the cleanup and expired-read underflows, expired entries being counted before a cleanup, and reset_counters keeping entries. test_cache_stats_branches built its health states by writing the counters; it now creates real expired entries. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Frontend SPA coverage (by module)
Generated from Vitest |
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #250
What
Administration → Cache for site administrators, wired to the
/api/cache/*endpoints the SPA never called:docs/developer/http-api-contract.md.The page uses only
GET /api/cache/statsand/recommendations.performanceandhealthare not used:hit_rate_percentageis a 0..1 fraction,memory_usage_mbis on the wrong scale andhealth.memory_usageis the entry count. This is noted in the contract doc.Backend fix found while testing this page
The first manual run showed Expired = 18446744073709552000 (u64::MAX).
Cache::stats()read active/expired counts from twoAtomicU64counters that drifted and underflowed:cleanup()subtracted fromexpired_entries, which time-expired entries never incremented. The background maintenance thread callscleanup()every TTL, so it wrapped on its own.get()on an expired entry subtracted fromactive_entriesunconditionally, andreset_counters()zeroed both counters while the entries stayed, so the next expired read wrappedactive_entries.total_entries = active + expiredthen overflowed (panic in debug, wrong sum in release).stats()now counts active and expired entries from the cache map in the pass that already sums the size, and the counters are gone.reset_counters()only zeroes hits, misses and access time, as the API documents. Side effect: "Expired" now includes entries that expired but are not yet cleaned up (before, it was almost always 0).Two existing tests encoded the old behaviour and were changed:
test_counters_and_reset(active entries dropped to 0 after a reset while the entry was still cached) andtest_cache_stats_branches(forced health states by writing the counters; it now creates real expired entries). Four regression tests were written first and failed on the old code.Verification
All builds and tests ran in Docker.
node:22-bookworm): 110 files, 720 tests pass;npm run build(tsc --noEmit+ Vite) is clean;docker build -f docker/frontend/Dockerfilesucceeds.rust:1-trixie):marreq-corelib tests 1589 pass (three consecutive runs); the 7 cache API integration tests pass with--features test-helpers;cargo clippy --workspace --all-targets -- -D warningsandcargo fmt --checkpass.Not verified
backup::tests::failed_dump_reports_stderr_and_cleans_upfailed in one early container run and passed in the next four. It runs a fakepg_dumpscript and does not use the cache; it looks flaky.Out of scope
performanceandhealthfields.docs/assets/requirements.csvRM-CACHE / RM-API entries describestatsas unauthenticated, which is stale against theAdminOnlyguard.docs/developer/http-api-contract.md; whichever merges second may need a small rebase.🤖 Generated with Claude Code