feat(diag): PERRY_TRACE_ASYNC — find lost async continuations#6481
Open
proggeramlug wants to merge 1 commit into
Open
feat(diag): PERRY_TRACE_ASYNC — find lost async continuations#6481proggeramlug wants to merge 1 commit into
proggeramlug wants to merge 1 commit into
Conversation
Adds an opt-in diagnostic (`PERRY_TRACE_ASYNC=1`) for the "async function never returns / response evaporates" bug class — where an `await` suspends on a promise that is never settled, so the continuation never runs (e.g. a hung Next.js request handler). It logs each await-SUSPEND on a *pending* promise (with a backtrace of the await site) and each SETTLE of a suspended-on promise. A SUSPEND with no matching SETTLE is a lost continuation; diffing the unmatched-await backtraces of a working run against a hanging one pinpoints the exact await whose promise never resolves. Hooks: `js_async_step_chain`'s pending arm (suspend) and `js_promise_resolve`/`js_promise_reject` (settle). Zero behavioral change and one cached-bool check when the env var is unset. Compiled in unconditionally (like `PERRY_TRACE_THROW`), so no rebuild is needed to turn it on. Build with `PERRY_DEBUG_SYMBOLS=1` for symbolized await sites. Claude-Session: https://claude.ai/code/session_01KD4GTmiwZjRrxShzQydJpF
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds opt-in async continuation diagnostics controlled by ChangesAsync continuation tracing
Estimated code review effort: 2 (Simple) | ~10 minutes Sequence Diagram(s)sequenceDiagram
participant AsyncStepChain
participant Promise
participant AsyncTrace
AsyncStepChain->>AsyncTrace: trace_async_suspend(promise)
AsyncTrace->>AsyncTrace: store promise and backtrace
Promise->>AsyncTrace: trace_async_settle(promise, "fulfill" or "reject")
AsyncTrace->>AsyncTrace: match and remove suspension
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
What
An opt-in diagnostic —
PERRY_TRACE_ASYNC=1— for the "async function never returns / response evaporates" bug class: anawaitsuspends on a promise that is never settled, so the continuation never runs. This is the signature behind several hard Next.js gaps (a request handler that produces no response and leaks — #6479, #5989 — and the streaming ordering races like #6477).How it works
js_async_step_chain's pending arm), it logs[ASYNC-SUSP #n P=<ptr>]+ a backtrace of the await site, and records the awaited pointer.js_promise_resolve/js_promise_reject), it logs[ASYNC-SETTLE … P=<ptr>]and clears it (only tracked pointers → low noise).awaitwhose promise never resolves.Build the target with
PERRY_DEBUG_SYMBOLS=1for symbolized await sites.Proven
Used to pinpoint #6479 (a next-intl app's bare
/hangs): diffing the unmatched awaits of a working/enagainst the hanging/isolated a single lost await — a top-levelawaitinnext/dist/server/lib/router-server.jswhose promise perry never resolves on the default-locale path. Validated on a plain async program: SUSPEND count == SETTLE count, every pointer matched.Cost when off
Zero behavioral change and a single cached-bool check per suspend/settle. Compiled in unconditionally (like
PERRY_TRACE_THROW) so it can be flipped on without a rebuild.https://claude.ai/code/session_01KD4GTmiwZjRrxShzQydJpF
Summary by CodeRabbit
New Features
PERRY_TRACE_ASYNC=1.Bug Fixes