Skip to content

Keep search responsive and show typeahead results sooner - #4402

Closed
brsbl wants to merge 10 commits into
mainfrom
bb/debug-why-search-and-typeahead-is-so-slow-in-bb-thr_kz4aksn579
Closed

brsbl wants to merge 10 commits into
mainfrom
bb/debug-why-search-and-typeahead-is-so-slow-in-bb-thr_kz4aksn579

Conversation

@brsbl

@brsbl brsbl commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Human comments

What was wrong

Implementation handoff: issue #4413. This PR is retained as an unmerged prototype and evidence reference for a colleague.

Full-text SQLite searches blocked the server. File typeahead repeated workspace discovery and sorting, while plugin mentions waited for the slowest provider.

What changed

  • The server stays responsive during full-text searches, with SQLite work in a read-only worker.
  • File suggestions reuse bounded listings for two seconds, refreshed by file watchers.
  • Mention providers return independently, allowing fast results to appear first.

Provider selection is additive; the existing unfiltered mention endpoint remains supported. The raw route allowance increases by 1 KiB.

An optional CI profiling job preserves reproducible fixtures, individual timings, packaged runtimes, and CPU profiles. Reproduction instructions.

How you verified

  • Remote CI passed at 3374bc3deb56c38e94e1c6ae9e2fcc8be305d327, including the paired profiling job.
  • Chrome for Testing 154.0.8037.57: matched keystroke-to-render samples, query replacement, hard reload, and provider arrival behavior.
  • One cumulative Slop Cop review found no P0/P1 blockers; three P2 follow-ups remain recorded below.

Trade-off: first search in a fresh packaged process increased from 143 to 245 ms median. Warm query time stayed about 132–136 ms; the worker removes server blocking without reducing SQL cost.

Matched timing and CPU evidence

Before: a8cc3d740016b9e55cc80e222745525b4bbe6a27 (merge base).
After: 3374bc3deb56c38e94e1c6ae9e2fcc8be305d327.

Download raw CI evidence: exact harness, fixture archive, runtime archives and hashes, machine manifest, individual samples, result hashes, logs, and 15 CPU profiles. CI retention is 90 days; a second copy is retained in this task's worktree.

Same four-vCPU Linux runner and Node 22.22.0; before/after/after/before order. One 54,000-segment SQLite fixture and a 10,000-file workspace. Cold means a fresh process and connection, not a flushed OS page cache. API latency excludes rendering, frontend debounce, and Connect.

Measurement Before p50 / p95 (ms) After p50 / p95 (ms) n per revision
cold-search startupMs 418.4 / 436.7 418.3 / 514.0 10 / 10
cold-search searchMs 142.7 / 167.2 245.0 / 299.9 10 / 10
warm-search searchMs 135.5 / 199.3 131.6 / 198.4 40 / 40
warm-search healthMs 120.5 / 183.8 1.3 / 6.3 40 / 40
cold-files ms 52.4 / 108.7 50.6 / 109.5 20 / 20
warm-files ms 56.2 / 75.0 37.0 / 46.3 80 / 80
mentions firstMs 1601.6 / 1602.6 21.2 / 22.0 20 / 20
mentions allMs 1601.6 / 1602.6 1601.7 / 1603.0 20 / 20

All comparable result hashes matched. CPU-profile runs are excluded from timings.

Browser measurements use the same isolated app fixture, route, 1280 × 900 viewport at 2×, and deterministic 20 ms/1,600 ms providers. Ten unique queries per revision, typed at 80 ms per character. Timing starts on the final input event and ends on the first animation frame containing the matching row. Recording and tracing runs are separate from these samples.

Browser measurement Before p50 / p95 After p50 / p95 n per revision
First provider rendered 1765.7 / 1780.7 ms 178.5 / 237.9 ms 10
Both providers rendered 1765.7 / 1780.8 ms 1759.0 / 1790.3 ms 10

