Watcher: targeted incremental update instead of full-root rescan #48

Merged
parkerhdavis merged 1 commit from watcher-targeted-update into main 2026-06-02 00:30:54 -06:00
parkerhdavis commented 2026-06-02 00:30:47 -06:00 (Migrated from github.com)

On any file change, the watcher ran a full scan_root — a WalkDir stat of every file in the root — even though only a path or two changed. On a large library that made every editor save feel slow (the rescan, not the commit itself).

Change

  • indexer::update_paths(conn, root_id, root_path, &paths) — the same modify/insert/rename/delete semantics as scan_root, but only for the event's changed paths (no tree walk). All risk checks run read-only before any writes, so a fallback never leaves partial mutations. Returns None (caller falls back to a full scan_root) when the change set is ambiguous: a path is now a directory, a deleted path still has tracked children (a directory was removed), or the batch is large (bulk op).
  • library::spawn_targeted_update — runs update_paths through the existing per-root scan slot so it can't race a full scan (escalates to a pending full scan under contention), and emits the same scan:* events so the UI refreshes either way.
  • watcher.rs now passes the changed paths instead of discarding them.

Editor overwrite-saves and new files hit the fast path; renames keep asset identity/tags; deletes and bulk/directory ops stay correct via the full-scan fallback. Startup auto-scan and the manual Scan button are unchanged (still full scan_root). Thumbnails still regenerate lazily on next view (mtime-keyed cache).

Verification

make lint typecheck test green — 137 Rust tests pass (7 new: modify / insert / delete / rename / dir-fallback / dir-deletion-fallback / bulk-fallback), clippy clean.

Also folds in the decision to retire Packi in favor of Garnet (the adjust-math divergence from Packi is now permanent — no shared crate to reconcile).

On any file change, the watcher ran a full `scan_root` — a `WalkDir` stat of every file in the root — even though only a path or two changed. On a large library that made every editor save feel slow (the rescan, not the commit itself). ## Change - **`indexer::update_paths(conn, root_id, root_path, &paths)`** — the same modify/insert/rename/delete semantics as `scan_root`, but only for the event's changed paths (no tree walk). All risk checks run read-only *before* any writes, so a fallback never leaves partial mutations. Returns `None` (caller falls back to a full `scan_root`) when the change set is ambiguous: a path is now a directory, a deleted path still has tracked children (a directory was removed), or the batch is large (bulk op). - **`library::spawn_targeted_update`** — runs `update_paths` through the existing per-root scan slot so it can't race a full scan (escalates to a pending full scan under contention), and emits the same `scan:*` events so the UI refreshes either way. - **`watcher.rs`** now passes the changed paths instead of discarding them. Editor overwrite-saves and new files hit the fast path; renames keep asset identity/tags; deletes and bulk/directory ops stay correct via the full-scan fallback. Startup auto-scan and the manual Scan button are unchanged (still full `scan_root`). Thumbnails still regenerate lazily on next view (mtime-keyed cache). ## Verification `make lint typecheck test` green — 137 Rust tests pass (7 new: modify / insert / delete / rename / dir-fallback / dir-deletion-fallback / bulk-fallback), clippy clean. Also folds in the decision to retire Packi in favor of Garnet (the adjust-math divergence from Packi is now permanent — no shared crate to reconcile).
Sign in to join this conversation.
No reviewers
No labels
No milestone
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
parker/garnet!48
No description provided.