Skip to content

perf(search-index): read source files concurrently and write the index compact - #1240

Merged
jeff-r2026 merged 2 commits into
Tencent:mainfrom
SaulMoro:perf/1239-index-concurrent-reads
Oct 21, 2026
Merged

jeff-r2026 merged 2 commits into
Tencent:mainfrom
SaulMoro:perf/1239-index-concurrent-reads

Conversation

@SaulMoro

Copy link
Copy Markdown
Collaborator

Summary

Step 1 of #1239. The full index rebuild that every pull runs gets cheaper, with no cache, so it cannot serve a stale index. Reusing unchanged entries (step 2) is left out until this lands and #1233 is settled.

 indexInMemory
   collectLearningsEntries               # roots in precedence order, as before
     collectFlatMdEntries / namespaces
-      for each file: await entryFromMdFile
+      entriesFromMdFiles                # 32 reads in flight, each result kept in its listing slot
   collectListedMdEntries / collectRecursiveMdEntries / collectSkillDirEntries
-    for each file: await entryFromMdFile
+    entriesFromMdFiles
 buildIndex
-  writeJsonAtomic(index)                # indented, 15.4 MB at 5,000 learnings
+  writeJsonAtomic(index, compact)       # 8.6 MB

Entries and the unreadable files named in the warning keep the order a sequential read gives, because that order breaks score ties. The previous index is loaded once and shared, because reads.kept dedupes previous entries by object identity.

Evidence

Real CLI, unchanged pull on a synthetic team repo with 5,000 learnings, 6 runs per side, base and branch alternating:

  • Before: index build 1152 ms [1085-1392], pull 1552 ms
    After: index build 902 ms [894-925], pull 1288 ms

The parsed index, without builtAt and elapsedMs, hashes the same on both sides at 1,000 and 5,000 learnings. All 24 pulls exit 0 with no warnings. A CPU profile of the new build shows main-thread idle down from 243 ms to 23 ms. The build is now bound by tokenization on one core (57%). Reading and parsing the compact index takes 27 ms, against 35 ms for the indented one.

New test search-index-read-order.test.ts:

mock readFile: hold every read until all 7 have settled, release them in reverse
build docs [d1, gone1, d2, gone2, d3, gone3, d4]
expect entries      == [d1, d2, d3, d4]
expect warning list == gone1, gone2, gone3

It fails on the first version of this change, which recorded unreadable files as reads finished. It also fails on a mutation that pushes entries as reads finish. It passes on this branch. tsc --noEmit, lint, and the full unit suite (11320 tests) pass.

Merge Danger

Door: two-way

Reverting the two commits restores the sequential build. The next pull rewrites the index, and every reader parses either format.

Blast Radius: index-build

  • Every reader goes through JSON.parse. A person or script that reads search-index.json line by line sees one line now.
  • Up to 32 source files are held in memory at once before truncation to 50 KB, where it used to be one. This matters only for very large generated docs.
  • The skillsDir walk, used only by viz, stays sequential.
  • Not measured: NFS, a cold page cache.

Refs #1239

The pretty-printed index is 15.4 MB at 5,000 learnings and 8.6 MB compact.
Every reader parses it with JSON.parse, so the format does not matter to
them, and a load reads and parses 24% faster.

Refs Tencent#1239
A build read and parsed each file before it opened the next, so at 5,000
learnings the main thread waited on the disk for about 240 ms. The
collectors now read through one helper that keeps 32 reads in flight. It
returns the entries, and records the unreadable files, in listing order,
because that order breaks score ties and names the files in the warning.

The previous index is loaded once and shared, so concurrent reads put
back the same entry objects.

Refs Tencent#1239
@github-actions

Copy link
Copy Markdown

No findings in the specified PR diff.

The PR description documents sufficient testing: representative real-CLI pull runs, matching parsed-index hashes, successful exits without warnings, and type-check, lint, and unit-test results.

Review was read-only; I did not run, build, or install anything from the PR.

@jeff-r2026
jeff-r2026 merged commit 1494bdf into Tencent:main Oct 21, 2026
14 checks passed
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.

2 participants