From 56e102b0774d371fd8d124f7c49984f50a584b9e Mon Sep 17 00:00:00 2001 From: Hassan Abdel-Rahman Date: Mon, 18 May 2026 18:12:29 -0400 Subject: [PATCH] Indexer: bounded-parallel pre-warm (concurrency=4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the serial `for (let moduleUrl of toWarm)` loop in `preWarmModulesTable` with a bounded-parallel sweep at concurrency = 4. **Bound rationale.** Matches the prerender server's default `#fileAdmissionCap` (`affinityTabMax − 1`, default 4 with the default `PRERENDER_AFFINITY_TAB_MAX=5`). Tying parallelism to the per-affinity file-admission cap keeps pre-warm from oversubscribing the realm's tab budget — at most `#fileAdmissionCap` file/render tabs can be in flight in the realm's affinity, and pre-warm borrows the same headroom strictly *before* the visit phase starts. The visit phase pays no penalty because the pre-warm has fully drained by the time the first visit fires. Higher concurrency re-introduces the prerender-server contention that motivated the original serial shape (an earlier experimental parallel pre-warm regressed reindex time on these realms — that experiment ran with the *wrong* set of modules in `toWarm`, but the contention story still applied for the modules it did warm). Lower concurrency leaves prerender tabs idle for the duration of the sweep. **No semantic change.** The set of URLs in `toWarm` is unchanged, the per-call `getCachedDefinitions` invocation is unchanged, and the failure handling (`failed += 1`, log on any non-zero) is unchanged. Only the loop shape differs. The `pre-warm complete` perfLog line now also reports `concurrency=N` so wall-time deltas between serial and parallel runs are easy to attribute. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/runtime-common/index-runner.ts | 40 +++++++++++++++++++------ 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/packages/runtime-common/index-runner.ts b/packages/runtime-common/index-runner.ts index 1e06d50587..cfc9d7f8a6 100644 --- a/packages/runtime-common/index-runner.ts +++ b/packages/runtime-common/index-runner.ts @@ -675,15 +675,37 @@ export class IndexRunner { } let failed = 0; - for (let moduleUrl of toWarm) { - try { - await this.#definitionLookup.getCachedDefinitions(moduleUrl, { - priority: this.#jobPriority, - }); - } catch { - failed += 1; + // Bounded-parallel sweep. The bound matches the default + // `#fileAdmissionCap` of the prerender server's PagePool + // (`affinityTabMax − 1`, default 4). Tying it to the + // file-admission cap keeps the parallel pre-warm from + // oversubscribing the per-affinity tab budget — at most + // `#fileAdmissionCap` file/render tabs will be in flight in the + // realm's affinity, and pre-warm modules borrow the same headroom + // before the visit phase starts. The visit phase pays no penalty + // because pre-warm runs strictly before it. Concurrency above 4 + // re-introduces the contention that motivated the original serial + // shape; below 4 leaves the prerender server's idle tabs unused + // for the duration of the sweep. + let prewarmConcurrency = 4; + let urls = [...toWarm]; + let cursor = 0; + let worker = async () => { + while (cursor < urls.length) { + let i = cursor++; + let moduleUrl = urls[i]; + try { + await this.#definitionLookup.getCachedDefinitions(moduleUrl, { + priority: this.#jobPriority, + }); + } catch { + failed += 1; + } } - } + }; + await Promise.all( + Array.from({ length: Math.min(prewarmConcurrency, urls.length) }, worker), + ); if (failed > 0) { this.#log.warn( `${jobIdentity(this.#jobInfo)} ${failed} of ${toWarm.size} module pre-warm lookups failed; the visit phase will retry on-demand if needed`, @@ -691,7 +713,7 @@ export class IndexRunner { } this.#perfLog.debug( - `${jobIdentity(this.#jobInfo)} pre-warm complete in ${Date.now() - preWarmStart} ms (candidates=${toWarm.size} failed=${failed})`, + `${jobIdentity(this.#jobInfo)} pre-warm complete in ${Date.now() - preWarmStart} ms (candidates=${toWarm.size} failed=${failed} concurrency=${prewarmConcurrency})`, ); }