René's URL Explorer Experiment


Title: quic: improve stream cleanup & lookup by pimterry · Pull Request #65944 · nodejs/node · GitHub

Open Graph Title: quic: improve stream cleanup & lookup by pimterry · Pull Request #65944 · nodejs/node

X Title: quic: improve stream cleanup & lookup by pimterry · Pull Request #65944 · nodejs/node

Description: Extracted from #63995, but with a few further improvements en route. The core change here is to stop querying for our Stream instance by id in every on_read_data, on_acked_stream_data and on_receive_data call (all hot paths where this is awkward & expensive). We now cache it in nghttp3's stream-linked data, which it then passes back to each callback for us, so we can just read it directly. Note that both ngtcp2 & nghttp3 separately track per-stream user data, which is a bit confusing for some of the explanation below - we're caching this in nghttp3 user data. Having extracted that though and working through the logic to clean up when H3 streams close, I found some interesting problems, so this expanded a bit: We actually already stashed &stream on every nghttp3 client stream (the last argument to nghttp3_conn_submit_request) but we never read it or never cleaned it up - this was a dangling pointer that's only safe because it was unused. There are multiple cleanup paths required (local destroy & remote close can happen in either order) but the stream closure chain to do that had a series of nested methods which took overlapping parts of the process and weaved in & out of nghttp3/ngtcp2 callbacks unnecessarily en route, with special cases depending on which other steps had already completed. This was hard to follow, but also created some bugs: Applications weren't actually informed about local stream closure, and in some cases nghttp3 never cleaned up local streams at all: stream.destroy() from JS called Session::RemoveStream which cleared ngtcp2 stream user data beforehand, and then Session::on_stream_close exited early if the ngtcp2 user data was null. That means we skipped calling ReceiveStreamClose, which means we never called nghttp3_conn_close_stream2, which is what actually frees the nghttp3 stream. End result is that locally destroyed nghttp3 streams were orphaned & retained for the lifetime of the entire connection. Http3ApplicationImpl::OnStreamClose called ExtendMaxStreams, and then Session::RemoveStream called ngtcp2_conn_extend_max_streams_* as well, so closing any remote stream actually expanded the max-streams window twice. Http3ApplicationImpl::OnStreamClose called ExtendMaxStreams with REMOTE unconditionally, even though it was called for local streams too, so closing a local stream expanded the remote stream window as well. More generally: the max stream window was tightly tied to the lifetime of our Stream object & application behaviour, not the actual live streams in ngtcp2, which is the thing it should be limiting. End result: we had a few different & overlapping paths that stream closure could take, and the interactions created a selection of small bugs. Src changes here are a net deletion, simplifying this and fixing the bugs: There's now one hook for "stream on the wire closed" which runs in all cases when a QUIC stream is actually closed. Session:on_stream_close calls application().ReceiveStreamClose(), and the application cleans up its state there in one place. There's now one hook for "local Stream destruction" (by JS, or after closure on the wire): Session::RemoveStream cleans up that side, and calls application.StreamRemoved() to inform the app when it's gone. Nghttp3 stream close callbacks become redundant, so that code is just removed. Nghttp3 close events only ever happened after we told nghttp3 about it from ReceiveStreamClose, so we just cleanup in a single step there instead. The max-stream limit is tied directly to ngtcp2 stream closure: when on_stream_close fires, the credit is returned immediately. Max-stream logic on Applications (ExtendMaxStreams, EndpointLabel) goes away completely. The original goal: we cache our Stream object in nghttp3 so we don't have to look it up for every data callback, and successfully clean that up later when the Stream is gone.

Open Graph Description: Extracted from #63995, but with a few further improvements en route. The core change here is to stop querying for our Stream instance by id in every on_read_data, on_acked_stream_data and on_receiv...

X Description: Extracted from #63995, but with a few further improvements en route. The core change here is to stop querying for our Stream instance by id in every on_read_data, on_acked_stream_data and on_receiv...

Opengraph URL: https://github.com/nodejs/node/pull/65944

X: @github

direct link

Domain: github.com

