Skip to content

feat(frontend): cache administration page (#250) - #430

Merged
mariusmm merged 2 commits into
mainfrom
feat/issue-250-cache-admin-ui
Oct 29, 2026
Merged

mariusmm merged 2 commits into
mainfrom
feat/issue-250-cache-admin-ui

Conversation

@mariusmm

Copy link
Copy Markdown
Collaborator

Closes #250

What

Administration → Cache for site administrators, wired to the /api/cache/* endpoints the SPA never called:

  • Entries (total, active, expired, size), performance (hit rate, hits, misses, requests, average access time, last cleanup) and recommendations with their priority.
  • Actions: Refresh, Clean up expired, Reset counters, Clear all. Clear all and Reset counters ask for confirmation; Clean up only drops expired entries and does not.
  • Access check and layout follow the Backup page. Docs: new "Admin application cache" section in docs/developer/http-api-contract.md.

The page uses only GET /api/cache/stats and /recommendations. performance and health are not used: 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 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 two AtomicU64 counters that drifted and underflowed:

  • cleanup() subtracted from expired_entries, which time-expired entries never incremented. The background maintenance thread calls cleanup() every TTL, so it wrapped 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.
  • total_entries = active + expired then 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) and test_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.

  • Frontend (node:22-bookworm): 110 files, 720 tests pass; npm run build (tsc --noEmit + Vite) is clean; docker build -f docker/frontend/Dockerfile succeeds.
  • Backend (rust:1-trixie): marreq-core lib tests 1589 pass (three consecutive runs); the 7 cache API integration tests pass with --features test-helpers; cargo clippy --workspace --all-targets -- -D warnings and cargo fmt --check pass.
  • Browser, against a running stack: the Cache tab appears, Clean up expired and Reset counters work.

Not verified

  • The backend fix has not been exercised in a browser against a rebuilt backend; the browser run above used an older backend image. It is covered by the unit and integration tests.
  • backup::tests::failed_dump_reports_stderr_and_cleans_up failed in one early container run and passed in the next four. It runs a fake pg_dump script and does not use the cache; it looks flaky.

Out of scope

🤖 Generated with Claude Code

marius and others added 2 commits October 29, 2026 14:12
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>
@github-actions

Copy link
Copy Markdown

Frontend Code Coverage

Frontend SPA coverage (by module)

Module Line Rate Branch Rate Function Rate Health
api 89.3% (317 / 355) 82.7% 86.4% ✔
components 74.1% (2234 / 3015) 62.7% 70.5% ➖
components/graph 100.0% (9 / 9) 66.7% 100.0% ✔
components/reports 86.1% (87 / 101) 70.6% 74.1% ✔
components/table 100.0% (7 / 7) 80.0% 100.0% ✔
context 77.8% (56 / 72) 40.0% 69.6% ➖
hooks 72.3% (146 / 202) 71.1% 70.0% ➖
lib 100.0% (18 / 18) 100.0% 100.0% ✔
pages 69.3% (2394 / 3457) 55.8% 61.7% ➖
pages/catalog 34.8% (162 / 465) 24.5% 18.3% ❌
pages/groups 0.0% (0 / 219) 0.0% 0.0% ❌
src (root) 33.3% (8 / 24) 38.1% 60.0% ❌
types 100.0% (0 / 0) 100.0% 100.0% ✔
utils 97.2% (620 / 638) 89.1% 98.8% ✔
Summary 70.6% (6058 / 8582) 59.2% 65.4% ➖

Generated from Vitest @vitest/coverage-v8 (frontend/src).

@github-actions

Copy link
Copy Markdown

Code Coverage

Package Line Rate Complexity Health
marreq-core.src.repository 88% 0 ✔
marreq-cloud.src.services 91% 0 ✔
marreq-core.src.helper_functions 95% 0 ✔
marreq-core.src.api 83% 0 ✔
marreq-core.src.models 80% 0 ✔
marreq-cloud.src 71% 0 ➖
marreq-core.src.services.semantic_search 75% 0 ➖
marreq-core.src.validation 96% 0 ✔
marreq-core.src.repository.cache 100% 0 ✔
marreq-core.src.deployment 84% 0 ✔
marreq-core.src.services 93% 0 ✔
marreq-core.src.storage 93% 0 ✔
marreq-core.src.auth.guards 57% 0 ➖
marreq-core.src.fairings 90% 0 ✔
marreq-core.src 80% 0 ✔
marreq-cloud.src.api 78% 0 ✔
marreq-core.src.diff 100% 0 ✔
marreq-core.src.importers 74% 0 ➖
marreq-core.src.reqif 97% 0 ✔
marreq-core.src.auth 90% 0 ✔
marreq-cloud.src.fairings 100% 0 ✔
marreq-core.src.routes 61% 0 ➖
marreq-core.src.use_case_document 94% 0 ✔
marreq-core.src.reports 95% 0 ✔
marreq-core.src.auth.rate_limiter 73% 0 ➖
marreq-core.src.repository.diesel_repo 5% 0 ❌
marreq-server.src 57% 0 ➖
marreq-core.src.generators 90% 0 ✔
Summary 81% (45269 / 56212) 0 ✔

@mariusmm
mariusmm merged commit 5f5f7a3 into main Oct 29, 2026
12 checks passed
@mariusmm
mariusmm deleted the feat/issue-250-cache-admin-ui branch October 29, 2026 14:02
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.

[Feature]: Cache administration UI in the SPA

1 participant