Memory: an 8 MiB cache for the cold fill (#398) #429

Closed
kayg wants to merge 0 commits from issue/398-cold-cache into dev
Owner

Part of #398, the cold-open peak. Based on #417.

What

A big batch into the list store ran with a 64 MiB SQLite page cache, and so did making its indexes after a fresh fill. SQLite's sorter for CREATE INDEX takes as much again. A heap profile of a cold 100,000-note open (staging, heaptrack) showed those two at 137 MB of the 203 MB heap peak. With 8 MiB the heap peak is 116 MB.

Numbers

Perf VM, 100k notes, 3 runs each, with every other #398 PR applied in both columns:

metric 64 MiB 8 MiB
cold.mem_peak MB 297 196
cold.index s 24.5 24.5
cold.disk_written MB 1,221 1,305
idle.rss MB 39.8 41.9
mem.peak MB p50 / p95 98.2 / 109 101 / 109

Warm runs don't touch this path, so their latencies are unchanged apart from run-to-run noise.

Still above the 150 MB target

The rest of the cold peak's 116 MB heap comes from:

  • the parsed links held for a batch of notes before it is stored: 57 MB across the 64 readers' chunks and the 4,096-note batch;
  • the scan's file table: 8.5 MB;
  • the id index: 11 MB.

On top of that, about 75 threads add allocator slack. Two things didn't help:

  • a 1,024-note batch;
  • 16 or 8 readers instead of 64, which saved 10 to 20 MB on staging and left the thread count about the same, because search's catch-up reads 64 at once.

The next step is to make the links smaller while a batch is held.

Checks: cargo fmt, clippy -D warnings and the workspace tests are clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_018Yyy8fQGxPLkLa8v8NXz7T

Part of #398, the cold-open peak. **Based on #417.** ## What A big batch into the list store ran with a 64 MiB SQLite page cache, and so did making its indexes after a fresh fill. SQLite's sorter for `CREATE INDEX` takes as much again. A heap profile of a cold 100,000-note open (staging, heaptrack) showed those two at **137 MB of the 203 MB heap peak**. With 8 MiB the heap peak is 116 MB. ## Numbers Perf VM, 100k notes, 3 runs each, with every other #398 PR applied in both columns: | metric | 64 MiB | **8 MiB** | |---|---|---| | cold.mem_peak MB | 297 | **196** | | cold.index s | 24.5 | 24.5 | | cold.disk_written MB | 1,221 | 1,305 | | idle.rss MB | 39.8 | 41.9 | | mem.peak MB p50 / p95 | 98.2 / 109 | 101 / 109 | Warm runs don't touch this path, so their latencies are unchanged apart from run-to-run noise. ## Still above the 150 MB target The rest of the cold peak's 116 MB heap comes from: - the parsed links held for a batch of notes before it is stored: 57 MB across the 64 readers' chunks and the 4,096-note batch; - the scan's file table: 8.5 MB; - the id index: 11 MB. On top of that, about 75 threads add allocator slack. Two things didn't help: - a 1,024-note batch; - 16 or 8 readers instead of 64, which saved 10 to 20 MB on staging and left the thread count about the same, because search's catch-up reads 64 at once. The next step is to make the links smaller while a batch is held. Checks: `cargo fmt`, `clippy -D warnings` and the workspace tests are clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018Yyy8fQGxPLkLa8v8NXz7T
Memory: remove the whole-Home notes.list (#398)
Some checks failed
CI / server (pull_request) Successful in 33s
CI / rust (pull_request) Successful in 2m35s
CI / web (pull_request) Failing after 44m28s
44306033b1
`notes.list` without input built every note of the Home into one answer
(100,000 objects at 100k notes, hundreds of megabytes at once). The app
only asks for pages, and the form was deprecated "for one release".
Without input it now returns the first page of 50, like `{}`.

The perf harness no longer calls it: `home.first_list`, `home.list` and
`mem.after_list` are gone (`mem.after_first_page` instead), and the cold
run opens the Home with a first page (`cold.first_page`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Yyy8fQGxPLkLa8v8NXz7T
Memory: an 8 MiB cache for the cold fill, not 64 (#398)
Some checks failed
CI / server (pull_request) Successful in 24s
CI / rust (pull_request) Successful in 1m41s
CI / web (pull_request) Failing after 46m40s
e30a9b861a
A big batch into the list store and making its indexes after a fresh
fill ran with a 64 MiB page cache, and SQLite's sorter for `CREATE
INDEX` takes as much again: 137 MB of a cold 100,000-note open's 203 MB
heap peak (heaptrack on staging). With 8 MiB the cold open on the perf
VM peaks at 196 MB instead of 295, and indexing takes as long (25.6 s
against 24.9).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Yyy8fQGxPLkLa8v8NXz7T
Author
Owner

Landed in dev via train m.

Landed in dev via train m.
kayg closed this pull request 2026-10-15 16:19:50 +00:00
Author
Owner

Rotational-disk check (#398): on its own, this PR makes cold indexing about 30% slower on a rotational disk. The disk is sdb1 on the perf VM (ext4, ROTA=1), 100k notes, 3 runs: dev 41.6 s, this PR 55.2 s. On the SSD the time is unchanged.

#459, the shared read limit, wins it back: 44.8 s with all #398 PRs (6 runs), +7% against dev, and the peak drops to 123 MB. Please merge this PR together with #459 or after it, not on its own. A bigger cache only for making the indexes (32 or 64 MiB) didn't help on either disk.

**Rotational-disk check (#398):** on its own, this PR makes cold indexing about 30% slower on a rotational disk. The disk is sdb1 on the perf VM (ext4, ROTA=1), 100k notes, 3 runs: dev 41.6 s, this PR 55.2 s. On the SSD the time is unchanged. #459, the shared read limit, wins it back: 44.8 s with all #398 PRs (6 runs), +7% against dev, and the peak drops to 123 MB. Please merge this PR together with #459 or after it, not on its own. A bigger cache only for making the indexes (32 or 64 MiB) didn't help on either disk.
Some checks failed
CI / server (pull_request) Successful in 24s
CI / rust (pull_request) Successful in 1m41s
CI / web (pull_request) Failing after 46m40s

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal!429
No description provided.