CPU profiles locate about 1,213 ms of SQLite work on the main server before and 1,201 ms on the search worker after. The daemon's sampled node:path self-time fell from 268 to 51 ms across the matched profiling workload. These are sampled costs, not latency percentiles; startup and shutdown are present in the raw profiles. Browser traces and the exact UI fixture are retained with the task artifacts.

All comparable search results, ordering, snippets, totals, file suggestions, and provider groups had matching hashes. Focused regressions cover discovery reuse/invalidation/expiry, independent providers, compatibility, worker responsiveness/cancellation, later writes, and result ordering.

The initial final-head CI attempt had two command-palette lazy-loading timeouts. That shard passed on retry at the same commit; no test assertions or product code were changed to obtain the pass.

Slop Cop review and P2 follow-ups

Reviewed head: 6548367b68d8d68e9fa34da1f7754664a8fe1ab2.
Base: a19b3ed4f7f834a1ecaa474161d031de219f5005.
Scope: git diff a19b3ed4f7f834a1ecaa474161d031de219f5005 6548367b68d8d68e9fa34da1f7754664a8fe1ab2 (23 files).

Verdict: PASS WITH FOLLOW-UPS. This consumes the PR's one deliberate code review.

Panel Result
Security No findings
Architecture One P2 consistency race
Code quality Corroborated the same race; no additional findings
Performance Two P2 follow-ups
End-to-end Passed; existing exact-head Chrome evidence reused
  1. P2 — Reconcile search membership after hydration. Worker/hydration boundary: concurrent archive/hide writes can change thread metadata after matching. Hydration checks deletion but not visibility or archive membership, allowing transient mismatched groups or hidden hits. Follow-up: revalidate membership and define snapshot-total handling. Verify with interleaved persisted archive/hide writes. Architecture finding, corroborated by code quality.
  2. P2 — Keep pending file discovery coalesced during invalidation. Listing invalidation deletes pending entries. File writes during slow scans can let successive keystrokes launch duplicate recursive scans. Follow-up: preserve pending discovery while invalidating its cache eligibility. Verify overlapping requests share one scan, then refresh after it settles. Performance finding.
  3. P2 — Release the queue's last successful payload. Queue tail retains fulfilled rows, including complete message text, until another search or shutdown. Large matching messages can leave megabytes retained after search closes. Follow-up: make both queue-barrier outcomes resolve to undefined. Verify result delivery and rejection recovery remain intact. Performance finding.

P2s remain follow-ups; none were implemented.

The initial slimming pass retained all 26 files. A subsequent simplification discussion identified the 699 lines of profiling tooling as evidence infrastructure rather than required product code, plus redundant worker error/cleanup handling and a route result wrapper. No simplification edits were applied before implementation was handed to a colleague through #4413. The branch remains intact for reference and reproduction.

The completed Slop Cop review was reused; no second code review ran. Later work added the profiling harness and evidence, then resolved the bundle-budget conflict with main. CI-equivalent checks ran remotely.

Before and after

Before: a8cc3d7400 · After: 3374bc3deb · Captured at 2×.
Same fixture, route, theme, and query replacement, approximately 500 ms after the final keystroke.

Screen / state Viewport Before After
Mention results / partial arrival Desktop · 1280 × 900 Before: stale suggestions remain while providers finish After: matching fast suggestion appears first

Before recording · After recording

This prototype was not approved through user QA and has not been merged. Issue #4413 owns the replacement implementation and its validation.

BB-Thread-ID: thr_kz4aksn579

AGENT GENERATED

@brsbl

brsbl commented Sep 26, 2026

Copy link
Copy Markdown
Collaborator Author

Closing unmerged in favor of the detailed investigation and colleague handoff in #4413. That issue records original symptoms, controlled timings and CPU profiles, causes, trade-offs, known prototype risks, proposed fixes, and acceptance criteria. The branch is retained for code reference and reproduction.

@brsbl brsbl closed this Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant