perf(node): fire-and-forget response send on the serve() path#219
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR adds a ChangesNode adapter response and request handling
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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 |
commit: |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/adapters/node.ts`:
- Around line 87-89: The detached response path in sendNodeResponse can bypass
the existing error handling, so a synchronous failure from writeHead/write or a
rejection in the Promise branch may escape serve() unexpectedly. Update the
sendNodeResponse/sendNodeResponseDetached flow in node.ts so the detached send
is protected by the same try/catch or equivalent guard used by sendNodeResponse,
and ensure the Promise branch handles rejection before calling
sendNodeResponseDetached. Keep the behavior consistent for both sync and async
results.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4932b5e8-311f-4d16-aac1-58137f722240
📒 Files selected for processing (3)
src/adapters/_node/request.tssrc/adapters/_node/send.tssrc/adapters/node.ts
The internal serve() path switched from the async `sendNodeResponse` to the synchronous `sendNodeResponseDetached`. The async variant implicitly turned a synchronous throw during serialization (e.g. an invalid header value in `writeHead`) into a swallowed rejected promise; the detached variant let it escape the node:http request listener as an `uncaughtException`, crashing the process. Guard `sendNodeResponseDetached` so a serialization throw fails the single response (500 if not yet committed, otherwise destroy the socket) instead of taking the server down. Also dedupe the buffered-body read shared by `text()`/`json()` behind a `#readBuffered()` helper, preserving json()'s single-continuation parse. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
What
The internal
serve()request listener no longer tracks per-responseend()completion with a Promise.
node:httpignores the listener's return value, sothe tracking Promise (and the microtask hops to settle it) was pure overhead on
the hot path. A new
sendNodeResponseDetachedvariant skips it; streamingbodies still return their tracking promise since it drives their own cleanup.
The public
sendNodeResponsekeeps its awaitable contract fortoNodeHandlerconsumers.
Also collapses the buffered request-body read shared by
request.text()andrequest.json()behind a private#readBuffered()helper, keepingjson()'ssingle
readBody -> parsecontinuation (no extra promise/microtask hop).Safety
Making the send path synchronous removed the implicit try/catch that the old
async sendNodeResponseprovided: a synchronous throw during serialization(e.g. an invalid header value rejected by
writeHead) would previously becomea swallowed rejected promise, but on the detached path it escaped the request
listener as an
uncaughtExceptionand crashed the process.sendNodeResponseDetachednow guards against this — a serialization throw failsthat single response (500 if headers aren't committed yet, otherwise the socket
is destroyed) instead of taking the server down.
Verification
writeHeadthrow is caught, theprocess stays up, and the client receives a 500.
Summary by CodeRabbit
New Features
Bug Fixes