Local search index: up to ~80 MB in memory, and rewritten on every post once full #232

Closed
opened 2026-10-21 19:08:08 +00:00 by littlefox · 1 comment
Owner

Found in the codebase review (#230, D-058). Relevant to the big-meeting reports (#227, #228).

kom_client::search keeps up to CAP = 20_000 posts with up to BODY_CHARS = 4000 characters of body each, all in memory in INDEX (app/src/search_view.rs). Full, that is up to ~80 MB of strings.

  1. Every search, every keystroke, Index::search builds format!("{subject}\n{author}\n{conf}\n{body}").to_lowercase() for each of the 20 000 entries. That allocates and lowercases up to ~80 MB per query.
  2. Desktop, once the cap is reached: search_view::note runs for every post shown or previewed, so each new post drops the oldest. A drop makes search_store::save read the whole index file, filter it and write it back, synchronously on the UI thread. Reading through a big meeting with a full index rewrites tens of MB for every card.
  3. Web: every note runs a new document::eval that opens the IndexedDB database again and runs its own transaction, once per post. A thread with 100 cards gives 100 evals and 100 opens.
  4. Login: the whole index is loaded through the eval bridge (IndexedDB getAll → JSON → Vec<String>) and parsed before search works.

Directions:

  • keep a lowercased copy (or a small word index) per entry, built once;
  • keep less body per post (the snippet needs ~120 characters around a hit; matching needs words, not the full text);
  • batch saves: collect notes and flush every few seconds, in one transaction / one append, off the UI thread on desktop;
  • desktop: a format where dropping old posts doesn't mean rewriting everything, for example compacting only once in a while, or SQLite;
  • cap memory as well as entries.

Measure with #225: memory at a full index, and the time of a search and of a card shown.

Found in the codebase review (#230, D-058). Relevant to the big-meeting reports (#227, #228). `kom_client::search` keeps up to `CAP = 20_000` posts with up to `BODY_CHARS = 4000` characters of body each, all in memory in `INDEX` (`app/src/search_view.rs`). Full, that is up to ~80 MB of strings. 1. **Every search, every keystroke**, `Index::search` builds `format!("{subject}\n{author}\n{conf}\n{body}").to_lowercase()` for each of the 20 000 entries. That allocates and lowercases up to ~80 MB per query. 2. **Desktop, once the cap is reached:** `search_view::note` runs for every post shown or previewed, so each new post drops the oldest. A drop makes `search_store::save` read the whole index file, filter it and write it back, **synchronously on the UI thread**. Reading through a big meeting with a full index rewrites tens of MB for every card. 3. **Web:** every `note` runs a new `document::eval` that opens the IndexedDB database again and runs its own transaction, once per post. A thread with 100 cards gives 100 evals and 100 opens. 4. **Login:** the whole index is loaded through the eval bridge (IndexedDB `getAll` → JSON → `Vec<String>`) and parsed before search works. Directions: - keep a lowercased copy (or a small word index) per entry, built once; - keep less body per post (the snippet needs ~120 characters around a hit; matching needs words, not the full text); - batch saves: collect notes and flush every few seconds, in one transaction / one append, off the UI thread on desktop; - desktop: a format where dropping old posts doesn't mean rewriting everything, for example compacting only once in a while, or SQLite; - cap memory as well as entries. Measure with #225: memory at a full index, and the time of a search and of a card shown.
Author
Owner

Merged in #267; main run #447 green. Verified: search and mentions specs on three profiles, incl. the index kept after a reload; unit tests incl. dropping a thousand at once; tools/perf.sh: hits 25 → 16 ms on a ~700-post index.

Merged in #267; main run #447 green. Verified: search and mentions specs on three profiles, incl. the index kept after a reload; unit tests incl. dropping a thousand at once; `tools/perf.sh`: hits 25 → 16 ms on a ~700-post index.
Sign in to join this conversation.
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
littlefox/neokom#232
No description provided.