react-profiling0
react-import-mapreact,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf
route-pattern/:user_id/:repository/pull/:id/files(.:format)
route-controllerpull_requests
route-actionfiles
fetch-noncev2:fed8d28b-3318-bd33-2419-484203f201c1
current-catalog-service-hashae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b
request-idA386:1E45AC:FD22BE:14B06A0:6AED5EC0
html-safe-nonce6480e7e058bd311b9a01f458a2c0f6db6c5dfb8669d3ae4326a6a023e065ddc0
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBMzg2OjFFNDVBQzpGRDIyQkU6MTRCMDZBMDo2QUVENUVDMCIsInZpc2l0b3JfaWQiOiI4NDc0NDY2MzI2NDgxMjk3MDg4IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0=
visitor-hmac00e47edf09d09fe2cd560b5a83c8c233ceaf4a0b72fa3ebb48322678e9dd36cd
hovercard-subject-tagpull_request:4487432924
github-keyboard-shortcutsrepository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///pull_requests/show/files
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/nodejs/node/pull/65944/files
twitter:imagehttps://avatars.githubusercontent.com/u/1526883?s=400&v=4
twitter:cardsummary_large_image
og:imagehttps://avatars.githubusercontent.com/u/1526883?s=400&v=4
og:image:altExtracted from #63995, but with a few further improvements en route. The core change here is to stop querying for our Stream instance by id in every on_read_data, on_acked_stream_data and on_receiv...
og:site_nameGitHub
og:typeobject
hostnamegithub.com
expected-hostnamegithub.com
None56fb42677ae87ed3e64410323d67980aa65d0485568a7858f874e7edfc6d1c11
turbo-cache-controlno-preview
diff-viewunified
go-importgithub.com/nodejs/node git https://github.com/nodejs/node.git
octolytics-dimension-user_id9950313
octolytics-dimension-user_loginnodejs
octolytics-dimension-repository_id27193779
octolytics-dimension-repository_nwonodejs/node
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id27193779
octolytics-dimension-repository_network_root_nwonodejs/node
turbo-body-classeslogged-out env-production page-responsive full-width
disable-turbotrue
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release9c9a37e2423ed382cf39f70b5aa4b4858b40a3b0
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/nodejs/node/pull/65944/files#start-of-content
https://github.com/
Sign inhttps://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fnodejs%2Fnode%2Fpull%2F65944%2Ffiles
GitHubRepository hosting, code review, and collaborationhttps://github.com/platform
CopilotAI coding agent for tasks, projects, and automated workhttps://github.com/features/copilot
ActionsCI/CD and automationhttps://github.com/features/actions
Code QualityQuality analysis, fixes, and enforcementhttps://github.com/features/code-quality
CodespacesCloud development environmentshttps://github.com/features/codespaces
Code SecurityVulnerability detection and remediationhttps://github.com/security/advanced-security/code-security
Secret ProtectionSecret scanning and leak preventionhttps://github.com/security/advanced-security/secret-protection
All Featureshttps://github.com/features
Documentationhttps://docs.github.com/en
Traininghttps://learn.github.com/skills
Certificationshttps://learn.github.com/credentials
Build with Copilothttps://github.com/features/ai
Customize Copilothttps://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/customize-copilot-overview
Integrationshttps://github.com/marketplace
Trending reposhttps://github.com/trending
GitHub Sponsorshttps://github.com/open-source/sponsors
Developer articleshttps://github.com/resources/articles
ChangelogYour source for the latest features, improvements, and fixes across GitHub products, all in one place.https://github.blog/changelog/
Enterpriseshttps://github.com/enterprise
Teamhttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
See all industrieshttps://github.com/solutions/industry
Why GitHubhttps://github.com/why-github
Customer Storieshttps://github.com/customer-stories
Business insightshttps://github.com/solutions/executive-insights
Trust Centerhttps://github.com/trust-center
GitHub Advanced SecurityEnterprise-grade code securityhttps://github.com/security/advanced-security
Copilot for businessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/enterprise/premium-support
Documentationhttps://docs.github.com/en
Resource centerhttps://github.com/resources
Bloghttps://github.blog/
Events & webinarshttps://github.com/resources/events
Community Forumhttps://github.com/orgs/community/discussions
Customer Storieshttps://github.com/customer-stories
Integrationshttps://github.com/marketplace
Customer supporthttps://help.github.com/
Traininghttps://learn.github.com/skills
GitHub Statushttps://www.githubstatus.com/
Abouthttps://github.com/about
Partnershttps://github.com/partners
Changeloghttps://github.blog/changelog/
GitHub UniverseThe event for developers, by developers — talks, workshops, and what’s next for GitHub.https://githubuniverse.com/
Pricinghttps://github.com/pricing
Sign inhttps://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fnodejs%2Fnode%2Fpull%2F65944%2Ffiles
Sign uphttps://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fpull_requests%2Fshow%2Ffiles&source=header-repo&source_repo=nodejs%2Fnode
Reloadhttps://github.com/nodejs/node/pull/65944/files
Reloadhttps://github.com/nodejs/node/pull/65944/files
Reloadhttps://github.com/nodejs/node/pull/65944/files
nodejshttps://github.com/nodejs
nodehttps://github.com/nodejs/node
Codehttps://github.com/nodejs/node
Issues654 (654)https://github.com/nodejs/node/issues
Pull requests662 (662)https://github.com/nodejs/node/pulls
Actionshttps://github.com/nodejs/node/actions
Projectshttps://github.com/nodejs/node/projects
Security and qualityhttps://github.com/nodejs/node/security
Insightshttps://github.com/nodejs/node/pulse
Sign up for GitHub https://github.com/signup?return_to=%2Fnodejs%2Fnode%2Fissues%2Fnew%2Fchoose
terms of servicehttps://docs.github.com/terms
privacy statementhttps://docs.github.com/privacy
Sign inhttps://github.com/login?return_to=%2Fnodejs%2Fnode%2Fissues%2Fnew%2Fchoose
pimterryhttps://github.com/pimterry
nodejs:mainhttps://github.com/nodejs/node/tree/main
pimterry:nghttp3-stream-cachinghttps://github.com/pimterry/node/tree/nghttp3-stream-caching
Conversation 10 https://github.com/nodejs/node/pull/65944
Commits 2 https://github.com/nodejs/node/pull/65944/commits
Checks 31 https://github.com/nodejs/node/pull/65944/checks
Files changed https://github.com/nodejs/node/pull/65944/files
Please reload this pagehttps://github.com/nodejs/node/pull/65944/files
quic: improve stream cleanup & lookup https://github.com/nodejs/node/pull/65944/files#top
Show all changes 2 commits https://github.com/nodejs/node/pull/65944/files
28daf59 quic: improve stream cleanup & lookup pimterry Sep 9, 2026 https://github.com/nodejs/node/pull/65944/commits/28daf5924f60c14110e12a412ddde009191338d2
7df3a19 Comment stream cleanup more to resolve nits pimterry Sep 16, 2026 https://github.com/nodejs/node/pull/65944/commits/7df3a19fd3f49a29440a46027a3e8de8138ab73a
Clear filters https://github.com/nodejs/node/pull/65944/files
Please reload this pagehttps://github.com/nodejs/node/pull/65944/files
Please reload this pagehttps://github.com/nodejs/node/pull/65944/files
application.cc https://github.com/nodejs/node/pull/65944/files#diff-a010523fa8894660b54049d865ba731f77fb22839124ad7c39f43d77c9302182
application.h https://github.com/nodejs/node/pull/65944/files#diff-e70cb985211b8bf58c6e48f7f79da1490dfd0051ed1cdc8b89daa591a709f1ec
defs.h https://github.com/nodejs/node/pull/65944/files#diff-dfbefc4ac4ad77b8ef71bd589ce4ae11dd6b6a6d38f7ef4a7625e3455560c8c1
http3.cc https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
session.cc https://github.com/nodejs/node/pull/65944/files#diff-90b56372b92188ce4cd4994c1361530afbb3cd177ef3f8b9de8b625e472e1030
session.h https://github.com/nodejs/node/pull/65944/files#diff-1d95031b7aca2c8d8acb5240cbf6363fce70f6eb11d41e1f4f95171f2acbc714
test-quic-h3-stream-credit.mjs https://github.com/nodejs/node/pull/65944/files#diff-0211e0dedd952c0dba9bae744e16b4594e0e012a428e276da922b725e86f13a1
src/quic/application.cchttps://github.com/nodejs/node/pull/65944/files#diff-a010523fa8894660b54049d865ba731f77fb22839124ad7c39f43d77c9302182
View file https://github.com/pimterry/node/blob/7df3a19fd3f49a29440a46027a3e8de8138ab73a/src/quic/application.cc
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/nodejs/node/pull/65944/{{ revealButtonHref }}
https://github.com/nodejs/node/pull/65944/files#diff-a010523fa8894660b54049d865ba731f77fb22839124ad7c39f43d77c9302182
Please reload this pagehttps://github.com/nodejs/node/pull/65944/files
https://github.com/nodejs/node/pull/65944/files#diff-a010523fa8894660b54049d865ba731f77fb22839124ad7c39f43d77c9302182
src/quic/application.hhttps://github.com/nodejs/node/pull/65944/files#diff-e70cb985211b8bf58c6e48f7f79da1490dfd0051ed1cdc8b89daa591a709f1ec
View file https://github.com/pimterry/node/blob/7df3a19fd3f49a29440a46027a3e8de8138ab73a/src/quic/application.h
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/nodejs/node/pull/65944/{{ revealButtonHref }}
https://github.com/nodejs/node/pull/65944/files#diff-e70cb985211b8bf58c6e48f7f79da1490dfd0051ed1cdc8b89daa591a709f1ec
https://github.com/nodejs/node/pull/65944/files#diff-e70cb985211b8bf58c6e48f7f79da1490dfd0051ed1cdc8b89daa591a709f1ec
https://github.com/nodejs/node/pull/65944/files#diff-e70cb985211b8bf58c6e48f7f79da1490dfd0051ed1cdc8b89daa591a709f1ec
https://github.com/nodejs/node/pull/65944/files#diff-e70cb985211b8bf58c6e48f7f79da1490dfd0051ed1cdc8b89daa591a709f1ec
src/quic/defs.hhttps://github.com/nodejs/node/pull/65944/files#diff-dfbefc4ac4ad77b8ef71bd589ce4ae11dd6b6a6d38f7ef4a7625e3455560c8c1
View file https://github.com/pimterry/node/blob/7df3a19fd3f49a29440a46027a3e8de8138ab73a/src/quic/defs.h
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/nodejs/node/pull/65944/{{ revealButtonHref }}
https://github.com/nodejs/node/pull/65944/files#diff-dfbefc4ac4ad77b8ef71bd589ce4ae11dd6b6a6d38f7ef4a7625e3455560c8c1
https://github.com/nodejs/node/pull/65944/files#diff-dfbefc4ac4ad77b8ef71bd589ce4ae11dd6b6a6d38f7ef4a7625e3455560c8c1
src/quic/http3.cchttps://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
View file https://github.com/pimterry/node/blob/7df3a19fd3f49a29440a46027a3e8de8138ab73a/src/quic/http3.cc
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/nodejs/node/pull/65944/{{ revealButtonHref }}
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
https://github.com/nodejs/node/pull/65944/files#diff-d7d3468ef1ccbd4556f1f5b61ced0287a7c15723008a940d17a1489b108ad583
src/quic/session.cchttps://github.com/nodejs/node/pull/65944/files#diff-90b56372b92188ce4cd4994c1361530afbb3cd177ef3f8b9de8b625e472e1030
View file https://github.com/pimterry/node/blob/7df3a19fd3f49a29440a46027a3e8de8138ab73a/src/quic/session.cc
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/nodejs/node/pull/65944/{{ revealButtonHref }}
https://github.com/nodejs/node/pull/65944/files#diff-90b56372b92188ce4cd4994c1361530afbb3cd177ef3f8b9de8b625e472e1030
Please reload this pagehttps://github.com/nodejs/node/pull/65944/files
https://github.com/nodejs/node/pull/65944/files#diff-90b56372b92188ce4cd4994c1361530afbb3cd177ef3f8b9de8b625e472e1030
https://github.com/nodejs/node/pull/65944/files#diff-90b56372b92188ce4cd4994c1361530afbb3cd177ef3f8b9de8b625e472e1030
https://github.com/nodejs/node/pull/65944/files#diff-90b56372b92188ce4cd4994c1361530afbb3cd177ef3f8b9de8b625e472e1030
https://github.com/nodejs/node/pull/65944/files#diff-90b56372b92188ce4cd4994c1361530afbb3cd177ef3f8b9de8b625e472e1030
https://github.com/nodejs/node/pull/65944/files#diff-90b56372b92188ce4cd4994c1361530afbb3cd177ef3f8b9de8b625e472e1030
src/quic/session.hhttps://github.com/nodejs/node/pull/65944/files#diff-1d95031b7aca2c8d8acb5240cbf6363fce70f6eb11d41e1f4f95171f2acbc714
View file https://github.com/pimterry/node/blob/7df3a19fd3f49a29440a46027a3e8de8138ab73a/src/quic/session.h
Open in desktop https://desktop.github.com
https://github.co/hiddenchars
https://github.com/nodejs/node/pull/65944/{{ revealButtonHref }}
https://github.com/nodejs/node/pull/65944/files#diff-1d95031b7aca2c8d8acb5240cbf6363fce70f6eb11d41e1f4f95171f2acbc714
https://github.com/nodejs/node/pull/65944/files#diff-1d95031b7aca2c8d8acb5240cbf6363fce70f6eb11d41e1f4f95171f2acbc714
https://github.com/nodejs/node/pull/65944/files#diff-1d95031b7aca2c8d8acb5240cbf6363fce70f6eb11d41e1f4f95171f2acbc714
https://github.com/nodejs/node/pull/65944/files#diff-1d95031b7aca2c8d8acb5240cbf6363fce70f6eb11d41e1f4f95171f2acbc714
Please reload this pagehttps://github.com/nodejs/node/pull/65944/files
Please reload this pagehttps://github.com/nodejs/node/pull/65944/files
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.