You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(ocap-kernel): launch subcluster vats in parallel (#983)
## Explanation
Subcluster startup was O(sum of vat init times) because
`#launchVatsForSubcluster` used a serial `for...of` loop — each vat's
`initVat` RPC handshake had to complete before the next one began.
This PR changes startup to O(max vat init time) by launching all vats
concurrently via `Promise.all`. The mechanism:
1. Before any `await`, one kernel promise (`kp<N>`) is pre-allocated per
vat and marked as kernel-decided.
2. The bootstrap message is queued immediately, targeting the bootstrap
vat's unresolved `kp<N>`. `KernelRouter` parks the send on the promise
via `enqueuePromiseMessage`.
3. All vats launch in parallel. As each vat's `initVat` handshake
completes, its root kernel promise is resolved via
`resolvePromises('kernel', ...)`, and the run loop forwards any queued
messages.
4. The bootstrap vat receives kernel promise KRefs for all peer vats in
its `bootstrap(roots, services)` call — it can pipeline calls to peers
while they are still initializing.
## Changes
- **`SubclusterManager.ts`** — rewrote `#launchVatsForSubcluster` to
pre-allocate kernel promises, queue bootstrap immediately, and launch
all vats with `Promise.all`.
- **`SubclusterManager.test.ts`** — updated mocks (`initKernelPromise`,
`setPromiseDecider`, `resolvePromises`) and assertions to match the new
flow.
- **`Kernel.test.ts`** — added `resolvePromises = vi.fn()` to the
`KernelQueue` mock class.
## Checklist
- [x] Tests pass (`yarn workspace @MetaMask/ocap-kernel test:dev:quiet`)
- [x] Build passes (`yarn workspace @MetaMask/ocap-kernel build`)
- [ ] Changelog updated
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes core subcluster launch ordering, failure propagation, and
cleanup in the kernel; mistakes could leak workers or break bootstrap
peer wiring, though coverage is expanded in unit and integration tests.
>
> **Overview**
> **Parallel subcluster startup** replaces serial vat launch in
`SubclusterManager` with `Promise.allSettled`, so subcluster bring-up
time tracks the slowest vat instead of the sum of init times. Kernel
service resolution still runs before any vat starts.
>
> After launches settle, bootstrap is invoked with real root `ko` refs
for successful vats. **Failed peer vats** get an immediately rejected
kernel promise (`VAT_TERMINATED`) in the `roots` map so bootstrap can
observe the failure via pipelined `E(roots.peer)` calls;
`launchSubcluster` still rejects once bootstrap has run.
**`SubclusterLaunchResult`** now includes **`vatRootKrefs`** (name →
root kref for vats that launched successfully).
>
> Failed launches **tear down** any vats that did start
(`#terminateVatQuietly` in reverse order) before IO/subcluster rollback.
Vat cleanup skips a baseline refcount decrement when GC already zeroed
reachability (`vat.ts`).
>
> Integration tests drop hardcoded `ko4`/`ko5`/`ko6` and use
`vatRootKrefs` / `rootKref` from `launchSubcluster`; a new **peer
rejection** integration test and bootstrap vat bundle were added.
Changelog documents concurrent launch and peer rejection behavior.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
6a2492a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: packages/ocap-kernel/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
17
17
-`error` is the failure's message and `detail` its whole cause chain, because only strings cross the wire: when a crank dies and its rollback then fails, the message names the rollback and only the chain names what killed the kernel
18
18
-**BREAKING:**`runLoop` is required, so `KernelStatus` gains a mandatory property and a `getStatus` reply from a kernel built before this field fails result validation outright. It cannot be made optional: `exactOptional` would leave the type and the validator disagreeing inside a `type()`, and `optional` widens the property to `| undefined`, which an RPC result may not be
19
19
- Add `onRunLoopFailure` to `Kernel.make` options, called with the error that killed the run loop so an embedder that outlives the kernel can exit or restart ([#1005](https://github.com/MetaMask/ocap-kernel/pull/1005))
20
+
- Launch all vats in a subcluster concurrently during `launchSubcluster`, reducing startup latency from serial to parallel; failed peer vats receive a rejected kernel promise observable via `E(roots.peer).method()` pipelining ([#983](https://github.com/MetaMask/ocap-kernel/pull/983))
20
21
- Add `fetch`, `Request`, `Headers`, and `Response` to available vat endowments ([#942](https://github.com/MetaMask/ocap-kernel/pull/942))
21
22
- Add `VatConfig.network: { allowedHosts: string[] }`; requesting `'fetch'` without it rejects `initVat`
22
23
- Integrate Snaps attenuated endowment factories into vat globals ([#937](https://github.com/MetaMask/ocap-kernel/pull/937))
0 commit comments