PERF: Search adopts shared revision, snapshot, mutation and delta contracts (#663) #675

Open
opened 2026-10-02 05:36:20 +00:00 by kayg · 1 comment
Owner

Context: #663 matrix, DESIGN §58 rules 3–6. This is the Search adapter issue. The only shared owners are #665 revision cache/ETag, #666 view snapshots, #667 optimistic client IDs/Undo receipts and #668 change stream/deltas. Depend on their public contracts; do not implement competing primitives.

Evidence at c4a61e8cf0:

  • R3: GET /api/v1/search; apps/web/src/lib/search/SearchPreview.svelte:173. 80-entry cache keyed only by target.id, with no revision/byte/User key or session-clear handler in this module. Needs #555 lifecycle and #665.
  • R4: POST/PATCH/DELETE /api/v1/search/saved; apps/web/src/lib/search/saved.svelte.ts:125. Saved-search create/update/remove await the API before changing items (:126/:133/:140). No client-ID durable receipt/Undo or synchronous retained-query update.
  • R5: GET /api/v1/search; apps/web/src/lib/search/window.svelte.ts:408. Queries pull full answers and saved counts are refreshed separately. No shared delta applies changed IDs/revisions while keeping unchanged hits by identity.
  • R6: /search?q=…; apps/web/src/lib/search/window.svelte.ts:1. Query state is retained in the window, but no shared byte/row LRU for query/facet/result cursor, preview and scroll. Existing 80-preview cache is partial reuse.

Expected:

  • Adopt #665 for list/detail revisions and header-complete rows. Cached open is synchronous, keeps object identity and makes zero requests. Authorize before conditional 304; User switch/revoke/plugin disable removes affected retained data.
  • Adopt #666 for a byte/row-bounded complete view snapshot. Restore before await, then one bounded catch-up; no blank/spinner over already-seen data. Deep-link intent and keyboard focus override a retained view when needed.
  • Adopt #667 for create/rename/pin/delete a saved search and update hits after an underlying item mutation; restore query, facets, selected result, preview and scroll. Changes publish to every visible/retained representation in one frame. Client IDs survive lost acknowledgement; Undo uses the durable receipt and cannot erase an intervening change. Definite rejection rolls back; timeout remains pending until receipt reconciliation. Do not add offline editing.
  • Adopt #668: one per-User wake-up, coalesced capped deltas, stable unchanged objects and deletion tombstones. Stop full-refetch fan-out and timer refreshes only after the delta covers their correctness duties.

Tests:

  • Production API/e2e for the listed actions and views, including stale 304, lost acknowledgement, duplicate ID, Undo after reconnect/restart, obsolete response, empty/error/offline state and cross-view consistency. Existing status/assertion expectations stay unchanged.
  • Two Users, session switch, share revoke and plugin disable cannot restore denied rows. Keep authorization/step-up and server-only writes through calternal-fs.
  • Pointer/touch (≥44 px), keyboard focus/Enter/Space/Escape, screen-reader name/role/state, Copy link and shortcuts work. Keyboard motion keeps shared durations (#611).
  • Extend #549/#641 profiles rather than create another harness. ≥5 locked production/HDD cold and warm samples; median/p95/max, CPU/RSS and large realistic data plus burst. Cached open ≤100 ms, accepted action ≤150 ms, warm return ≤100 ms, first usable view ≤1.5 s at 10k items. Warm blaze: zero incomplete frames; collect identity/unpainted/long-task/heap/RSS metrics.

Reuse and active work: Existing search-dialog virtualization (60-row threshold), SearchPreview 80-entry LRU, #639 Note-link opening and #641 blaze harness. Reuse them; add no second search interface.

Measurement scope: the production/HDD read table on #663 supplies a representative endpoint result, not proof that a complete Search view meets all budgets. Rule 7/9 findings remain with #641/#549 and the existing surface jobs. This follow-up integrates their results and adds shared-contract acceptance after they land.

Representative measurement from the audit (not a full-view budget result):
/api/v1/search?q=log&limit=100&semantic=false, five serial requests after fixture readiness, all HTTP 200. Median/p95/max 49.3/55.3/55.3 ms; five-request burst median/p95/max 52.1/54.2/54.2 ms. Serial window server CPU 130 ms, RSS 515657728 bytes; no ETag on these sampled responses. Load inside lock 3.69/2.16/0.96.
Shared release server source cc25c441b7a974185622a1dee853cf38686d2b67, binary SHA-256 2f3567d91c34839851247bc0acbc25a56aaacd14dca269b8f0342ddf83447ed9; embedded production SPA; Chromium browser on the build host through SSH/HTTPS. Server/Home/Index on perf VM HDD emulator: direct-I/O loop, 8 ms read/write dm-delay, 200 IOPS and 150 MiB/s caps. Every measured phase held flock -w 14400 /root/perf.lock. Qualification QD1 115.3 IOPS/8.028 ms median, QD16 200.7 IOPS/96.993 ms. Fixture: 366 Daily notes, 10,980 Logs, 100 Files/Photos, 20 Notes/Tasks, three Budgets and 100 transactions; Mail empty, Admin one User.
Structural source evidence above is the newer audit base, not the measured binary revision. No claim that these revisions are equivalent. The baseline in docs/perf/baseline.json uses another fixture/build/transport; no regression ratio is valid here. See #663 for matching baseline endpoint values and coverage gaps.

Context: #663 matrix, DESIGN §58 rules 3–6. This is the Search adapter issue. The only shared owners are #665 revision cache/ETag, #666 view snapshots, #667 optimistic client IDs/Undo receipts and #668 change stream/deltas. Depend on their public contracts; do not implement competing primitives. Evidence at c4a61e8cf090170f35b1bed3350d9de20c83ecd5: - R3: `GET /api/v1/search`; `apps/web/src/lib/search/SearchPreview.svelte:173`. 80-entry cache keyed only by target.id, with no revision/byte/User key or session-clear handler in this module. Needs #555 lifecycle and #665. - R4: `POST/PATCH/DELETE /api/v1/search/saved`; `apps/web/src/lib/search/saved.svelte.ts:125`. Saved-search create/update/remove await the API before changing items (:126/:133/:140). No client-ID durable receipt/Undo or synchronous retained-query update. - R5: `GET /api/v1/search`; `apps/web/src/lib/search/window.svelte.ts:408`. Queries pull full answers and saved counts are refreshed separately. No shared delta applies changed IDs/revisions while keeping unchanged hits by identity. - R6: `/search?q=…`; `apps/web/src/lib/search/window.svelte.ts:1`. Query state is retained in the window, but no shared byte/row LRU for query/facet/result cursor, preview and scroll. Existing 80-preview cache is partial reuse. Expected: - Adopt #665 for list/detail revisions and header-complete rows. Cached open is synchronous, keeps object identity and makes zero requests. Authorize before conditional 304; User switch/revoke/plugin disable removes affected retained data. - Adopt #666 for a byte/row-bounded complete view snapshot. Restore before await, then one bounded catch-up; no blank/spinner over already-seen data. Deep-link intent and keyboard focus override a retained view when needed. - Adopt #667 for create/rename/pin/delete a saved search and update hits after an underlying item mutation; restore query, facets, selected result, preview and scroll. Changes publish to every visible/retained representation in one frame. Client IDs survive lost acknowledgement; Undo uses the durable receipt and cannot erase an intervening change. Definite rejection rolls back; timeout remains pending until receipt reconciliation. Do not add offline editing. - Adopt #668: one per-User wake-up, coalesced capped deltas, stable unchanged objects and deletion tombstones. Stop full-refetch fan-out and timer refreshes only after the delta covers their correctness duties. Tests: - Production API/e2e for the listed actions and views, including stale 304, lost acknowledgement, duplicate ID, Undo after reconnect/restart, obsolete response, empty/error/offline state and cross-view consistency. Existing status/assertion expectations stay unchanged. - Two Users, session switch, share revoke and plugin disable cannot restore denied rows. Keep authorization/step-up and server-only writes through calternal-fs. - Pointer/touch (≥44 px), keyboard focus/Enter/Space/Escape, screen-reader name/role/state, Copy link and shortcuts work. Keyboard motion keeps shared durations (#611). - Extend #549/#641 profiles rather than create another harness. ≥5 locked production/HDD cold and warm samples; median/p95/max, CPU/RSS and large realistic data plus burst. Cached open ≤100 ms, accepted action ≤150 ms, warm return ≤100 ms, first usable view ≤1.5 s at 10k items. Warm blaze: zero incomplete frames; collect identity/unpainted/long-task/heap/RSS metrics. Reuse and active work: Existing search-dialog virtualization (60-row threshold), SearchPreview 80-entry LRU, #639 Note-link opening and #641 blaze harness. Reuse them; add no second search interface. Measurement scope: the production/HDD read table on #663 supplies a representative endpoint result, not proof that a complete Search view meets all budgets. Rule 7/9 findings remain with #641/#549 and the existing surface jobs. This follow-up integrates their results and adds shared-contract acceptance after they land. Representative measurement from the audit (not a full-view budget result): `/api/v1/search?q=log&limit=100&semantic=false`, five serial requests after fixture readiness, all HTTP 200. Median/p95/max 49.3/55.3/55.3 ms; five-request burst median/p95/max 52.1/54.2/54.2 ms. Serial window server CPU 130 ms, RSS 515657728 bytes; no ETag on these sampled responses. Load inside lock 3.69/2.16/0.96. Shared release server source `cc25c441b7a974185622a1dee853cf38686d2b67`, binary SHA-256 `2f3567d91c34839851247bc0acbc25a56aaacd14dca269b8f0342ddf83447ed9`; embedded production SPA; Chromium browser on the build host through SSH/HTTPS. Server/Home/Index on perf VM HDD emulator: direct-I/O loop, 8 ms read/write dm-delay, 200 IOPS and 150 MiB/s caps. Every measured phase held `flock -w 14400 /root/perf.lock`. Qualification QD1 115.3 IOPS/8.028 ms median, QD16 200.7 IOPS/96.993 ms. Fixture: 366 Daily notes, 10,980 Logs, 100 Files/Photos, 20 Notes/Tasks, three Budgets and 100 transactions; Mail empty, Admin one User. Structural source evidence above is the newer audit base, not the measured binary revision. No claim that these revisions are equivalent. The baseline in docs/perf/baseline.json uses another fixture/build/transport; no regression ratio is valid here. See #663 for matching baseline endpoint values and coverage gaps.
Author
Owner

Client audit for #663; #665/#675 already own this preview cache work.

At origin/dev c4a61e8cf0 and merge-round-7a 2f4482ded0, SearchPreview.svelte:3–8 caps loaded previews at 80 entries but not bytes; :173–179 retrieves by result ID without revision. Cached previews return immediately; the 70 ms debounce only applies to misses and prevents fetch-per-arrow. Adopt #665 with revision and byte caps rather than a second cache. Test repeated open after Note edit and long traversal of large preview bodies.

search-dialog.svelte:182–195 virtualises expanded results over 60 rows but scans offsets to find the start on scroll. SearchWindow's MAX_LIMIT is 200 (window.svelte.ts:64), so this is bounded and low priority, not an unbounded-list finding. No timing or GPU cost is claimed. The full audit is audit-findings.md on job/perf-arch-client.

Client audit for #663; #665/#675 already own this preview cache work. At origin/dev c4a61e8cf090170f35b1bed3350d9de20c83ecd5 and merge-round-7a 2f4482ded066d9c5d9c59130377907f7fd2916c9, SearchPreview.svelte:3–8 caps loaded previews at 80 entries but not bytes; :173–179 retrieves by result ID without revision. Cached previews return immediately; the 70 ms debounce only applies to misses and prevents fetch-per-arrow. Adopt #665 with revision and byte caps rather than a second cache. Test repeated open after Note edit and long traversal of large preview bodies. search-dialog.svelte:182–195 virtualises expanded results over 60 rows but scans offsets to find the start on scroll. SearchWindow's MAX_LIMIT is 200 (window.svelte.ts:64), so this is bounded and low priority, not an unbounded-list finding. No timing or GPU cost is claimed. The full audit is audit-findings.md on job/perf-arch-client.
Sign in to join this conversation.
No labels
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#675
No description provided.