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
With #8528 merged, a Coop in_process daemon can load and initialize two
Next.js applications in one process — but the second application's HTTP
handler rejects at dispatch, so it serves 500 while the first serves 200.
Evidence
Measured on the quiet M1 mini, Perry 7c22189aa (0.5.1514), Coop in_process,
two deployments of the same Next fixture. Provider provenance verified by
content, not by recorded metadata: the merged fix deletes ERR_PERRY_PATH_MODULE_THREAD, and strings on the packaged libperry_runtime.dylib finds 0 occurrences of it (the string also appears
nowhere in main's source, so the check is meaningful rather than vacuous).
Both applications initialize cleanly:
INFO application library preloaded on dedicated Perry thread deployment="bench-000" load_ms=... init_ms=...
INFO application library preloaded on dedicated Perry thread deployment="bench-001" load_ms=749.8 init_ms=697.0
The rejection reason is empty, which is itself worth fixing — an empty handler promise rejected: gives an operator nothing to act on.
What this is NOT
Not the path-module registry. Zero rejected duplicate path-module initializer messages in this run; both apps register their own initializers
on their own dedicated Perry threads. (An earlier iteration of fix(runtime): make the path-module registry per-heap so one process can host several apps #8528 that kept
the initializer-address map process-wide DID produce 115 such rejections;
that is fixed.)
Not app load or module init — both complete, with timings logged.
Not ordering-dependent in an obvious way: the first deployment to be
dispatched succeeds and the second fails.
Hypothesis
Some other process-global runtime/stdlib state assumes a single application per
process. Until #8528 the fail-closed thread guard made multi-app hosting
impossible, so none of that state has ever been exercised by two heaps. The
per-request registries are the obvious suspects — several are Mutex<HashMap<usize, ...>>
and are UNCOVERED by scripts/gc_runtime_root_holders.py, i.e. nobody has
determined whether they hold heap pointers:
REQUEST_REGISTRY, BLOB_REGISTRY, FETCH_RESPONSES, TRANSFORM_PAIRS, STREAM_HANDLES (crates/perry-stdlib/src/fetch/mod.rs, streams/transform.rs), plus buffer/own_props.rs's PROPS and buffer/header.rs's EXTERNAL_*_REGISTRY (the latter keyed by RAW heap
address).
Suggested first steps
Make the rejection reason non-empty — surface the underlying JS error.
With that, name the failing operation, then check whether its backing table
is process-global.
Reproduction
Needs a Coop checkout whose perry-main.lock points at a Perry with #8528, plus coop-daemon's rusqlite bumped 0.37 -> 0.39 to match perry-stdlib (they link
the same native sqlite3; see the companion Coop change). Then scripts/prepare-next-benchmark.sh and the resource_benchmark harness with COOP_BENCH_APP_COUNTS=2, COOP_BENCH_EXECUTION_MODE=in_process.
Summary
With #8528 merged, a Coop
in_processdaemon can load and initialize twoNext.js applications in one process — but the second application's HTTP
handler rejects at dispatch, so it serves 500 while the first serves 200.
Evidence
Measured on the quiet M1 mini, Perry
7c22189aa(0.5.1514), Coopin_process,two deployments of the same Next fixture. Provider provenance verified by
content, not by recorded metadata: the merged fix deletes
ERR_PERRY_PATH_MODULE_THREAD, andstringson the packagedlibperry_runtime.dylibfinds 0 occurrences of it (the string also appearsnowhere in main's source, so the check is meaningful rather than vacuous).
Both applications initialize cleanly:
Then:
The rejection reason is empty, which is itself worth fixing — an empty
handler promise rejected:gives an operator nothing to act on.What this is NOT
rejected duplicate path-module initializermessages in this run; both apps register their own initializerson their own dedicated Perry threads. (An earlier iteration of fix(runtime): make the path-module registry per-heap so one process can host several apps #8528 that kept
the initializer-address map process-wide DID produce 115 such rejections;
that is fixed.)
dispatched succeeds and the second fails.
Hypothesis
Some other process-global runtime/stdlib state assumes a single application per
process. Until #8528 the fail-closed thread guard made multi-app hosting
impossible, so none of that state has ever been exercised by two heaps. The
per-request registries are the obvious suspects — several are
Mutex<HashMap<usize, ...>>and are UNCOVERED by
scripts/gc_runtime_root_holders.py, i.e. nobody hasdetermined whether they hold heap pointers:
REQUEST_REGISTRY,BLOB_REGISTRY,FETCH_RESPONSES,TRANSFORM_PAIRS,STREAM_HANDLES(crates/perry-stdlib/src/fetch/mod.rs,streams/transform.rs), plusbuffer/own_props.rs'sPROPSandbuffer/header.rs'sEXTERNAL_*_REGISTRY(the latter keyed by RAW heapaddress).
Suggested first steps
is process-global.
Reproduction
Needs a Coop checkout whose
perry-main.lockpoints at a Perry with #8528, pluscoop-daemon'srusqlitebumped 0.37 -> 0.39 to matchperry-stdlib(they linkthe same native
sqlite3; see the companion Coop change). Thenscripts/prepare-next-benchmark.shand theresource_benchmarkharness withCOOP_BENCH_APP_COUNTS=2,COOP_BENCH_EXECUTION_MODE=in_process.