-
-
Notifications
You must be signed in to change notification settings - Fork 151
Description
We use dd-trace to instrument our test suite. Since upgrading to nock@14 (or [email protected]), which now uses @mswjs/[email protected], we've been having issues where the periodic dd-trace telemetry uploads are crashing the process with either EINVAL or ECANCELED. They are allowed to passthrough, but there is something about the implementation in MockHttpSocket such that in a large test suite this will always happen to multiple tests, but it's rare enough that reproducing it once at will is annoyingly difficult.
ECANCELED (failed write)
Error: write ECANCELED Canceled because of SSL destruction
at WriteWrap.onWriteComplete [as oncomplete] (node:internal/stream_base_commons:95:16)
at WriteWrap.callbackTrampoline (node:internal/async_hooks:130:17)
at TCP.done (node:_tls_wrap:641:11)
at TCP.callbackTrampoline (node:internal/async_hooks:130:17)
Emitted 'error' event on ClientRequest instance at:
at ClientRequest.req.emit (/home/circleci/project/node_modules/dd-trace/packages/datadog-instrumentations/src/http/client.js:123:25)
at emitErrorEvent (node:_http_client:101:11)
at MockHttpSocket.socketErrorListener (node:_http_client:504:5)
at MockHttpSocket.emit (node:events:536:35)
at MockHttpSocket.emit (/home/circleci/project/node_modules/@mswjs/interceptors/src/interceptors/ClientRequest/MockHttpSocket.ts:160:12)
at TLSSocket.<anonymous> (/home/circleci/project/node_modules/@mswjs/interceptors/src/interceptors/ClientRequest/MockHttpSocket.ts:293:14)
at TLSSocket.emit (node:events:524:28)
at TLSSocket.emit (/home/circleci/project/node_modules/dd-trace/packages/datadog-instrumentations/src/net.js:65:25)
at emitErrorNT (node:internal/streams/destroy:169:8)
at emitErrorCloseNT (node:internal/streams/destroy:128:3)
at processTicksAndRejections (node:internal/process/task_queues:82:21) {
errno: -125,
code: 'ECANCELED',
syscall: 'write'
}
EINVAL (failed read)
Error: read EINVAL
at tryReadStart (node:net:716:20)
at Socket._read (node:net:731:5)
at MockHttpSocket.<anonymous> (node:net:729:37)
at Object.onceWrapper (node:events:633:28)
at MockHttpSocket.emit (node:events:531:35)
at MockHttpSocket.emit (/home/circleci/interceptors/repro/node_modules/.pnpm/@[email protected]/node_modules/@mswjs/interceptors/lib/node/chunk-N4ZZFE24.js:454:12)
at TLSSocket.<anonymous> (/home/circleci/interceptors/repro/node_modules/.pnpm/@[email protected]/node_modules/@mswjs/interceptors/lib/node/chunk-N4ZZFE24.js:531:12)
at TLSSocket.emit (node:events:531:35)
at /home/circleci/interceptors/repro/node_modules/.pnpm/[email protected]_@[email protected]_@[email protected]_@[email protected]_/node_modules/dd-trace/packages/datadog-instrumentations/src/net.js:62:27
at run (node:diagnostics_channel:168:14)
Emitted 'error' event on ClientRequest instance at:
at req.emit (/home/circleci/interceptors/repro/node_modules/.pnpm/[email protected]_@[email protected]_@[email protected]_@[email protected]_/node_modules/dd-trace/packages/datadog-instrumentations/src/http/client.js:123:25)
at emitErrorEvent (node:_http_client:107:11)
at MockHttpSocket.socketErrorListener (node:_http_client:574:5)
at MockHttpSocket.emit (node:events:519:28)
at MockHttpSocket.emit (/home/circleci/interceptors/repro/node_modules/.pnpm/@[email protected]/node_modules/@mswjs/interceptors/lib/node/chunk-N4ZZFE24.js:454:12)
at TLSSocket.<anonymous> (/home/circleci/interceptors/repro/node_modules/.pnpm/@[email protected]/node_modules/@mswjs/interceptors/lib/node/chunk-N4ZZFE24.js:536:12)
at TLSSocket.emit (node:events:519:28)
at TLSSocket.emit (/home/circleci/interceptors/repro/node_modules/.pnpm/[email protected]_@[email protected]_@[email protected]_@[email protected]_/node_modules/dd-trace/packages/datadog-instrumentations/src/net.js:65:25)
at emitErrorNT (node:internal/streams/destroy:170:8)
at emitErrorCloseNT (node:internal/streams/destroy:129:3) {
errno: -22,
code: 'EINVAL',
syscall: 'read'
}
Potential solutions
If I revert the changes in #706 99967d7 the issue goes away. I understand that was in place for a reason, but reverting absolutely solves this. It seems like having the _handle exposed gets dicey when something else has closed the socket and (consumers of) MockHttpSocket thinks it's still ok to read/write.
I'm not well versed in this (type of) codebase. I rarely think about this low level socket stuff. I had an LLM hold my hand through the process of generating detailed logs, reproductions. I got very far with very little knowledge, which is a dangerous thing ;)
It's suggesting:
public _read(size: number): void {
if (this.socketState === 'passthrough') {
return
}
super._read(size)
}and then removing the '_handle' aliasing. It suggests that the repeated _read is the source of the event listener oversubscription, and removing _handle stops the "2 owners of a single handle" issue.
This seems to work well. I'm happy to submit a PR, but I wanted to socialize it first, and also make sure it works well for us in production for a bit. Reproducing in an isolated repo has been very hard: I was successful exactly 2 times (out of hundreds of attempts) using the real dd-trace. Trying to simulate its behavior with an identical http.agent did not work. (But of course in our production apps, this happens dozens of times per CI run... π¬)