Comments (6)
The problem is timers always fire at least 1000ms after the provided timeout in the best case, and 2000ms after provided in the "best worst case" - ie just unlucky with the scheduling time, but no event loop contention.
Test case
const timers = require("./timers.js")
// needed because timers in `./timers.js` are unref-ed
setTimeout(() => {
console.log("10s elapsed, exiting")
process.exit(0)
}, 10000)
for(const target of [1000, 1500, 1990, 1999, 2000, 2001, 2500, 2990, 2999, 3000, 4000, 5000]) {
const start = process.hrtime.bigint()
timers.setTimeout(() => {
const end = process.hrtime.bigint()
console.log(`target = ${target}, actual = ${(end - start) / 1_000_000n}`)
}, target)
}
Result
% node test-latency.js
target = 1000, actual = 2002
target = 1500, actual = 3020
target = 1990, actual = 3020
target = 1999, actual = 3020
target = 2000, actual = 3020
target = 2001, actual = 3021
target = 3000, actual = 4022
target = 2500, actual = 4023
target = 2990, actual = 4023
target = 2999, actual = 4024
target = 4000, actual = 5026
target = 5000, actual = 6028
10s elapsed, exiting
Which means for a server sent 5000 ms keep-alive and 1000ms default keepAliveTimeoutThreshold, the target is 4000, and the actual timeout is always too late - 5000ms or more.
Happy to propose a solution via PR, or @ronag jump in if you prefer.
Edit: I'm think best course of action is:
- increase default keepAliveTimeoutThreshold to 2000ms
- double the "tick rate" of the fast timeout code by reducing the setTimeout calls from 1000 to 500
- subtract 500 from the requested timeout duration -- that means in the best case, the timer will fire exactly when called for.
Taking all three changes together, that means given a 5000ms keep alive, best case scenario we close the connection after 3000ms, best-worst case (ie- timeout scheduled just after a previous timeout was scheduled) it takes 3500ms, and then that leaves 1500ms of breathing room in the worst-worst case if the event loop is blocked.
from undici.
Regardless of the default, it appears to not be working as expected, and the cause is looking to be in lib/util/timers:
If I stub out the fast timers implementation, then no errors:
module.exports = {
setTimeout (callback, delay, opaque) {
return setTimeout(callback, delay, opaque)
},
I will continue looking into lib/util/timers.
from undici.
Looking closer at the logs, I don't think there's anything undici can do. The socket is being closed by the server after the 2nd request has been sent, as far as I can tell.
...
FETCH 27254: sending request to GET http://127.0.0.1:3000//
TIMER 27254: 4990 list empty
NET 27254: destroy
NET 27254: close
NET 27254: close handle
NET 27254: emit close
FETCH 27254: request to GET http://127.0.0.1:3000// errored - read ECONNRESET
node:internal/deps/undici/undici:12500
Error.captureStackTrace(err, this);
^
TypeError: fetch failed
at node:internal/deps/undici/undici:12500:13
at async Timeout.start [as _onTimeout] (/Users/matt/code/replicate-undici-3133/client.js:6:15) {
[cause]: Error: read ECONNRESET
at TCP.onStreamRead (node:internal/stream_base_commons:217:20) {
errno: -54,
code: 'ECONNRESET',
syscall: 'read'
}
}
Node.js v21.7.3
from undici.
one option could be to always add the retry interceptor for gets (and just retry them).
for posts and all non-idempotent requests is a bit more tricky.
from undici.
What is the goal of keepAliveTimeoutThreshold? With a Keep-Alive: timeout=5 returned by the server, and a default keepAliveTimeoutThreshhold of 1000ms, is it unexpected that after 4900ms a request sent on the existing connection?
from undici.
I think we should always be keeping 2s (or more) to account for slow networks. So if the server gives us 5s, we should actually consider it 3s.
from undici.
Related Issues (20)
- publish autobahn test results on the docs site HOT 7
- Nightly tests are failing HOT 2
- Improve support for pre-shared sessions (PSK) HOT 3
- Request constructor: Failed to parse URL from ___ HOT 2
- Broken links in docs webpage HOT 2
- Failing JSON parsing in Node 22 HOT 13
- undici/types/index.d.ts:21:8: Type error TS1192: Module '"[email protected]/node_modules/undici/types/interceptors"' has no default export HOT 6
- Node 22.3.0 regression: undici sends invalid origin header in some cases HOT 2
- fetch arrayBuffer() leaks random data from process memory HOT 18
- build: husky install is deprecated HOT 1
- Ability to externalize WASM was broken HOT 11
- Measure request latency HOT 3
- dns round-robin interceptor + cache HOT 3
- async dispatch handler helper
- Cannot convert argument to a ByteString because the character at index 46 has a value of 65286 which is greater than 255 HOT 2
- Undici.Request and AbortController doesn't work well HOT 22
- The RetryHandler receives a duplicate body when the server does not support Range requests. HOT 11
- `Dispatcher.compose` doesn't return a true dispatcher HOT 9
- Back-pressure doesn't work with sub dispatches
- Add generic type for opaque object for stream() and pipeline() HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from undici.