| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a2b6a81 commit 84d3100
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2723,6 +2723,23 @@ added: v23.8.0 | |||
| 2723 | 2723 | Specifies the maximum number of milliseconds a TLS handshake is permitted to take | |
| 2724 | 2724 | to complete before timing out. | |
| 2725 | 2725 | ||
| 2726 | + #### `sessionOptions.initialRtt` | ||
| 2727 | + | ||
| 2728 | + <!-- YAML | ||
| 2729 | + added: REPLACEME | ||
| 2730 | + --> | ||
| 2731 | + | ||
| 2732 | + * Type: {bigint|number} | ||
| 2733 | + * **Default:** `0` (use ngtcp2 default of 333ms) | ||
| 2734 | + | ||
| 2735 | + Specifies the initial round-trip time estimate in milliseconds. This value is | ||
| 2736 | + used for probe timeout (PTO) computation, initial pacing, and early loss | ||
| 2737 | + detection before the first actual RTT sample is collected from the connection. | ||
| 2738 | + The default of 333ms is appropriate for the general internet. For low-latency | ||
| 2739 | + environments such as loopback or same-rack deployments, setting a value closer | ||
| 2740 | + to the actual RTT (e.g., `1`) avoids unnecessarily conservative initial | ||
| 2741 | + behavior. | ||
| 2742 | + | ||
| 2726 | 2743 | #### `sessionOptions.keepAlive` | |
| 2727 | 2744 | ||
| 2728 | 2745 | <!-- YAML | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -404,6 +404,9 @@ const endpointRegistry = new SafeSet(); | |||
| 404 | 404 | * @property {ArrayBufferView} [token] An opaque address validation token | |
| 405 | 405 | * previously received from the server via `onnewtoken` (client only). | |
| 406 | 406 | * @property {bigint|number} [handshakeTimeout] The handshake timeout | |
| 407 | + * @property {bigint|number} [initialRtt] The initial round-trip time estimate in milliseconds. | ||
| 408 | + * Used for PTO computation and initial pacing before the first RTT sample. Default uses | ||
| 409 | + * ngtcp2's built-in default of 333ms. Set lower for low-latency environments. | ||
| 407 | 410 | * @property {bigint|number} [keepAlive] The keep-alive timeout in milliseconds. When set, | |
| 408 | 411 | * PING frames will be sent automatically to prevent idle timeout. | |
| 409 | 412 | * @property {bigint|number} [maxStreamWindow] The maximum stream window | |
@@ -4875,6 +4878,7 @@ function processSessionOptions(options, config = kEmptyObject) { | |||
| 4875 | 4878 | maxPayloadSize, | |
| 4876 | 4879 | unacknowledgedPacketThreshold = 0, | |
| 4877 | 4880 | handshakeTimeout, | |
| 4881 | + initialRtt, | ||
| 4878 | 4882 | keepAlive, | |
| 4879 | 4883 | maxStreamWindow, | |
| 4880 | 4884 | maxWindow, | |
@@ -4982,6 +4986,7 @@ function processSessionOptions(options, config = kEmptyObject) { | |||
| 4982 | 4986 | maxPayloadSize, | |
| 4983 | 4987 | unacknowledgedPacketThreshold, | |
| 4984 | 4988 | handshakeTimeout, | |
| 4989 | + initialRtt, | ||
| 4985 | 4990 | keepAlive, | |
| 4986 | 4991 | maxStreamWindow, | |
| 4987 | 4992 | maxWindow, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -93,6 +93,7 @@ class SessionManager; | |||
| 93 | 93 | V(groups, "groups") \ | |
| 94 | 94 | V(handshake_timeout, "handshakeTimeout") \ | |
| 95 | 95 | V(http3_alpn, &NGHTTP3_ALPN_H3[1]) \ | |
| 96 | + V(initial_rtt, "initialRtt") \ | ||
| 96 | 97 | V(keep_alive_timeout, "keepAlive") \ | |
| 97 | 98 | V(initial_max_data, "initialMaxData") \ | |
| 98 | 99 | V(initial_max_stream_data_bidi_local, "initialMaxStreamDataBidiLocal") \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -513,6 +513,12 @@ Session::Config::Config(Environment* env, | |||
| 513 | 513 | options.handshake_timeout == UINT64_MAX | |
| 514 | 514 | ? UINT64_MAX | |
| 515 | 515 | : options.handshake_timeout * NGTCP2_MILLISECONDS; | |
| 516 | + | ||
| 517 | + // The initial_rtt option is in milliseconds; ngtcp2 expects nanoseconds. | ||
| 518 | + // A value of 0 leaves the ngtcp2 default (333ms) unchanged. | ||
| 519 | + if (options.initial_rtt > 0) | ||
| 520 | + settings.initial_rtt = options.initial_rtt * NGTCP2_MILLISECONDS; | ||
| 521 | + | ||
| 516 | 522 | settings.max_stream_window = options.max_stream_window; | |
| 517 | 523 | settings.max_window = options.max_window; | |
| 518 | 524 | settings.ack_thresh = options.unacknowledged_packet_threshold; | |
@@ -604,10 +610,11 @@ Maybe<Session::Options> Session::Options::From(Environment* env, | |||
| 604 | 610 | ||
| 605 | 611 | if (!SET(version) || !SET(min_version) || !SET(preferred_address_strategy) || | |
| 606 | 612 | !SET(transport_params) || !SET(tls_options) || !SET(qlog) || | |
| 607 | - !SET(handshake_timeout) || !SET(keep_alive_timeout) || | ||
| 608 | - !SET(max_stream_window) || !SET(max_window) || !SET(max_payload_size) || | ||
| 609 | - !SET(unacknowledged_packet_threshold) || !SET(cc_algorithm) || | ||
| 610 | - !SET(draining_period_multiplier) || !SET(max_datagram_send_attempts)) { | ||
| 613 | + !SET(handshake_timeout) || !SET(initial_rtt) || | ||
| 614 | + !SET(keep_alive_timeout) || !SET(max_stream_window) || !SET(max_window) || | ||
| 615 | + !SET(max_payload_size) || !SET(unacknowledged_packet_threshold) || | ||
| 616 | + !SET(cc_algorithm) || !SET(draining_period_multiplier) || | ||
| 617 | + !SET(max_datagram_send_attempts)) { | ||
| 611 | 618 | return Nothing<Options>(); | |
| 612 | 619 | } | |
| 613 | 620 | ||
@@ -726,6 +733,12 @@ std::string Session::Options::ToString() const { | |||
| 726 | 733 | res += prefix + "handshake timeout: " + std::to_string(handshake_timeout) + | |
| 727 | 734 | " nanoseconds"; | |
| 728 | 735 | } | |
| 736 | + if (initial_rtt > 0) { | ||
| 737 | + res += prefix + "initial rtt: " + std::to_string(initial_rtt) + | ||
| 738 | + " milliseconds"; | ||
| 739 | + } else { | ||
| 740 | + res += prefix + "initial rtt: <default>"; | ||
| 741 | + } | ||
| 729 | 742 | res += prefix + "max stream window: " + std::to_string(max_stream_window); | |
| 730 | 743 | res += prefix + "max window: " + std::to_string(max_window); | |
| 731 | 744 | res += prefix + "max payload size: " + std::to_string(max_payload_size); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -163,6 +163,15 @@ class Session final : public AsyncWrap, private SessionTicket::AppData::Source { | |||
| 163 | 163 | static constexpr uint64_t DEFAULT_HANDSHAKE_TIMEOUT = 10'000; | |
| 164 | 164 | uint64_t handshake_timeout = DEFAULT_HANDSHAKE_TIMEOUT; | |
| 165 | 165 | ||
| 166 | + // The initial round-trip time estimate in milliseconds. ngtcp2 uses this | ||
| 167 | + // for PTO computation, initial pacing, and early loss detection before | ||
| 168 | + // the first RTT sample is collected. The default of 0 uses ngtcp2's | ||
| 169 | + // built-in default of 333ms, which is appropriate for the general | ||
| 170 | + // internet. For low-latency environments (e.g., loopback or same-rack | ||
| 171 | + // deployments), setting a value closer to the actual RTT avoids | ||
| 172 | + // unnecessarily conservative initial behavior. | ||
| 173 | + uint64_t initial_rtt = 0; | ||
| 174 | + | ||
| 166 | 175 | // The keep-alive timeout in milliseconds. When set to a non-zero value, | |
| 167 | 176 | // ngtcp2 will automatically send PING frames to keep the connection alive | |
| 168 | 177 | // before the idle timeout fires. Set to 0 to disable (default). | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,60 @@ | |||
| 1 | + // Flags: --experimental-quic --experimental-stream-iter --no-warnings | ||
| 2 | + | ||
| 3 | + // Test: initialRtt session option is accepted and the session functions | ||
| 4 | + // correctly with a custom initial RTT estimate. | ||
| 5 | + | ||
| 6 | + import { hasQuic, skip, mustCall } from '../common/index.mjs'; | ||
| 7 | + import assert from 'node:assert'; | ||
| 8 | + | ||
| 9 | + const { ok } = assert; | ||
| 10 | + | ||
| 11 | + if (!hasQuic) { | ||
| 12 | + skip('QUIC is not enabled'); | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + const { listen, connect } = await import('../common/quic.mjs'); | ||
| 16 | + const { bytes } = await import('stream/iter'); | ||
| 17 | + | ||
| 18 | + const encoder = new TextEncoder(); | ||
| 19 | + const payload = encoder.encode('hello rtt'); | ||
| 20 | + const serverDone = Promise.withResolvers(); | ||
| 21 | + | ||
| 22 | + // Use a low initialRtt (1ms) to simulate a low-latency environment. | ||
| 23 | + // The session should complete successfully and the smoothed RTT in | ||
| 24 | + // stats should converge to a value well below the default 333ms. | ||
| 25 | + const serverEndpoint = await listen(mustCall((serverSession) => { | ||
| 26 | + serverSession.onstream = mustCall(async (stream) => { | ||
| 27 | + const data = await bytes(stream); | ||
| 28 | + ok(data.byteLength > 0); | ||
| 29 | + stream.writer.endSync(); | ||
| 30 | + await stream.closed; | ||
| 31 | + serverSession.close(); | ||
| 32 | + serverDone.resolve(); | ||
| 33 | + }); | ||
| 34 | + }), { | ||
| 35 | + initialRtt: 1, // 1ms | ||
| 36 | + }); | ||
| 37 | + | ||
| 38 | + const clientSession = await connect(serverEndpoint.address, { | ||
| 39 | + initialRtt: 1, // 1ms | ||
| 40 | + }); | ||
| 41 | + await clientSession.opened; | ||
| 42 | + | ||
| 43 | + const stream = await clientSession.createBidirectionalStream({ | ||
| 44 | + body: payload, | ||
| 45 | + }); | ||
| 46 | + | ||
| 47 | + for await (const _ of stream) { /* drain */ } // eslint-disable-line no-unused-vars | ||
| 48 | + await stream.closed; | ||
| 49 | + await serverDone.promise; | ||
| 50 | + | ||
| 51 | + // After data exchange, the smoothed RTT should have converged to a | ||
| 52 | + // realistic value. On loopback it should be well under 10ms (10,000,000ns). | ||
| 53 | + // The stat is in nanoseconds. | ||
| 54 | + const smoothedRtt = clientSession.stats.smoothedRtt; | ||
| 55 | + ok(smoothedRtt > 0n, 'smoothedRtt should be non-zero after data exchange'); | ||
| 56 | + ok(smoothedRtt < 10_000_000n, | ||
| 57 | + `smoothedRtt should be under 10ms on loopback, got ${smoothedRtt}ns`); | ||
| 58 | + | ||
| 59 | + await clientSession.close(); | ||
| 60 | + await serverEndpoint.close(); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments