Skip to content

fix: tweak keep-alive timeout implementation#3145

Merged
ronag merged 4 commits into
nodejs:mainfrom
mweberxyz:timeout-tweaks
Apr 22, 2024
Merged

fix: tweak keep-alive timeout implementation#3145
ronag merged 4 commits into
nodejs:mainfrom
mweberxyz:timeout-tweaks

Conversation

@mweberxyz
Copy link
Copy Markdown
Contributor

Closes #3141

This relates to...

Rationale

The goal of the util/timers.js is to provide setTimeout-compatible timers with 1 second granularity, as opposed to native setTimeout's 1ms. Because the timers were scheduled, then executed in two separate "ticks" of the fast timeout, this meant timeouts are called best case 1000ms after intended, and worst case 2000ms. Combined with the previous 1000ms default for keepAliveTimeoutThreshold, the end result is an effective keepAliveTimeoutThreshold of 0 -- so unluckily timed requests would attempt to use a connection which has already been closed by the remote.

Changes

Multi-pronged approach here to prioritize safety and avoid attempting to re-use sockets that have already been closed:

  1. Increase default keepAliveTimeoutThreshold from 1000 to 2000
  2. Determine timeout times immediately upon timeout registration, instead of in the next tick of fast timer
  3. Double fast timer tick rate
  4. Timer tests added: see test file comments for approach details

Note to @ronag I went with change 2 instead of the proposed subtraction of the tick time from the duration because after writing the tests, that approach was more flaky.

Features

N/A

Bug Fixes

Some of the reports in #3133 may be resolved by this change, though it would need to be backported to 5.x, included in a Node v20 release, and then picked up by Amazon before we would see any improvement. ¯\_(ツ)_/¯

Breaking Changes and Deprecations

The more conservative default keepAliveTimeoutThreshold value should not be a breaking change.

Benchmarks

Within run-to-run variance:

PR:

┌─────────┬─────────────────────┬─────────┬────────────────────┬────────────┬─────────────────────────┐
│ (index) │ Tests               │ Samples │ Result             │ Tolerance  │ Difference with slowest │
├─────────┼─────────────────────┼─────────┼────────────────────┼────────────┼─────────────────────────┤
│ 0       │ 'undici - fetch'    │ 45      │ '16454.53 req/sec' │ '± 2.74 %' │ '-'                     │
│ 1       │ 'undici - pipeline' │ 25      │ '23976.66 req/sec' │ '± 2.61 %' │ '+ 45.71 %'             │
│ 2       │ 'undici - stream'   │ 15      │ '31539.92 req/sec' │ '± 2.94 %' │ '+ 91.68 %'             │
│ 3       │ 'undici - request'  │ 40      │ '31627.55 req/sec' │ '± 2.98 %' │ '+ 92.21 %'             │
│ 4       │ 'undici - dispatch' │ 10      │ '36186.91 req/sec' │ '± 2.54 %' │ '+ 119.92 %'            │
└─────────┴─────────────────────┴─────────┴────────────────────┴────────────┴─────────────────────────┘

main:

┌─────────┬─────────────────────┬─────────┬────────────────────┬────────────┬─────────────────────────┐
│ (index) │ Tests               │ Samples │ Result             │ Tolerance  │ Difference with slowest │
├─────────┼─────────────────────┼─────────┼────────────────────┼────────────┼─────────────────────────┤
│ 0       │ 'undici - fetch'    │ 40      │ '17433.63 req/sec' │ '± 2.88 %' │ '-'                     │
│ 1       │ 'undici - pipeline' │ 30      │ '23934.65 req/sec' │ '± 3.00 %' │ '+ 37.29 %'             │
│ 2       │ 'undici - request'  │ 101     │ '29557.97 req/sec' │ '± 3.88 %' │ '+ 69.55 %'             │
│ 3       │ 'undici - dispatch' │ 20      │ '32711.63 req/sec' │ '± 2.60 %' │ '+ 87.64 %'             │
│ 4       │ 'undici - stream'   │ 15      │ '34008.68 req/sec' │ '± 2.35 %' │ '+ 95.08 %'             │ 
└─────────┴─────────────────────┴─────────┴────────────────────┴────────────┴─────────────────────────┘

Status

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Race condition at-or-near keep-alive expiration

4 participants