fix(perry-ext-fastify): #747 + #746 — event_loop + wait_for_promise pump stdlib#751
Merged
Conversation
85c82cb to
c87e101
Compare
…ump stdlib (v0.5.904)
The perry-stdlib→perry-ext port (v0.5.572) dropped the
`js_stdlib_process_pending` call that the original
`crates/perry-stdlib/src/fastify/server.rs` had at the top of every
event-loop tick. As a result, any program combining `fastify` with
a perry-ext-* wrapper (ws, net, http, fetch) saw its listener
callbacks stranded the moment `app.listen()` entered its loop —
events accumulated on tokio workers but the JS main thread never
drained the per-wrapper queues. The user's hub reproduced this as
"`wss.on('connection', cb)` never fires" on v0.5.700–v0.5.892, filed
as #746; the underlying defect is in perry-ext-fastify, filed as
Three edits in `crates/perry-ext-fastify/src/server.rs`:
1. extern "C" block: declare `js_run_stdlib_pump` (defined in
`crates/perry-runtime/src/lib.rs:177`).
2. `event_loop`: call `js_run_stdlib_pump()` ahead of
`js_promise_run_microtasks()`, matching perry-stdlib's order.
3. `wait_for_promise`: same call inside the per-iteration body,
for parity with `crates/perry-stdlib/src/fastify/server.rs:432`
(defense-in-depth — the codegen-emitted `await` loop at
`crates/perry-codegen/src/expr.rs:9500` already pumps stdlib,
so this only matters for the rare case where a route handler
returns a Promise resolved by an external mechanism without an
inner `await` chain).
Validation:
- Fastify + WS combo before fix: `[wss listening]` + `[wss connection
fired]` never print; after fix: both fire on the next tick.
- Pure-WS minimal repro: already passed on macOS at HEAD (covered
by the codegen-emitted main event loop's existing pump call). The
combined fastify+ws path is what the hub actually exercises, so
this closes #746 in practice.
- Multi-await fastify handler (3× Promise.resolve + reply.code(201))
returns correct JSON in 4 ms.
- Multi-await perry-ext-bcrypt chain (2× hash + 1× compare inside a
POST handler) returns correct JSON in 4 ms.
- `cargo test --release -p perry-ext-fastify`: 10 passed.
- `cargo test --release --workspace --exclude perry-ui-{ios,tvos,
watchos,visionos,android,windows,gtk4}`: exit 0.
wait_for_promise omission caused @perryts/mysql's `await
pool.exec(...)` chain to stall after the first INSERT, but a
perry-ext-bcrypt chain (same shape, same queue_promise_resolution
mechanism) works fine without the wait_for_promise pump — the
codegen-emitted await loop pumps stdlib on every iteration, so
inner awaits don't strand. The actual #748 root cause needs
reproduction with a real @perryts/mysql + MySQL setup and is left
open.
Closes #747.
Closes #746 (for the realistic fastify+ws use case; the pure-WS-only
Linux symptom from the minimal repro doesn't reproduce on macOS at
HEAD — left as a Linux follow-up if it persists).
Refs #748 (investigated but not closed; see CHANGELOG for full
context).
c87e101 to
38cb7d6
Compare
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.
Summary
perry-ext-fastify'sevent_loopwas missing thejs_run_stdlib_pump()call that the originalperry-stdlib::fastifyversion (crates/perry-stdlib/src/fastify/server.rs:270-289) has had all along. The port in v0.5.572 (commit 97e80d3) dropped it. Any program combiningfastifywith a perry-ext-* wrapper (ws,net,http,fetch) had its listener callbacks stranded the momentapp.listen()entered its loop — events accumulated on tokio workers but the JS main thread never drained the per-wrapper queues.wsandfastify; the symptom they filed against the WS surface ("wss.on('connection', cb)never fires on v0.5.700–v0.5.892") was the same defect surfacing through the fastify event loop. The pure-WS-only minimal repro from WebSocketServer.on() callbacks never fire on incoming connections (regression v0.5.491–v0.5.700+) #746 already passes on macOS at HEAD via the codegen-emitted main event loop's existing pump call, so closing perry-ext-fastify::event_loop doesn't pump stdlib — perry-ext-{ws,net,http} events stranded after app.listen() #747 closes WebSocketServer.on() callbacks never fire on incoming connections (regression v0.5.491–v0.5.700+) #746 in practice for the realistic hub scenario.0+ HTTP 200 after sequence of @perryts/mysql writes; first INSERT commits, rest silently no-op, explicit return dropped #748 (investigated, not closed). Initial hypothesis was that the samewait_for_promiseomission caused@perryts/mysql'sawait pool.exec(...)chain to stall after the first INSERT. Disproven — a perry-ext-bcrypt chain (samequeue_promise_resolutionmechanism) works fine without thewait_for_promisepump because the codegen-emittedawaitloop atcrates/perry-codegen/src/expr.rs:9500already pumps stdlib on every iteration. Native route returns body0+ HTTP 200 after sequence of @perryts/mysql writes; first INSERT commits, rest silently no-op, explicit return dropped #748 stays open.Three edits in
crates/perry-ext-fastify/src/server.rs:extern "C"block: declarejs_run_stdlib_pump(defined incrates/perry-runtime/src/lib.rs:177, dispatches through theSTDLIB_PUMP_FNAtomicPtr that perry-stdlib registers at startup).event_loop: call it ahead ofjs_promise_run_microtasks(), matching perry-stdlib's order.wait_for_promise: same call inside the per-iteration body, for parity with perry-stdlib'swait_for_promiseatcrates/perry-stdlib/src/fastify/server.rs:432— defense-in-depth for the rare case of an externally-resolved Promise returned by a route handler without an innerawaitchain.Test plan
/tmp/test_746_with_fastify.ts(Fastify().get(...).listen({port: 18905})alongsidenew WebSocketServer({port: 18906})): before the fix,[wss listening]never printed and[wss connection fired]never printed; after the fix, both fire on the next event-loop tick./tmp/test_746_exact.tsmatches the issue body verbatim. Passes on macOS at HEAD (verified before + after the fix)./tmp/test_748_fastify_async.tswith3× await Promise.resolve(N) + reply.code(201)returns{a:11,b:22,c:33,sum:66,stage:4}HTTP 201 in 4 ms./tmp/test_748_bcrypt.tswith2× bcrypt.hash + bcrypt.comparereturns{h1_len:60,h2_len:60,ok:1,hashes_differ:true}HTTP 201 in 4 ms. This is the same async-await + perry-ext-* + fastify shape from Native route returns body0+ HTTP 200 after sequence of @perryts/mysql writes; first INSERT commits, rest silently no-op, explicit return dropped #748 and works correctly (which is what disproved the wait_for_promise hypothesis for Native route returns body0+ HTTP 200 after sequence of @perryts/mysql writes; first INSERT commits, rest silently no-op, explicit return dropped #748).cargo test --release -p perry-ext-fastify— 10 passed.cargo test --release --workspace --exclude perry-ui-{ios,tvos,watchos,visionos,android,windows,gtk4}— exit 0.cargo build --release -p perry-ext-fastify -p perry— clean.0+ HTTP 200 after sequence of @perryts/mysql writes; first INSERT commits, rest silently no-op, explicit return dropped #748 root cause investigation — needs a real@perryts/mysql+ MySQL setup to reproduce. Not in this PR.