Measure and bound Capacity history performance for long-running data
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 0
- Forks
- 0
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 6
Description
Context
Capacity history intentionally keeps observations in append-only JSONL until the user explicitly clears them. We should preserve that behavior unless measured evidence justifies a different retention or storage policy.
Raw storage growth is expected to remain modest for typical use, but several hot paths still scale with the total retained history. This issue tracks reproducible measurement and the smallest evidence-backed changes needed for long-running histories.
Current behavior
The current implementation has these unbounded paths:
JSONLAppendRecovery.repairUnterminatedTailmaterializes the existing file before every append, including the normal terminated-file case.CapacityHistoryStore.readAll()materializes the complete JSONL file, decodes every row, and sorts all observations.- While the Capacity history window is open, each successful persisted observation increments the recorder revision and triggers
refreshAndReconcile(), which callsreadAll()again. - The window's 30-second
TimelineViewupdate filters the retained observations and rebuildsCapacityHistoryProjection; the projection filters and sorts the selected limit's full history before extracting the visible range. - CSV export reads all observations, filters and sorts them, builds an array of row strings, and joins them into one in-memory
String.
One concern from the original design note is already mitigated: historical chart marks are reduced through CapacityHistoryVisualSamplingPolicy before being passed to Swift Charts. A 30-day heartbeat series is therefore no longer rendered as one mark per stored heartbeat. Measurement should still include sampling and projection cost, especially across many segments, but should not assume the old raw-mark count.
Measurement plan
Create deterministic, current-schema fixtures of approximately 10 MB, 25 MB, and 50 MB in a temporary location. Fixtures should be chronological and include realistic combinations of:
- five-minute heartbeats;
- Capacity changes;
- gaps and session boundaries;
- reset boundaries;
- more than one supported limit where applicable.
Do not seed or modify a user's real Capacity history file.
Measure in a release build:
- initial history-window presentation;
- append persistence time and append-to-chart latency while the window is open;
- switching between Current Window, 24 Hours, 7 Days, and 30 Days;
- idle CPU and projection work while the window remains open;
- peak resident memory;
- CSV export duration and peak resident memory.
Record the fixture definition, hardware/OS context, number of observations and segments, repeated-run method, and results so the measurements are reproducible.
Candidate optimization order
Only implement changes justified by the measurements. Compare the smallest options first:
- Avoid materializing the full file merely to validate the append tail, while preserving recovery of valid or torn unterminated final rows.
- Make chronological ordering an explicit invariant, remove repeated full-history sorts, and use binary search or an equivalent range slice for the visible viewport.
- Apply successful appends incrementally to the view model instead of reloading the entire file.
- Consider range-oriented reads or time-based segments only if the simpler changes are insufficient.
- Stream CSV rows directly to the destination instead of constructing the complete export in memory.
Non-goals
- Automatically deleting old history without evidence and an explicit product decision.
- Adding a separate Capacity authority, reset history, or history-only polling.
- Moving to SQLite or another larger persistence mechanism before measurements require it.
- Changing the existing JSONL schema or invalidating stored history as an incidental optimization.
- Removing visual sampling that already bounds ordinary chart mark density.
Acceptance criteria
- Deterministic fixtures and a documented measurement procedure reproduce the current limits.
- Baseline results identify which path becomes material first and establish an explicit target data size and interaction budget.
- Current Window, 24 Hours, 7 Days, and 30 Days remain practical at the accepted target size.
- Recording and append persistence are not blocked by chart projection or export work.
- Peak memory during open, refresh, range switching, and export stays within the adopted budget.
- Existing history remains readable; any data-loss risk or schema/storage change is justified and tested separately.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating JSONLAppendRecovery.repairUnterminatedTail, CapacityHistoryStore.readAll(), CapacityHistoryProjection, CapacityHistoryVisualSamplingPolicy, and the TimelineView refresh path. Create chronological 10 MB, 25 MB, and 50 MB fixtures in a temporary location, then run the listed interactions and exports in a release build. Done means reproducible documented baseline results, an adopted target budget, and only measurements-backed changes that preserve existing history.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- performance
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100