4.5 KiB
4.5 KiB
spec, created, status
| spec | created | status |
|---|---|---|
| docs/specs/system-page/requirements/storage-maintenance.md | 2026-07-23 | implemented |
Implementation Plan: Storage Overview Cache and Settings Stability
Overview
Put a concurrency-safe 15-minute cache in front of the existing typed overview providers, then expose the snapshot timestamp through the existing Storage API. Manual Analyze uses the same cache owner but forces a refresh. The frontend displays snapshot age, hydrates the external Go-cache path from persisted settings, and separates policy-save blocking from unrelated analysis and cleanup activity.
Backend
Cached overview contract
- Add
apps/backend/internal/system/storage/overview_cache.gowith anOverviewSnapshotcontainingSummaryandAnalyzedAt, plus cached and forced-refresh methods around the existing provider. - Use a 15-minute default TTL, cache only successful scans, and serialize concurrent cache misses so page refreshes do not duplicate provider work.
- Keep the snapshot process-local. A backend restart starts with an empty cache.
API and composition
- Update
apps/backend/internal/system/storage/handler.gosoGET /storagereturns cachedsummaryandanalyzed_at. - Update
apps/backend/internal/system/storage/operations.goso manual Analyze calls the forced refresh path and records that summary in the existing run. - Wrap
storageOverviewonce inapps/backend/internal/backendapp/storage_maintenance.go, then pass the same cache owner to the handler and operations so forced analysis immediately updates subsequent GET responses. - Update handler/composition fakes that implement the overview interface.
Frontend
Contracts and analysis card
- Add
analyzed_attoStorageOverviewResponseinapps/web/lib/types/system.ts. - Update
apps/web/components/settings/system/storage/storage-overview-card.tsxto render “Last analyzed …” using the sharedformatRelativeTimehelper and an absolute timestamp tooltip. - Preserve the existing loading spinner while the first post-startup snapshot is measured.
Policy and external Go cache
- In
storage-policy-card.tsx, initialize the external-cache input fromsavedSettings.go_cache.adopted_pathand synchronize later persisted changes without overwriting an in-progress user edit. - In
storage-maintenance-settings.tsx, distinguish policy-mutating pending actions from analysis, cleanup, restore, and delete jobs. Those jobs must not disable policy editing. - Do not mark the contributor invalid because its own save is in progress. If adoption is the conflicting mutation, show a specific adoption message rather than the generic current-action text.
Tests
- Cache freshness and forcing:
apps/backend/internal/system/storage/overview_cache_test.goverifies one source call within 15 minutes, refresh after expiry, forced refresh inside the TTL, successful-snapshot timestamps, failed-refresh retention, and concurrent miss coalescing. - Handler/operations wiring: focused tests in
handler_test.goandoperations_test.goverifyanalyzed_atoutput and that manual Analyze uses the forced path. - External path hydration and save blocking: existing Storage component tests verify the adopted path survives rerender/reopen and analysis/cleanup do not disable policy controls or the shared Save action.
- Frontend contract: update Storage fixtures and API tests for
analyzed_at.
E2E Tests
- Extend
apps/web/e2e/tests/system/storage-maintenance.spec.tsto count overview measurements across page refresh/save and verify manual Analyze produces a newer displayed timestamp. - Extend
apps/web/e2e/tests/system/mobile-storage-maintenance.spec.tsto verify the relative analysis timestamp and persisted external-cache field remain usable without horizontal overflow.
Implementation Waves
Wave 1:
Wave 2:
Wave 3:
Risks
- Summary values can intentionally lag newly saved thresholds or an adopted path until manual
Analyze or TTL expiry;
analyzed_atmakes that staleness visible. - The cache must be shared by GET and manual Analyze. Separate wrappers would leave GET stale after a forced scan.
- Pending-state changes must retain mutual exclusion for adoption and saving without coupling policy controls to cleanup jobs that only consume a settings snapshot.