Title: lib: defer AbortSignal.any() following by Han5991 · Pull Request #62367 · nodejs/node · GitHub
Open Graph Title: lib: defer AbortSignal.any() following by Han5991 · Pull Request #62367 · nodejs/node
X Title: lib: defer AbortSignal.any() following by Han5991 · Pull Request #62367 · nodejs/node
Description: Summary defer following source signals for AbortSignal.any() until the composite is observed keep composite state in sync via lazy refresh and follow paths unregister fired timeout signals from clearTimeoutRegistry to reduce timeout churn retention add regression coverage for long-lived and timeout churn cases Problem AbortSignal.any() eagerly registered listener-less composites as dependants of their source signals. With a long-lived source, repeated AbortSignal.any([source]) calls accumulated in kDependantSignals and retained memory (#62363). While investigating broader AbortSignal churn, timeout-only churn also kept timeout finalization registry entries alive after timers had fired, which delayed cleanup. What Changed AbortSignal.any() listener-less composites no longer follow source signals eagerly composites follow their sources only when observed composite state is refreshed lazily when reading aborted, reason, or throwIfAborted() AbortSignal.timeout() fired timeout signals now unregister themselves from clearTimeoutRegistry this releases timeout churn bookkeeping sooner after the timer has fired Results #62363 repro out/Release/node stayed flat through 2,000,000 iterations kDependantSignals stayed at 0 Timeout churn Repro Before patch With patch AbortSignal.timeout(1) after GC rss 164.55 MiB, heap 23.15 MiB rss 67.19 MiB, heap 3.96 MiB AbortSignal.any([AbortSignal.timeout(1)]) after GC rss 135.30 MiB, heap 7.85 MiB rss 74.22 MiB, heap 4.01 MiB Timeout churn logs Before patch AbortSignal.timeout(1) start {"rss":"45.67","heap":"3.49"} before-gc {"rss":"153.39","heap":"79.54"} after-gc {"rss":"164.55","heap":"23.15"} after-1-immediate {"rss":"160.23","heap":"6.28"} after-50ms {"rss":"158.67","heap":"6.26"} AbortSignal.any([AbortSignal.timeout(1)]) start {"rss":"45.72","heap":"3.49","handles":0} before-gc {"rss":"133.58","heap":"32.48","handles":2} after-gc {"rss":"135.30","heap":"7.85","handles":2} after-1-immediate {"rss":"135.31","heap":"5.06","handles":2} after-50ms {"rss":"135.31","heap":"5.04","handles":2} With patch AbortSignal.timeout(1) start {"rss":"45.52","heap":"3.49"} before-gc {"rss":"66.55","heap":"6.84"} after-gc {"rss":"67.19","heap":"3.96"} after-1-immediate {"rss":"67.20","heap":"3.88"} after-50ms {"rss":"67.23","heap":"3.87"} AbortSignal.any([AbortSignal.timeout(1)]) start {"rss":"45.58","heap":"3.49","handles":0} before-gc {"rss":"73.52","heap":"9.82","handles":2} after-gc {"rss":"74.22","heap":"4.01","handles":2} after-1-immediate {"rss":"74.22","heap":"3.91","handles":2} after-50ms {"rss":"74.22","heap":"3.90","handles":2} Notes The same-tick fresh AbortController microbenchmark from #54614 still shows heap growth until the next turn. That appears to be due to WeakRef cleanup semantics rather than long-lived source retention: the heap drops back after a setImmediate() and the weak references clear on the next turn. Testing python3 tools/test.py test/parallel/test-abortsignal-any.mjs test/parallel/test-abort-controller-any-timeout.js test/parallel/test-abortsignal-drop-settled-signals.mjs test/parallel/test-abortsignal-timeout-memory.js test/parallel/test-abortcontroller.js Fixes: #62363 Refs: #54614
Open Graph Description: Summary defer following source signals for AbortSignal.any() until the composite is observed keep composite state in sync via lazy refresh and follow paths unregister fired timeout signals from cl...
X Description: Summary defer following source signals for AbortSignal.any() until the composite is observed keep composite state in sync via lazy refresh and follow paths unregister fired timeout signals from cl...
Opengraph URL: https://github.com/nodejs/node/pull/62367
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:cae16d63-ff63-4ce5-5555-da1bff44649e |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | AB90:1C4EB2:BDA49F:FC3F39:6A4D4764 |
| html-safe-nonce | 504ecedf64e47e7ee4cc2612a7877769102fc9b81e7df7ae1091e597e52a3ae6 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBQjkwOjFDNEVCMjpCREE0OUY6RkMzRjM5OjZBNEQ0NzY0IiwidmlzaXRvcl9pZCI6IjI5MTgzNzQzMjg4NzY0ODQ0NTIiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 4a1d9ee236bb55ebb4fd566b2622cef7301cb8db8bbb2b52eaf274a3ed1713db |
| hovercard-subject-tag | pull_request:3428919001 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/nodejs/node/pull/62367/files |
| twitter:image | https://avatars.githubusercontent.com/u/73056306?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/73056306?s=400&v=4 |
| og:image:alt | Summary defer following source signals for AbortSignal.any() until the composite is observed keep composite state in sync via lazy refresh and follow paths unregister fired timeout signals from cl... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 92571a8944142227b7e19cd10918b1ddd06e5066c1ad5bc7e4769cf6140a87e6 |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/nodejs/node git https://github.com/nodejs/node.git |
| octolytics-dimension-user_id | 9950313 |
| octolytics-dimension-user_login | nodejs |
| octolytics-dimension-repository_id | 27193779 |
| octolytics-dimension-repository_nwo | nodejs/node |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 27193779 |
| octolytics-dimension-repository_network_root_nwo | nodejs/node |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 56fc8347865a14e2ec811533d68f929cf4e0ec19 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width