tls: reduce SetImmediate deferrals in TLSWrap write completion for lower latency
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 30/100
- Loại issue
- Tính năng
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Ít trao đổi
- Lĩnh vực
- networking, security
Hướng nghiên cứu
Bắt đầu trong src/crypto/crypto_tls.cc bằng cách lần theo các đường dẫn được nêu EncOut(), DoWrite(), in_dowrite_ và Cycle(), sau đó chạy các trường hợp test-tls-* hiện có cho ghi, drain và hủy. Được xem là hoàn tất khi giảm SetImmediate deferrals một cách có cơ sở, với độ bao phủ hồi quy hai chiều, đồng thời vẫn duy trì thứ tự, việc hủy trong khi ghi và hành vi double-callback.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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.
- Ngôn ngữ chính
- JavaScript
- Star
- 122k
- Fork
- 37.4k
- Merge trung bình
- 4 ngày 2 giờ
- Pull request đã merge (30 ngày)
- 277
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của nodejs/node
-
doc
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
-
build
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
-
feature request
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
-
area-deployment area-integrations triage:bot-seen
Độ khó 2/5 Nửa ngày Mức phù hợp với người mới 86/100
-
Issue-Bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
sugarlabs/musicblocks#8924 ·
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
ArduPilot/ardupilot_wiki#8088 ·
-
[BUG] createTool tools cannot be registered with Mastra when exactOptionalPropertyTypes is enabled Đang mởcustomer-eng status: needs triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100