Skip to content

Commit 4c24cd6

Browse files
authored
feat: populate dnslookup timing with actual DNS resolution time (#743)
1 parent 7fbbc0a commit 4c24cd6

4 files changed

Lines changed: 63 additions & 0 deletions

File tree

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,45 @@ Response is normal object, it contains:
203203
- `timing`: timing object if timing enable.
204204
- `socket`: socket info
205205

206+
### Timing
207+
208+
All timing values are milliseconds elapsed from the start of the request. Enable via `options.timing = true` (default).
209+
210+
```
211+
┌──────────┬──────────┬──────────┬─────────────────┬─────────────┬─────────┬─────────────────┐
212+
│ Queuing │ DNS │ TCP │ Request │ Request │ Waiting │ Content │
213+
│ │ Lookup │ Connect │ Headers Sent │ Body Sent │ (TTFB) │ Download │
214+
└──────────┴──────────┴──────────┴─────────────────┴─────────────┴─────────┴─────────────────┘
215+
| | | | | | | |
216+
0 queuing dnslookup connected requestHeadersSent requestSent waiting contentDownload
217+
```
218+
219+
| Field | Description |
220+
| -------------------- | ------------------------------------------------------------------------------------ |
221+
| `queuing` | Socket pool queuing time. The socket has been assigned to the request. |
222+
| `dnslookup` | DNS lookup completed (only on first request of a new socket, `0` on reused sockets). |
223+
| `connected` | TCP (or TLS) connection is established. |
224+
| `requestHeadersSent` | Request headers have been written to the socket. |
225+
| `requestSent` | Full request (headers + body) has been sent. |
226+
| `waiting` | Time to First Byte (TTFB). Response headers have been received. |
227+
| `contentDownload` | Response body and trailers have been fully received. |
228+
229+
Example:
230+
231+
```json
232+
{
233+
"queuing": 2.8,
234+
"dnslookup": 9.0,
235+
"connected": 12.7,
236+
"requestHeadersSent": 13.4,
237+
"requestSent": 13.6,
238+
"waiting": 24.0,
239+
"contentDownload": 25.1
240+
}
241+
```
242+
243+
> **Note**: `dnslookup` is `0` when the socket is reused from the pool (keep-alive), since no DNS resolution occurs.
244+
206245
## Run test with debug log
207246

208247
```bash

src/diagnosticsChannel.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,18 @@ export function initDiagnosticsChannel(): void {
8787
if (initedDiagnosticsChannel) return;
8888
initedDiagnosticsChannel = true;
8989

90+
// capture DNS lookup time on socket via net.client.socket diagnostics channel
91+
// this channel fires when a new TCP/IPC connection is initiated, giving us
92+
// access to the socket before the 'lookup' event fires
93+
subscribe('net.client.socket', (message: unknown) => {
94+
const socket = (message as { socket: SocketExtend }).socket;
95+
if (socket) {
96+
socket.once('lookup', () => {
97+
socket[symbols.kSocketDnsLookupTime] = performance.now();
98+
});
99+
}
100+
});
101+
90102
let kHandler: symbol;
91103
// This message is published when a new outgoing request is created.
92104
// Note: a request is only loosely completed to a given socket.
@@ -201,6 +213,13 @@ export function initDiagnosticsChannel(): void {
201213
opaque[symbols.kRequestStartTime],
202214
socket[symbols.kSocketStartTime] as number,
203215
);
216+
// kSocketDnsLookupTime - kRequestStartTime = dns lookup time
217+
if (socket[symbols.kSocketDnsLookupTime] !== undefined) {
218+
opaque[symbols.kRequestTiming].dnslookup = performanceTime(
219+
opaque[symbols.kRequestStartTime],
220+
socket[symbols.kSocketDnsLookupTime] as number,
221+
);
222+
}
204223
}
205224
});
206225

src/symbols.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const symbols: {
99
kSocketConnectHost: symbol;
1010
kSocketConnectPort: symbol;
1111
kSocketConnectProtocol: symbol;
12+
kSocketDnsLookupTime: symbol;
1213
kHandledRequests: symbol;
1314
kHandledResponses: symbol;
1415
kRequestSocket: symbol;
@@ -30,6 +31,7 @@ const symbols: {
3031
kSocketConnectHost: Symbol('socket connect params: host'),
3132
kSocketConnectPort: Symbol('socket connect params: port'),
3233
kSocketConnectProtocol: Symbol('socket connect params: protocol'),
34+
kSocketDnsLookupTime: Symbol('socket dns lookup time'),
3335
kHandledRequests: Symbol('handled requests per socket'),
3436
kHandledResponses: Symbol('handled responses per socket'),
3537
kRequestSocket: Symbol('request on the socket'),

test/options.timing.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ describe('options.timing.test.ts', () => {
3030
assert(res.timing.waiting > 0);
3131
assert(res.timing.queuing > 0);
3232
assert(res.timing.connected > 0);
33+
assert(res.timing.dnslookup >= 0);
34+
assert(res.timing.dnslookup <= res.timing.connected);
3335
assert(res.timing.requestHeadersSent > 0);
3436
assert(res.timing.requestSent > 0);
3537
assert(res.timing.contentDownload > 0);
@@ -49,6 +51,7 @@ describe('options.timing.test.ts', () => {
4951
assert(res.timing.waiting > 0);
5052
assert(res.timing.queuing > 0);
5153
assert.equal(res.timing.connected, 0);
54+
assert.equal(res.timing.dnslookup, 0);
5255
assert(res.timing.requestHeadersSent > 0);
5356
assert(res.timing.requestSent > 0);
5457
assert(res.timing.contentDownload > 0);

0 commit comments

Comments
 (0)