tls: reduce SetImmediate deferrals in TLSWrap write completion for lower latency
維護者通常 1 天內回覆
還沒有人認領這個 Issue。
評估
- 難度
- 5/5
- 預估耗時
- 一週以上
- 新手友好度
- 30/100
- Issue 類型
- 功能
- 描述清晰度
- 基本清楚
- 活躍度
- 冷清
- 領域
- networking, security
研究方向
從 src/crypto/crypto_tls.cc 開始,追蹤所述的 EncOut()、DoWrite()、in_dowrite_ 和 Cycle() 路徑,然後執行現有的 test-tls-* 寫入、排空和銷毀案例。完成的標準是在具備雙向回歸涵蓋的情況下,有依據地減少 SetImmediate 延遲,同時維持順序、寫入期間銷毀以及雙重回呼行為。
由索引模型根據 Issue 內容生成。
描述
What is the problem this feature will solve?
TLSWrap in src/crypto/crypto_tls.cc deliberately defers write completion to a later event-loop turn in cases where the underlying stream finishes synchronously. This adds systematic latency and extra queue depth on high-throughput, full-duplex TLS connections.
Concrete cases in the current implementation:
- EncOut() after a synchronous underlying write — when underlying_stream()->Write() returns non-async, completion is simulated via env()->SetImmediate(...) before OnStreamAfterWrite() runs (~L725–733).
- Empty DoWrite() driving the stream — zero-length writes used to tick the stream machinery also defer via SetImmediate (~L1054–1058).
- in_dowrite_ + pending cleartext — when EncOut() runs inside DoWrite() and encrypted output is not yet flushed, InvokeQueued(0) is deferred to the next tick (~L689–701). The code comments note uncertainty about correctness vs. blocking data flow.
For tunnel/VPN/proxy workloads (and any latency-sensitive full-duplex TLS), each extra tick compounds:
- Delayed 'drain' / write() callbacks → send-side stalls
- Head-of-line blocking between read and write directions
- Higher event-loop utilization under sustained MTU-sized traffic
This is separate from OpenSSL crypto cost — it is Node-imposed async shaping on paths that already completed synchronously at the TCP layer.
What is the feature you are proposing to solve the problem?
Improve TLSWrap so write completion and queued callbacks run as soon as it is safe, without defaulting to SetImmediate on every sync underlying write.
Proposed changes:
- Audit and narrow SetImmediate usage in EncOut(), empty DoWrite(), and the in_dowrite_ branch — identify cases where OnStreamAfterWrite() / InvokeQueued() can run inline without violating existing TLS/stream invariants.
- Add an opt-in TLSSocket option, e.g. lowLatency: true (name bikesheddable), that enables synchronous completion where the test suite permits. Default behavior stays unchanged for backward compatibility until confidence is high.
- Strengthen TLSWrap::Cycle() — when both directions have work pending (ClearIn → ClearOut → EncOut), drain in one pass where cycle_depth_ allows, reducing reliance on a subsequent tick to flush state left in pending_cleartext_input_ or enc_out_.
- Add regression tests — bidirectional pummel/benchmark comparing event-loop turns per megabyte transferred with and without the fast path; ensure existing test-tls-* write/drain/destroy cases still pass.
Success criteria:
- Measurably fewer SetImmediate / next-tick deferrals per SSL_write → TCP flush cycle under full-duplex load.
- No regressions in write ordering, destroy() during write, or double-callback cases covered by existing parallel TLS tests.
What alternatives have you considered?
- Do nothing; require native TLS bridges for tunnel workloads — Valid for extreme cases (see separate tls.createBridge proposal), but this latency tax affects all TLSSocket users doing full-duplex I/O, not only TUN/VPN apps.
- Userland workarounds (setImmediate batching, socket.cork) — Cannot bypass TLSWrap internals; users cannot force synchronous WriteWrap::Done() from JavaScript.
- Disable Nagle / tune TCP only — Reduces kernel buffering but does not address Node deferring completion after sync writes.
- Always run WriteWrap::Done() synchronously — Likely breaks edge cases the SetImmediate workaround was added for; needs careful gating behind an option or proven safe paths only.
- Move TLS to worker threads — Larger architectural change; orthogonal to fixing unnecessary main-thread deferral in the existing TLSWrap state machine.
- 主要語言
- JavaScript
- 星號
- 122k
- 分支
- 39.9k
- 平均合併
- 3 天 22 小時
- 30 天內合併 PR
- 233
環境準備
- 沒有 Dockerfile 或 Docker Compose 檔案
- 有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
nodejs/node 的其他 Issue
-
Docs: --watch-path *is* supported on linux可能已有人在做 @motokaneyuki 於 2 天前認領。 未關閉
難度 1/5 1 小時以內 新手友好度 82/100
nodejs/node#66743 · 1 個 reaction ·
維護者通常 1 天內回覆
-
node:internal/inspector/network_http: `TypeError: Missing dataLength` in event when response uses `setEncoding()`可能已有人在做 @lazerg 於 11 天前認領。 未關閉
難度 2/5 1-3 小時 新手友好度 78/100
維護者通常 1 天內回覆
-
[Docs] `process.loadEnvFile()` does not document behaviour when variables already exist可能已有人在做 @Sepandard 於 20 天前認領。 未關閉doc
難度 1/5 1 小時以內 新手友好度 90/100
維護者通常 1 天內回覆
-
Stream.prototype.forEach will block in first promise in queue before read more chunk可能已有人在做 @mmustafasenoglu 於 21 天前認領。 未關閉doc
難度 2/5 1-3 小時 新手友好度 65/100
維護者通常 1 天內回覆
-
nodejs 26.8.2 fails to build with clang/LLVM可能重新可做 @btyagi18 於 7 天前認領,目前沒有進行中的 PR。 未關閉build
難度 1/5 1 小時以內 新手友好度 88/100
維護者通常 1 天內回覆
相似的 Issue
-
enhancement
難度 2/5 1-3 小時 新手友好度 67/100
維護者通常 1 天內回覆
-
enhancement good first issue
難度 2/5 1-3 小時 新手友好度 76/100
SasanLabs/VulnerableApp#836 ·
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 76/100
keyxmakerx/Chronicle#1469 ·
維護者通常 1 天內回覆
-
Connection.prototype.bulkWrite() with ordered: false resolves null when a write fails可能已有人在做 @maxymlyskov 今天認領。 未關閉
難度 2/5 1-3 小時 新手友好度 78/100
Automattic/mongoose#16601 ·
維護者通常 2 天內回覆
-
sandbox-vercel: persistent: false is dropped for live sandboxes created from a template, so every stop writes a snapshot可能已有人在做 @jvg123 今天認領。 未關閉factory-active task-identify-harness-labels-done task-identify-issue-type-done
難度 2/5 1-3 小時 新手友好度 78/100
維護者通常 1 天內回覆