Give user index jobs a worker lane render sweeps cannot occupy#5530
Merged
Conversation
The worker-manager's user-index pool workers start with --indexJobsOnly, which restricts their handler registration to the indexing job types (from-scratch-index, incremental-index, copy-index). The queue's claim query only dequeues job types a worker has registered handlers for, so this makes the pool a dedicated indexing lane: a prerender_html job — whose realm-wide module pre-warm sweep can run for tens of seconds — can never occupy it, regardless of priority tier. User-initiated indexing and prerender-html share the same priority (a published realm's rendered HTML is as first-class as its search index), so a priority floor alone cannot separate the two kinds of work. Indexing latency is user-visible in ways rendering latency is not: createRealm blocks on the from-scratch index, and read-your-writes endpoints (card GET, _publishability) drain in-flight incremental indexing before responding. In the matrix Playwright suite, both Playwright workers funnel realm provisioning into one worker manager; when concurrent pre-warm sweeps and a large boot reindex hold every user-tier worker, a just-written card's incremental index job sits queued for 15-20s and the publish modal's private-dependency check outlives the test's assertion budget (publish-realm.spec.ts "it warns when private dependencies would cause host mode errors"). Diagnostics for the same failure class: - Router.handle logs thrown CardErrors before mapping them to an error response; a thrown 404 was previously indistinguishable in the request log from a deliberate not-found response. - The _publishability handler logs its drain duration and scan counts, attributing a slow or empty report to queue wait vs. the scan. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Contributor
There was a problem hiding this comment.
Pull request overview
This PR isolates user-initiated indexing work onto a dedicated worker “lane” by restricting handler registration for a configurable pool of workers, preventing long prerender sweeps from occupying all user-tier workers and delaying read-your-writes drains (e.g. _publishability) in CI.
Changes:
- Added an
indexJobsOnlyworker mode that registers handlers only for indexing job types, and used it for the worker-manager’suserIndexCountpool. - Improved CI diagnosability by logging when routed handlers throw
CardError, and by timing/logging the_publishabilityindexing-drain plus scan counts. - Added a unit test to pin the job-handler registration set for default vs
indexJobsOnlyworkers, and enabled the dedicated lane in the Matrix isolated realm-server harness.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| packages/runtime-common/worker.ts | Introduces INDEX_JOB_TYPES and indexJobsOnly to restrict handler registration to indexing jobs. |
| packages/runtime-common/router.ts | Logs when a handler throws CardError to distinguish thrown errors from deliberate responses in request logs. |
| packages/runtime-common/realm.ts | Adds timing and summary logging around _publishability’s incremental-index drain and scan results. |
| packages/realm-server/worker.ts | Adds --indexJobsOnly CLI flag and wires it through to Worker. |
| packages/realm-server/worker-manager.ts | Starts userIndexCount workers with --indexJobsOnly and preserves that behavior on respawn. |
| packages/realm-server/tests/worker-job-registration-test.ts | New test ensuring handler registration differs correctly between default and indexing-only workers. |
| packages/realm-server/tests/index.ts | Includes the new worker registration test in the realm-server test suite. |
| packages/matrix/support/isolated-realm-server.ts | Configures the Matrix harness to use a dedicated indexing-only worker lane. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
FadhlanR
approved these changes
Jul 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
The matrix Playwright suite runs each shard against one isolated realm server on
:4205, shared by both of Playwright's parallel workers. Realm provisioning and every subsequent write funnel into one worker manager, whose job queue serves several kinds of work:from-scratch-index,incremental-index,copy-index) gate everything user-visible about a write:createRealmblocks on the from-scratch index, and the read-your-writes endpoints — card GET and_publishability— drain in-flight incremental indexing before readingboxel_index, so their response time includes however long the write's index job sits in the queue.prerender_htmljobs render card HTML after an index pass. The job spawned by a from-scratch index runs a realm-wide module pre-warm sweep first, which takes tens of seconds for a fresh ~110-module workspace.User-initiated indexing and prerender-html are deliberately co-equal in priority (
runtime-common/queue.ts): a published realm's rendered HTML is as first-class as its search index. Dequeue is oldest-first within a priority floor, so a priority floor alone cannot keep the two kinds of work from occupying the same workers.The failure
On loaded CI runners,
publish-realm.spec.ts › it warns when private dependencies would cause host mode errorsfails when the publish modal's private-dependency check exceeds its 15s assertion budget. The job-level timeline from a failing run shows why: at the moment the test POSTs its card sources, all three user-tier workers are held — two by concurrent pre-warm sweeps (25.8s and 17.9s) for freshly created workspaces, one by the base realm's 600-file boot reindex. The just-written cards' incremental index job waits 15.1s in the queue before a worker even starts it, so the_publishabilitydrain alone outlives the assertion (dur=15072ms,dur=17480msacross attempts; the final retry's response landed at 14.8s — a hair too late). The same queue wait shows up on the neighboring card GETs (/index,/realmat 15–18s).The fix
The worker-manager's
userIndexCountpool now starts its workers with--indexJobsOnly, which restricts handler registration to the indexing job types. The queue's claim query only dequeues job types a worker has registered handlers for, so this makes the pool a dedicated indexing lane that a render sweep can never occupy, independent of priority tiers. The high-priority pool still carries the prerender-html sweeps alongside other user-initiated work — nothing about job priorities changes, so rendered-HTML work keeps its standing.userIndexCountdefaults to 0 and the matrix harness is its only consumer, so no deployed worker changes behavior.With the lane in place, the drain on
_publishabilitycosts the actual indexing time of the written files (a few seconds under CI load, matching passing-run timings) instead of an unbounded queue wait.Diagnostics
The failing runs also produced
_publishability404s whose origin the logs cannot attribute, because two failure paths are silent:Router.handlemaps a thrownCardErrorto an error response without logging it, so a thrown 404 is indistinguishable in the request log from a deliberate not-found response. It now logs the method, URL, status, and message before responding._publishabilityhandler now logs its drain duration and scan counts (instances scanned,error rows,violations,publishable) so a slow or empty-looking report can be attributed to queue wait vs. the scan itself.If the 404 recurs, these two lines plus the request log pin down which layer produced it.
Testing
worker-job-registration-test.tspins the registration sets: a default worker registers every job type; an--indexJobsOnlyworker registers exactly the indexing job types and cannot claimprerender_html.🤖 Generated with Claude Code