Skip to content

feat(diag): PERRY_TRACE_ASYNC — find lost async continuations#6481

Merged
proggeramlug merged 2 commits into
PerryTS:mainfrom
proggeramlug:feat/trace-async-lost-continuations
Jul 20, 2026
Merged

feat(diag): PERRY_TRACE_ASYNC — find lost async continuations#6481
proggeramlug merged 2 commits into
PerryTS:mainfrom
proggeramlug:feat/trace-async-lost-continuations

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

What

An opt-in diagnostic — PERRY_TRACE_ASYNC=1 — for the "async function never returns / response evaporates" bug class: an await suspends 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

  • On each await-suspend on a pending promise (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.
  • On each settle of a suspended-on promise (js_promise_resolve / js_promise_reject), it logs [ASYNC-SETTLE … P=<ptr>] and clears it (only tracked pointers → low noise).
  • A SUSPEND with no matching SETTLE is a lost continuation. Diff the unmatched-await backtraces of a working run against a hanging one and the extra one is the exact await whose promise never resolves.

Build the target with PERRY_DEBUG_SYMBOLS=1 for symbolized await sites.

Proven

Used to pinpoint #6479 (a next-intl app's bare / hangs): diffing the unmatched awaits of a working /en against the hanging / isolated a single lost await — a top-level await in next/dist/server/lib/router-server.js whose 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

    • Added opt-in async diagnostics using PERRY_TRACE_ASYNC=1.
    • Tracks suspended awaits and logs when their promises fulfill or reject.
    • Provides backtrace information to help identify asynchronous continuations that remain unresolved.
  • Bug Fixes

    • Improved visibility into potential async continuation leaks without changing promise behavior.

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
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ba67159-a022-41e6-ab58-87d38bd25b7e

📥 Commits

Reviewing files that changed from the base of the PR and between 77def53 and 2258fd7.

📒 Files selected for processing (2)
  • crates/perry-runtime/src/promise/async_step.rs
  • crates/perry-runtime/src/promise/then.rs

📝 Walkthrough

Walkthrough

Adds opt-in async continuation diagnostics controlled by PERRY_TRACE_ASYNC=1. Pending native-promise awaits are recorded with backtraces, and fulfillment or rejection settles matching trace entries.

Changes

Async continuation tracing

Layer / File(s) Summary
Suspension tracking
crates/perry-runtime/src/promise/async_step.rs
Adds thread-local tracing state and records pending native promises when async steps suspend.
Settlement tracing
crates/perry-runtime/src/promise/then.rs
Records fulfillment and rejection events for pending promises through the async tracing hooks.

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
Loading

Possibly related PRs

Suggested reviewers: andrewtdiz

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed, but it does not follow the required template sections for Summary, Changes, Related issue, Test plan, and Checklist. Add the template sections explicitly, including a short Summary, concrete Changes, Related issue, Test plan, and Checklist items.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the new async tracing diagnostic for lost continuations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@proggeramlug
proggeramlug merged commit b72f183 into PerryTS:main Jul 20, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready PR triaged: CodeRabbit feedback + conflicts addressed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant