From 2661544456a839231802186d4578353b4b73bc85 Mon Sep 17 00:00:00 2001 From: tdeekens Date: Sat, 12 Apr 2025 13:16:30 +0200 Subject: [PATCH 01/17] Add shared stats symbol --- lib/core/symbols.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/symbols.js b/lib/core/symbols.js index f3b563a5419..cfdd8f05f0b 100644 --- a/lib/core/symbols.js +++ b/lib/core/symbols.js @@ -64,5 +64,6 @@ module.exports = { kMaxConcurrentStreams: Symbol('max concurrent streams'), kNoProxyAgent: Symbol('no proxy agent'), kHttpProxyAgent: Symbol('http proxy agent'), - kHttpsProxyAgent: Symbol('https proxy agent') + kHttpsProxyAgent: Symbol('https proxy agent'), + kStats: Symbol('stats') } From 28ec198c2b7a461573b9a68b32c03fe73879308f Mon Sep 17 00:00:00 2001 From: tdeekens Date: Sat, 12 Apr 2025 13:16:53 +0200 Subject: [PATCH 02/17] Add client stats class --- lib/dispatcher/client-stats.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 lib/dispatcher/client-stats.js diff --git a/lib/dispatcher/client-stats.js b/lib/dispatcher/client-stats.js new file mode 100644 index 00000000000..e2ce6ecac82 --- /dev/null +++ b/lib/dispatcher/client-stats.js @@ -0,0 +1,28 @@ +'use strict' + +const { kConnected, kPending, kRunning, kSize } = require('../core/symbols') +const kClient = Symbol('client') + +class ClientStats { + constructor (client) { + this[kClient] = client + } + + get connected () { + return this[kClient][kConnected] + } + + get pending () { + return this[kClient][kPending] + } + + get running () { + return this[kClient][kRunning] + } + + get size () { + return this[kClient][kSize] + } +} + +module.exports = ClientStats From df86d9b60a2bfc64b1282f77169c3d9701cb40ac Mon Sep 17 00:00:00 2001 From: tdeekens Date: Sat, 12 Apr 2025 13:17:07 +0200 Subject: [PATCH 03/17] Use client stats in client --- lib/dispatcher/client.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/dispatcher/client.js b/lib/dispatcher/client.js index be0e8195d0b..380101fddfd 100644 --- a/lib/dispatcher/client.js +++ b/lib/dispatcher/client.js @@ -7,6 +7,7 @@ const util = require('../core/util.js') const { channels } = require('../core/diagnostics.js') const Request = require('../core/request.js') const DispatcherBase = require('./dispatcher-base') +const ClientStats = require('./client-stats') const { InvalidArgumentError, InformationalError, @@ -51,7 +52,8 @@ const { kOnError, kHTTPContext, kMaxConcurrentStreams, - kResume + kResume, + kStats } = require('../core/symbols.js') const connectH1 = require('./client-h1.js') const connectH2 = require('./client-h2.js') @@ -249,6 +251,8 @@ class Client extends DispatcherBase { this[kResume] = (sync) => resume(this, sync) this[kOnError] = (err) => onError(this, err) + + this[kStats] = new ClientStats(this) } get pipelining () { From 4370e3db2c100dbf830a03c42755281cb8730f70 Mon Sep 17 00:00:00 2001 From: tdeekens Date: Sat, 12 Apr 2025 13:17:45 +0200 Subject: [PATCH 04/17] Adjust pool and expose stats from agent --- lib/dispatcher/agent.js | 35 +++++++++++++++++++++++++++++++---- lib/dispatcher/pool-base.js | 3 +-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/lib/dispatcher/agent.js b/lib/dispatcher/agent.js index 46fc15742d1..d8290755b3c 100644 --- a/lib/dispatcher/agent.js +++ b/lib/dispatcher/agent.js @@ -1,7 +1,14 @@ 'use strict' const { InvalidArgumentError } = require('../core/errors') -const { kClients, kRunning, kClose, kDestroy, kDispatch } = require('../core/symbols') +const { + kClients, + kRunning, + kClose, + kDestroy, + kDispatch, + kUrl +} = require('../core/symbols') const DispatcherBase = require('./dispatcher-base') const Pool = require('./pool') const Client = require('./client') @@ -26,7 +33,11 @@ class Agent extends DispatcherBase { throw new InvalidArgumentError('factory must be a function.') } - if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') { + if ( + connect != null && + typeof connect !== 'function' && + typeof connect !== 'object' + ) { throw new InvalidArgumentError('connect must be a function or an object') } @@ -67,10 +78,15 @@ class Agent extends DispatcherBase { [kDispatch] (opts, handler) { let key - if (opts.origin && (typeof opts.origin === 'string' || opts.origin instanceof URL)) { + if ( + opts.origin && + (typeof opts.origin === 'string' || opts.origin instanceof URL) + ) { key = String(opts.origin) } else { - throw new InvalidArgumentError('opts.origin must be a non-empty string or URL.') + throw new InvalidArgumentError( + 'opts.origin must be a non-empty string or URL.' + ) } let dispatcher = this[kClients].get(key) @@ -110,6 +126,17 @@ class Agent extends DispatcherBase { await Promise.all(destroyPromises) } + + get stats () { + const stats = [] + for (const client of this[kClients].values()) { + const stats = client.stats + if (stats) { + stats.push([client[kUrl], stats]) + } + } + return stats + } } module.exports = Agent diff --git a/lib/dispatcher/pool-base.js b/lib/dispatcher/pool-base.js index d0ba2c3c53a..7b3975bf11c 100644 --- a/lib/dispatcher/pool-base.js +++ b/lib/dispatcher/pool-base.js @@ -2,7 +2,7 @@ const DispatcherBase = require('./dispatcher-base') const FixedQueue = require('./fixed-queue') -const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = require('../core/symbols') +const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch, kStats } = require('../core/symbols') const PoolStats = require('./pool-stats') const kClients = Symbol('clients') @@ -16,7 +16,6 @@ const kOnConnectionError = Symbol('onConnectionError') const kGetDispatcher = Symbol('get dispatcher') const kAddClient = Symbol('add client') const kRemoveClient = Symbol('remove client') -const kStats = Symbol('stats') class PoolBase extends DispatcherBase { constructor () { From 2947cba8b22d79e2542856ac6d6f5c39ad37e1ab Mon Sep 17 00:00:00 2001 From: tdeekens Date: Sat, 12 Apr 2025 13:17:59 +0200 Subject: [PATCH 05/17] Add types for changes --- types/client-stats.d.ts | 15 +++++++++++++++ types/client.d.ts | 25 ++++++++++++++----------- 2 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 types/client-stats.d.ts diff --git a/types/client-stats.d.ts b/types/client-stats.d.ts new file mode 100644 index 00000000000..4cb60b0a425 --- /dev/null +++ b/types/client-stats.d.ts @@ -0,0 +1,15 @@ +import Client from './client' + +export default ClientStats + +declare class ClientStats { + constructor (pool: Client) + /** Number of open socket connections in this pool. */ + connected: number + /** Number of open socket connections in this pool that do not have an active request. */ + pending: number + /** Number of currently active requests across all clients in this pool. */ + running: number + /** Number of active, pending, or queued requests across all clients in this pool. */ + size: number +} diff --git a/types/client.d.ts b/types/client.d.ts index 55bfcef5866..4b06c255aa5 100644 --- a/types/client.d.ts +++ b/types/client.d.ts @@ -1,6 +1,7 @@ import { URL } from 'url' import Dispatcher from './dispatcher' import buildConnector from './connector' +import TClientStats from './client-stats' type ClientConnectOptions = Omit @@ -15,6 +16,8 @@ export class Client extends Dispatcher { closed: boolean /** `true` after `client.destroyed()` has been called or `client.close()` has been called and the client shutdown has completed. */ destroyed: boolean + /** Aggregate stats for a Client. */ + readonly stats: TClientStats // Override dispatcher APIs. override connect ( @@ -84,23 +87,23 @@ export declare namespace Client { /** * @description Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation. * @default false - */ + */ allowH2?: boolean; /** * @description Dictates the maximum number of concurrent streams for a single H2 session. It can be overridden by a SETTINGS remote frame. * @default 100 - */ - maxConcurrentStreams?: number + */ + maxConcurrentStreams?: number; } export interface SocketInfo { - localAddress?: string - localPort?: number - remoteAddress?: string - remotePort?: number - remoteFamily?: string - timeout?: number - bytesWritten?: number - bytesRead?: number + localAddress?: string; + localPort?: number; + remoteAddress?: string; + remotePort?: number; + remoteFamily?: string; + timeout?: number; + bytesWritten?: number; + bytesRead?: number; } } From 39c76176a202679c58f433313ed14944c0e36c3b Mon Sep 17 00:00:00 2001 From: tdeekens Date: Sat, 12 Apr 2025 13:18:12 +0200 Subject: [PATCH 06/17] Add test for client --- test/types/client.test-d.ts | 388 +++++++++++++++++++++++------------- 1 file changed, 251 insertions(+), 137 deletions(-) diff --git a/test/types/client.test-d.ts b/test/types/client.test-d.ts index 3e6d9c060f8..faafda72711 100644 --- a/test/types/client.test-d.ts +++ b/test/types/client.test-d.ts @@ -1,84 +1,126 @@ import { Duplex, Readable, Writable } from 'stream' -import { expectAssignable } from 'tsd' +import { expectAssignable, expectType } from 'tsd' import { Client, Dispatcher } from '../..' import { URL } from 'url' expectAssignable(new Client('')) expectAssignable(new Client('', {})) -expectAssignable(new Client('', { - maxRequestsPerClient: 10 -})) -expectAssignable(new Client('', { - connect: { rejectUnauthorized: false } -})) +expectAssignable( + new Client('', { + maxRequestsPerClient: 10 + }) +) +expectAssignable( + new Client('', { + connect: { rejectUnauthorized: false } + }) +) expectAssignable(new Client(new URL('http://localhost'), {})) /** * Tests for Client.Options: */ -expectAssignable(new Client('', { - maxHeaderSize: 16384 -})) -expectAssignable(new Client('', { - headersTimeout: 300e3 -})) -expectAssignable(new Client('', { - connectTimeout: 300e3 -})) -expectAssignable(new Client('', { - bodyTimeout: 300e3 -})) -expectAssignable(new Client('', { - keepAliveTimeout: 4e3 -})) -expectAssignable(new Client('', { - keepAliveMaxTimeout: 600e3 -})) -expectAssignable(new Client('', { - keepAliveTimeoutThreshold: 1e3 -})) -expectAssignable(new Client('', { - socketPath: '/var/run/docker.sock' -})) -expectAssignable(new Client('', { - pipelining: 1 -})) -expectAssignable(new Client('', { - strictContentLength: true -})) -expectAssignable(new Client('', { - maxCachedSessions: 1 -})) -expectAssignable(new Client('', { - maxRedirections: 1 -})) -expectAssignable(new Client('', { - maxRequestsPerClient: 1 -})) -expectAssignable(new Client('', { - localAddress: '127.0.0.1' -})) -expectAssignable(new Client('', { - maxResponseSize: -1 -})) -expectAssignable(new Client('', { - autoSelectFamily: true -})) -expectAssignable(new Client('', { - autoSelectFamilyAttemptTimeout: 300e3 -})) -expectAssignable(new Client('', { - interceptors: { - Client: [(dispatcher) => { - expectAssignable(dispatcher) - return (opts, handlers) => { - expectAssignable(opts) - expectAssignable(handlers) - return dispatcher(opts, handlers) - } - }] - } -})) +expectAssignable( + new Client('', { + maxHeaderSize: 16384 + }) +) +expectAssignable( + new Client('', { + headersTimeout: 300e3 + }) +) +expectAssignable( + new Client('', { + connectTimeout: 300e3 + }) +) +expectAssignable( + new Client('', { + bodyTimeout: 300e3 + }) +) +expectAssignable( + new Client('', { + keepAliveTimeout: 4e3 + }) +) +expectAssignable( + new Client('', { + keepAliveMaxTimeout: 600e3 + }) +) +expectAssignable( + new Client('', { + keepAliveTimeoutThreshold: 1e3 + }) +) +expectAssignable( + new Client('', { + socketPath: '/var/run/docker.sock' + }) +) +expectAssignable( + new Client('', { + pipelining: 1 + }) +) +expectAssignable( + new Client('', { + strictContentLength: true + }) +) +expectAssignable( + new Client('', { + maxCachedSessions: 1 + }) +) +expectAssignable( + new Client('', { + maxRedirections: 1 + }) +) +expectAssignable( + new Client('', { + maxRequestsPerClient: 1 + }) +) +expectAssignable( + new Client('', { + localAddress: '127.0.0.1' + }) +) +expectAssignable( + new Client('', { + maxResponseSize: -1 + }) +) +expectAssignable( + new Client('', { + autoSelectFamily: true + }) +) +expectAssignable( + new Client('', { + autoSelectFamilyAttemptTimeout: 300e3 + }) +) +expectAssignable( + new Client('', { + interceptors: { + Client: [ + (dispatcher) => { + expectAssignable(dispatcher) + return (opts, handlers) => { + expectAssignable(opts) + expectAssignable(handlers) + return dispatcher(opts, handlers) + } + } + ] + } + }) +) { const client = new Client('') @@ -89,85 +131,151 @@ expectAssignable(new Client('', { expectAssignable(client.destroyed) // request - expectAssignable>(client.request({ origin: '', path: '', method: 'GET' })) - expectAssignable>(client.request({ origin: new URL('http://localhost:3000'), path: '', method: 'GET' })) - expectAssignable(client.request({ origin: '', path: '', method: 'GET' }, (err, data) => { - expectAssignable(err) - expectAssignable(data) - })) - expectAssignable(client.request({ origin: new URL('http://localhost:3000'), path: '', method: 'GET' }, (err, data) => { - expectAssignable(err) - expectAssignable(data) - })) + expectAssignable>( + client.request({ origin: '', path: '', method: 'GET' }) + ) + expectAssignable>( + client.request({ + origin: new URL('http://localhost:3000'), + path: '', + method: 'GET' + }) + ) + expectAssignable( + client.request({ origin: '', path: '', method: 'GET' }, (err, data) => { + expectAssignable(err) + expectAssignable(data) + }) + ) + expectAssignable( + client.request( + { origin: new URL('http://localhost:3000'), path: '', method: 'GET' }, + (err, data) => { + expectAssignable(err) + expectAssignable(data) + } + ) + ) // stream - expectAssignable>(client.stream({ origin: '', path: '', method: 'GET' }, data => { - expectAssignable(data) - return new Writable() - })) - expectAssignable>(client.stream({ origin: new URL('http://localhost'), path: '', method: 'GET' }, data => { - expectAssignable(data) - return new Writable() - })) - expectAssignable(client.stream( - { origin: '', path: '', method: 'GET' }, - data => { + expectAssignable>( + client.stream({ origin: '', path: '', method: 'GET' }, (data) => { expectAssignable(data) return new Writable() - }, - (err, data) => { - expectAssignable(err) - expectAssignable(data) - } - )) - expectAssignable(client.stream( - { origin: new URL('http://localhost'), path: '', method: 'GET' }, - data => { - expectAssignable(data) - return new Writable() - }, - (err, data) => { - expectAssignable(err) - expectAssignable(data) - } - )) + }) + ) + expectAssignable>( + client.stream( + { origin: new URL('http://localhost'), path: '', method: 'GET' }, + (data) => { + expectAssignable(data) + return new Writable() + } + ) + ) + expectAssignable( + client.stream( + { origin: '', path: '', method: 'GET' }, + (data) => { + expectAssignable(data) + return new Writable() + }, + (err, data) => { + expectAssignable(err) + expectAssignable(data) + } + ) + ) + expectAssignable( + client.stream( + { origin: new URL('http://localhost'), path: '', method: 'GET' }, + (data) => { + expectAssignable(data) + return new Writable() + }, + (err, data) => { + expectAssignable(err) + expectAssignable(data) + } + ) + ) // pipeline - expectAssignable(client.pipeline({ origin: '', path: '', method: 'GET' }, data => { - expectAssignable(data) - return new Readable() - })) - expectAssignable(client.pipeline({ origin: new URL('http://localhost'), path: '', method: 'GET' }, data => { - expectAssignable(data) - return new Readable() - })) + expectAssignable( + client.pipeline({ origin: '', path: '', method: 'GET' }, (data) => { + expectAssignable(data) + return new Readable() + }) + ) + expectAssignable( + client.pipeline( + { origin: new URL('http://localhost'), path: '', method: 'GET' }, + (data) => { + expectAssignable(data) + return new Readable() + } + ) + ) // upgrade - expectAssignable>(client.upgrade({ path: '' })) - expectAssignable>(client.upgrade({ path: '', headers: [] })) - expectAssignable>(client.upgrade({ path: '', headers: {} })) - expectAssignable>(client.upgrade({ path: '', headers: null })) - expectAssignable(client.upgrade({ path: '' }, (err, data) => { - expectAssignable(err) - expectAssignable(data) - })) + expectAssignable>( + client.upgrade({ path: '' }) + ) + expectAssignable>( + client.upgrade({ path: '', headers: [] }) + ) + expectAssignable>( + client.upgrade({ path: '', headers: {} }) + ) + expectAssignable>( + client.upgrade({ path: '', headers: null }) + ) + expectAssignable( + client.upgrade({ path: '' }, (err, data) => { + expectAssignable(err) + expectAssignable(data) + }) + ) // connect - expectAssignable>(client.connect({ path: '' })) - expectAssignable>(client.connect({ path: '', headers: [] })) - expectAssignable>(client.connect({ path: '', headers: {} })) - expectAssignable>(client.connect({ path: '', headers: null })) - expectAssignable(client.connect({ path: '' }, (err, data) => { - expectAssignable(err) - expectAssignable(data) - })) + expectAssignable>( + client.connect({ path: '' }) + ) + expectAssignable>( + client.connect({ path: '', headers: [] }) + ) + expectAssignable>( + client.connect({ path: '', headers: {} }) + ) + expectAssignable>( + client.connect({ path: '', headers: null }) + ) + expectAssignable( + client.connect({ path: '' }, (err, data) => { + expectAssignable(err) + expectAssignable(data) + }) + ) // dispatch - expectAssignable(client.dispatch({ origin: '', path: '', method: 'GET' }, {})) - expectAssignable(client.dispatch({ origin: '', path: '', method: 'GET', headers: [] }, {})) - expectAssignable(client.dispatch({ origin: '', path: '', method: 'GET', headers: {} }, {})) - expectAssignable(client.dispatch({ origin: '', path: '', method: 'GET', headers: null }, {})) - expectAssignable(client.dispatch({ origin: new URL('http://localhost'), path: '', method: 'GET' }, {})) + expectAssignable( + client.dispatch({ origin: '', path: '', method: 'GET' }, {}) + ) + expectAssignable( + client.dispatch({ origin: '', path: '', method: 'GET', headers: [] }, {}) + ) + expectAssignable( + client.dispatch({ origin: '', path: '', method: 'GET', headers: {} }, {}) + ) + expectAssignable( + client.dispatch({ origin: '', path: '', method: 'GET', headers: null }, {}) + ) + expectAssignable( + client.dispatch( + { origin: new URL('http://localhost'), path: '', method: 'GET' }, + {} + ) + ) // close expectAssignable>(client.close()) @@ -180,4 +288,10 @@ expectAssignable(new Client('', { expectAssignable(client.destroy(() => {})) expectAssignable(client.destroy(new Error(), () => {})) expectAssignable(client.destroy(null, () => {})) + + // stats + expectType(client.stats.connected) + expectType(client.stats.pending) + expectType(client.stats.running) + expectType(client.stats.size) } From 651eb865173b3bb00ddb3e7756aefdde054b0f8b Mon Sep 17 00:00:00 2001 From: tdeekens Date: Sat, 12 Apr 2025 13:29:40 +0200 Subject: [PATCH 07/17] Fix formatting changes --- lib/dispatcher/agent.js | 22 ++++------------------ types/client.d.ts | 16 ++++++++-------- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/lib/dispatcher/agent.js b/lib/dispatcher/agent.js index d8290755b3c..0f0c1757d01 100644 --- a/lib/dispatcher/agent.js +++ b/lib/dispatcher/agent.js @@ -1,14 +1,7 @@ 'use strict' const { InvalidArgumentError } = require('../core/errors') -const { - kClients, - kRunning, - kClose, - kDestroy, - kDispatch, - kUrl -} = require('../core/symbols') +const { kClients, kRunning, kClose, kDestroy, kDispatch, kUrl } = require('../core/symbols') const DispatcherBase = require('./dispatcher-base') const Pool = require('./pool') const Client = require('./client') @@ -34,9 +27,7 @@ class Agent extends DispatcherBase { } if ( - connect != null && - typeof connect !== 'function' && - typeof connect !== 'object' + connect != null && typeof connect !== 'function' && typeof connect !== 'object' ) { throw new InvalidArgumentError('connect must be a function or an object') } @@ -78,15 +69,10 @@ class Agent extends DispatcherBase { [kDispatch] (opts, handler) { let key - if ( - opts.origin && - (typeof opts.origin === 'string' || opts.origin instanceof URL) - ) { + if (opts.origin && (typeof opts.origin === 'string' || opts.origin instanceof URL)) { key = String(opts.origin) } else { - throw new InvalidArgumentError( - 'opts.origin must be a non-empty string or URL.' - ) + throw new InvalidArgumentError('opts.origin must be a non-empty string or URL.') } let dispatcher = this[kClients].get(key) diff --git a/types/client.d.ts b/types/client.d.ts index 4b06c255aa5..088a673eb52 100644 --- a/types/client.d.ts +++ b/types/client.d.ts @@ -96,14 +96,14 @@ export declare namespace Client { maxConcurrentStreams?: number; } export interface SocketInfo { - localAddress?: string; - localPort?: number; - remoteAddress?: string; - remotePort?: number; - remoteFamily?: string; - timeout?: number; - bytesWritten?: number; - bytesRead?: number; + localAddress?: string + localPort?: number + remoteAddress?: string + remotePort?: number + remoteFamily?: string + timeout?: number + bytesWritten?: number + bytesRead?: number } } From 0c22ba546e546ff4a92525104137c79e902725c1 Mon Sep 17 00:00:00 2001 From: tdeekens Date: Sat, 12 Apr 2025 13:33:58 +0200 Subject: [PATCH 08/17] Add types for agent stats getter --- types/agent.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/types/agent.d.ts b/types/agent.d.ts index ee313b5209b..9284ccb35d0 100644 --- a/types/agent.d.ts +++ b/types/agent.d.ts @@ -1,6 +1,8 @@ import { URL } from 'url' import Pool from './pool' import Dispatcher from './dispatcher' +import TClientStats from './client-stats' +import TPoolStats from './pool-stats' export default Agent @@ -12,6 +14,8 @@ declare class Agent extends Dispatcher { destroyed: boolean /** Dispatches a request. */ dispatch (options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandler): boolean + /** Aggregate stats for a Agent by origin. */ + readonly stats: [string, [TClientStats, TPoolStats]][] } declare namespace Agent { From 8855ecb5fda73f7c645d862bb2491b31c38b84f0 Mon Sep 17 00:00:00 2001 From: tdeekens Date: Sat, 12 Apr 2025 13:35:17 +0200 Subject: [PATCH 09/17] fixup! Fix formatting changes --- lib/dispatcher/agent.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/dispatcher/agent.js b/lib/dispatcher/agent.js index 0f0c1757d01..1c15b6a8f38 100644 --- a/lib/dispatcher/agent.js +++ b/lib/dispatcher/agent.js @@ -26,9 +26,7 @@ class Agent extends DispatcherBase { throw new InvalidArgumentError('factory must be a function.') } - if ( - connect != null && typeof connect !== 'function' && typeof connect !== 'object' - ) { + if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') { throw new InvalidArgumentError('connect must be a function or an object') } From ab942e49f42858f639ac6ab064deb05b1ac993da Mon Sep 17 00:00:00 2001 From: tdeekens Date: Sun, 13 Apr 2025 20:40:23 +0200 Subject: [PATCH 10/17] Move pool and client stats into util/stats --- lib/dispatcher/client-stats.js | 28 -------------- lib/dispatcher/client.js | 2 +- lib/dispatcher/pool-base.js | 2 +- lib/dispatcher/pool-stats.js | 36 ------------------ lib/util/stats.js | 67 ++++++++++++++++++++++++++++++++++ mise.toml | 2 + 6 files changed, 71 insertions(+), 66 deletions(-) delete mode 100644 lib/dispatcher/client-stats.js delete mode 100644 lib/dispatcher/pool-stats.js create mode 100644 lib/util/stats.js create mode 100644 mise.toml diff --git a/lib/dispatcher/client-stats.js b/lib/dispatcher/client-stats.js deleted file mode 100644 index e2ce6ecac82..00000000000 --- a/lib/dispatcher/client-stats.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict' - -const { kConnected, kPending, kRunning, kSize } = require('../core/symbols') -const kClient = Symbol('client') - -class ClientStats { - constructor (client) { - this[kClient] = client - } - - get connected () { - return this[kClient][kConnected] - } - - get pending () { - return this[kClient][kPending] - } - - get running () { - return this[kClient][kRunning] - } - - get size () { - return this[kClient][kSize] - } -} - -module.exports = ClientStats diff --git a/lib/dispatcher/client.js b/lib/dispatcher/client.js index 380101fddfd..43fe7c88dbe 100644 --- a/lib/dispatcher/client.js +++ b/lib/dispatcher/client.js @@ -4,10 +4,10 @@ const assert = require('node:assert') const net = require('node:net') const http = require('node:http') const util = require('../core/util.js') +const { ClientStats } = require('../util/stats.js') const { channels } = require('../core/diagnostics.js') const Request = require('../core/request.js') const DispatcherBase = require('./dispatcher-base') -const ClientStats = require('./client-stats') const { InvalidArgumentError, InformationalError, diff --git a/lib/dispatcher/pool-base.js b/lib/dispatcher/pool-base.js index 7b3975bf11c..043e2cd67ef 100644 --- a/lib/dispatcher/pool-base.js +++ b/lib/dispatcher/pool-base.js @@ -1,9 +1,9 @@ 'use strict' +const { PoolStats } = require('../util/stats.js') const DispatcherBase = require('./dispatcher-base') const FixedQueue = require('./fixed-queue') const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch, kStats } = require('../core/symbols') -const PoolStats = require('./pool-stats') const kClients = Symbol('clients') const kNeedDrain = Symbol('needDrain') diff --git a/lib/dispatcher/pool-stats.js b/lib/dispatcher/pool-stats.js deleted file mode 100644 index c739211f098..00000000000 --- a/lib/dispatcher/pool-stats.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict' - -const { kFree, kConnected, kPending, kQueued, kRunning, kSize } = require('../core/symbols') -const kPool = Symbol('pool') - -class PoolStats { - constructor (pool) { - this[kPool] = pool - } - - get connected () { - return this[kPool][kConnected] - } - - get free () { - return this[kPool][kFree] - } - - get pending () { - return this[kPool][kPending] - } - - get queued () { - return this[kPool][kQueued] - } - - get running () { - return this[kPool][kRunning] - } - - get size () { - return this[kPool][kSize] - } -} - -module.exports = PoolStats diff --git a/lib/util/stats.js b/lib/util/stats.js new file mode 100644 index 00000000000..e6bb28dd47f --- /dev/null +++ b/lib/util/stats.js @@ -0,0 +1,67 @@ +'use strict' + +const { + kConnected, + kPending, + kRunning, + kSize, + kFree, + kQueued +} = require('../core/symbols') + +const kPool = Symbol('pool') +const kClient = Symbol('client') + +class ClientStats { + constructor (client) { + this[kClient] = client + } + + get connected () { + return this[kClient][kConnected] + } + + get pending () { + return this[kClient][kPending] + } + + get running () { + return this[kClient][kRunning] + } + + get size () { + return this[kClient][kSize] + } +} + +class PoolStats { + constructor (pool) { + this[kPool] = pool + } + + get connected () { + return this[kPool][kConnected] + } + + get free () { + return this[kPool][kFree] + } + + get pending () { + return this[kPool][kPending] + } + + get queued () { + return this[kPool][kQueued] + } + + get running () { + return this[kPool][kRunning] + } + + get size () { + return this[kPool][kSize] + } +} + +module.exports = { ClientStats, PoolStats } diff --git a/mise.toml b/mise.toml new file mode 100644 index 00000000000..6a0493c69bc --- /dev/null +++ b/mise.toml @@ -0,0 +1,2 @@ +[tools] +node = "22" From f2452c4f505f02e698253e9954bbb9f74e4c3312 Mon Sep 17 00:00:00 2001 From: tdeekens Date: Sun, 13 Apr 2025 21:49:01 +0200 Subject: [PATCH 11/17] Add tests and fix implementation --- lib/dispatcher/agent.js | 9 ++++----- lib/dispatcher/client.js | 4 ++++ test/client.js | 30 ++++++++++++++++++++++++++++++ test/node-test/agent.js | 38 ++++++++++++++++++++++++++++++++++++++ test/pool.js | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 5 deletions(-) diff --git a/lib/dispatcher/agent.js b/lib/dispatcher/agent.js index 1c15b6a8f38..9183bb78d55 100644 --- a/lib/dispatcher/agent.js +++ b/lib/dispatcher/agent.js @@ -112,14 +112,13 @@ class Agent extends DispatcherBase { } get stats () { - const stats = [] + const allClientStats = [] for (const client of this[kClients].values()) { - const stats = client.stats - if (stats) { - stats.push([client[kUrl], stats]) + if (client.stats) { + allClientStats.push([client[kUrl].origin, client.stats]) } } - return stats + return allClientStats } } diff --git a/lib/dispatcher/client.js b/lib/dispatcher/client.js index 43fe7c88dbe..89e35a31251 100644 --- a/lib/dispatcher/client.js +++ b/lib/dispatcher/client.js @@ -264,6 +264,10 @@ class Client extends DispatcherBase { this[kResume](true) } + get stats () { + return this[kStats] + } + get [kPending] () { return this[kQueue].length - this[kPendingIdx] } diff --git a/test/client.js b/test/client.js index 6b031be173a..78bcde2cbab 100644 --- a/test/client.js +++ b/test/client.js @@ -2166,3 +2166,33 @@ test('\\n in Method', async (t) => { t.strictEqual(err.message, 'invalid request method') }) }) + +test('stats', async (t) => { + t = tspl(t, { plan: 3 }) + + const server = createServer({ joinDuplicateHeaders: true }, (req, res) => { + t.strictEqual('/', req.url) + t.strictEqual('GET', req.method) + t.strictEqual(`localhost:${server.address().port}`, req.headers.host) + res.setHeader('Content-Type', 'text/plain') + res.end('hello') + }) + after(() => server.close()) + + server.listen(0, () => { + const client = new Client(`http://localhost:${server.address().port}`) + after(() => client.close()) + + client.request({ + path: '/', + method: 'GET' + }, (err, data) => { + t.ifError(err) + t.strictEqual(client.stats.connected, true) + t.strictEqual(client.stats.pending, 1) + t.strictEqual(client.stats.running, 1) + }) + }) + + await t.completed +}) diff --git a/test/node-test/agent.js b/test/node-test/agent.js index 6f7d7ffe53c..20486e0e1eb 100644 --- a/test/node-test/agent.js +++ b/test/node-test/agent.js @@ -808,3 +808,41 @@ test('the dispatcher is truly global', t => { assert.ok(require.resolve('../../index.js') in require.cache) assert.strictEqual(agent, undiciFresh.getGlobalDispatcher()) }) + +test('stats', async t => { + const p = tspl(t, { plan: 8 }) + const wanted = 'payload' + + const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => { + p.strictEqual('/', req.url) + p.strictEqual('GET', req.method) + res.end(wanted) + }) + + t.after(closeServerAsPromise(server)) + + const dispatcher = new Agent({ + connect: { + servername: 'agent1' + } + }) + + server.listen(0, () => { + request(`http://localhost:${server.address().port}`, { dispatcher }) + .then(({ statusCode, headers, body }) => { + p.strictEqual(statusCode, 200) + const agentStats = dispatcher.stats[0] + const [origin, stats] = agentStats + p.strictEqual(origin, `http://localhost:${server.address().port}`) + p.strictEqual(stats.connected, 1) + p.strictEqual(stats.pending, 0) + p.strictEqual(stats.running, 0) + p.strictEqual(stats.size, 0) + }) + .catch(err => { + p.fail(err) + }) + }) + + await p.completed +}) diff --git a/test/pool.js b/test/pool.js index 2b847af70d3..31d74c7d061 100644 --- a/test/pool.js +++ b/test/pool.js @@ -1150,3 +1150,35 @@ test('pool destroy fails queued requests', async (t) => { }) await t.completed }) + +test('stats', async (t) => { + t = tspl(t, { plan: 11 }) + + const server = createServer({ joinDuplicateHeaders: true }, (req, res) => { + t.strictEqual('/', req.url) + t.strictEqual('GET', req.method) + res.setHeader('content-type', 'text/plain') + res.end('hello') + }) + after(() => server.close()) + + server.listen(0, async () => { + const client = new Pool(`http://localhost:${server.address().port}`) + after(() => client.destroy()) + + t.strictEqual(client[kUrl].origin, `http://localhost:${server.address().port}`) + + client.request({ path: '/', method: 'GET' }, (err, { statusCode, headers, body }) => { + t.ifError(err) + t.strictEqual(statusCode, 200) + t.strictEqual(client.stats.connected, 1) + t.strictEqual(client.stats.free, 0) + t.strictEqual(client.stats.pending, 0) + t.strictEqual(client.stats.queued, 0) + t.strictEqual(client.stats.running, 1) + t.strictEqual(client.stats.size, 1) + }) + }) + + await t.completed +}) From 15bc63b63f3437a938505be6999f6b91f1503e3a Mon Sep 17 00:00:00 2001 From: tdeekens Date: Tue, 15 Apr 2025 23:32:00 +0200 Subject: [PATCH 12/17] Adds documentation and fixes --- docs/docs/api/Agent.md | 12 +++++++++--- docs/docs/api/ClientStats.md | 27 +++++++++++++++++++++++++++ types/agent.d.ts | 2 +- types/client-stats.d.ts | 10 +++++----- 4 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 docs/docs/api/ClientStats.md diff --git a/docs/docs/api/Agent.md b/docs/docs/api/Agent.md index 5141ce3d3a7..c1cc92f9b2f 100644 --- a/docs/docs/api/Agent.md +++ b/docs/docs/api/Agent.md @@ -10,7 +10,7 @@ Requests are not guaranteed to be dispatched in order of invocation. Arguments: -* **options** `AgentOptions` (optional) +- **options** `AgentOptions` (optional) Returns: `Agent` @@ -18,7 +18,7 @@ Returns: `Agent` Extends: [`PoolOptions`](/docs/docs/api/Pool.md#parameter-pooloptions) -* **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)` +- **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)` ## Instance Properties @@ -48,7 +48,7 @@ Implements [`Dispatcher.dispatch(options, handler)`](/docs/docs/api/Dispatcher.m Extends: [`DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dispatchoptions) -* **origin** `string | URL` +- **origin** `string | URL` Implements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise). @@ -75,3 +75,9 @@ See [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatche ### `Agent.upgrade(options[, callback])` See [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback). + +### `Agent.stats()` + +Returns stats of all clients for each origin in format of `[string, [TClientStats | TPoolStats]][]` + +See [`PoolStats`](/docs/docs/api/PoolStats.md) and [`ClientStats`](/docs/docs/api/ClientStats.md). diff --git a/docs/docs/api/ClientStats.md b/docs/docs/api/ClientStats.md new file mode 100644 index 00000000000..fa899d482c8 --- /dev/null +++ b/docs/docs/api/ClientStats.md @@ -0,0 +1,27 @@ +# Class: ClientStats + +Stats for a [Client](/docs/docs/api/Client.md). + +## `new ClientStats(client)` + +Arguments: + +* **client** `Client` - Client from which to return stats. + +## Instance Properties + +### `ClientStats.connected` + +Boolean if socket as open connection by this client. + +### `ClientStats.pending` + +Number of pending requests of this client. + +### `ClientStats.running` + +Number of currently active requests across this client. + +### `ClientStats.size` + +Number of active, pending, or queued requests of this clients. diff --git a/types/agent.d.ts b/types/agent.d.ts index 9284ccb35d0..5d271545af2 100644 --- a/types/agent.d.ts +++ b/types/agent.d.ts @@ -15,7 +15,7 @@ declare class Agent extends Dispatcher { /** Dispatches a request. */ dispatch (options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandler): boolean /** Aggregate stats for a Agent by origin. */ - readonly stats: [string, [TClientStats, TPoolStats]][] + readonly stats: [string, [TClientStats | TPoolStats]][] } declare namespace Agent { diff --git a/types/client-stats.d.ts b/types/client-stats.d.ts index 4cb60b0a425..ad9bd8482df 100644 --- a/types/client-stats.d.ts +++ b/types/client-stats.d.ts @@ -4,12 +4,12 @@ export default ClientStats declare class ClientStats { constructor (pool: Client) - /** Number of open socket connections in this pool. */ - connected: number - /** Number of open socket connections in this pool that do not have an active request. */ + /** If socket has open connection. */ + connected: boolean + /** Number of open socket connections in this client that do not have an active request. */ pending: number - /** Number of currently active requests across all clients in this pool. */ + /** Number of currently active requests of this client. */ running: number - /** Number of active, pending, or queued requests across all clients in this pool. */ + /** Number of active, pending, or queued requests of this client. */ size: number } From 5a935298e870431d9879b0f5ae6950c241295ccd Mon Sep 17 00:00:00 2001 From: tdeekens Date: Tue, 22 Apr 2025 08:45:50 +0200 Subject: [PATCH 13/17] Fix to remove mise --- mise.toml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 mise.toml diff --git a/mise.toml b/mise.toml deleted file mode 100644 index 6a0493c69bc..00000000000 --- a/mise.toml +++ /dev/null @@ -1,2 +0,0 @@ -[tools] -node = "22" From 435acc8c39a1a4848f281282f7997df581d300cb Mon Sep 17 00:00:00 2001 From: tdeekens Date: Tue, 22 Apr 2025 08:49:53 +0200 Subject: [PATCH 14/17] Restore docs list star --- docs/docs/api/Agent.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/api/Agent.md b/docs/docs/api/Agent.md index c1cc92f9b2f..716466de30b 100644 --- a/docs/docs/api/Agent.md +++ b/docs/docs/api/Agent.md @@ -10,7 +10,7 @@ Requests are not guaranteed to be dispatched in order of invocation. Arguments: -- **options** `AgentOptions` (optional) +* **options** `AgentOptions` (optional) Returns: `Agent` @@ -18,7 +18,7 @@ Returns: `Agent` Extends: [`PoolOptions`](/docs/docs/api/Pool.md#parameter-pooloptions) -- **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)` +* **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)` ## Instance Properties @@ -48,7 +48,7 @@ Implements [`Dispatcher.dispatch(options, handler)`](/docs/docs/api/Dispatcher.m Extends: [`DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dispatchoptions) -- **origin** `string | URL` +* **origin** `string | URL` Implements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise). From 373736e76b61be20a514144362783e25e648be37 Mon Sep 17 00:00:00 2001 From: tdeekens Date: Tue, 22 Apr 2025 08:55:17 +0200 Subject: [PATCH 15/17] Fix boolean type --- test/types/client.test-d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/types/client.test-d.ts b/test/types/client.test-d.ts index faafda72711..61168a1a5bb 100644 --- a/test/types/client.test-d.ts +++ b/test/types/client.test-d.ts @@ -290,7 +290,7 @@ expectAssignable( expectAssignable(client.destroy(null, () => {})) // stats - expectType(client.stats.connected) + expectType(client.stats.connected) expectType(client.stats.pending) expectType(client.stats.running) expectType(client.stats.size) From 52e7d940bf3d0669a93b50d5f9fe6954614df57d Mon Sep 17 00:00:00 2001 From: tdeekens Date: Sat, 26 Apr 2025 22:06:53 +0200 Subject: [PATCH 16/17] Refactor to return object over list of list --- docs/docs/api/Agent.md | 2 +- lib/dispatcher/agent.js | 4 ++-- test/node-test/agent.js | 15 +++++++-------- types/agent.d.ts | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/docs/api/Agent.md b/docs/docs/api/Agent.md index 716466de30b..2a8e30bac14 100644 --- a/docs/docs/api/Agent.md +++ b/docs/docs/api/Agent.md @@ -78,6 +78,6 @@ See [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dis ### `Agent.stats()` -Returns stats of all clients for each origin in format of `[string, [TClientStats | TPoolStats]][]` +Returns an object of stats by origin in the format of `Record` See [`PoolStats`](/docs/docs/api/PoolStats.md) and [`ClientStats`](/docs/docs/api/ClientStats.md). diff --git a/lib/dispatcher/agent.js b/lib/dispatcher/agent.js index 9183bb78d55..938333b63a4 100644 --- a/lib/dispatcher/agent.js +++ b/lib/dispatcher/agent.js @@ -112,10 +112,10 @@ class Agent extends DispatcherBase { } get stats () { - const allClientStats = [] + const allClientStats = {} for (const client of this[kClients].values()) { if (client.stats) { - allClientStats.push([client[kUrl].origin, client.stats]) + allClientStats[client[kUrl].origin] = client.stats } } return allClientStats diff --git a/test/node-test/agent.js b/test/node-test/agent.js index 20486e0e1eb..32977df5989 100644 --- a/test/node-test/agent.js +++ b/test/node-test/agent.js @@ -810,7 +810,7 @@ test('the dispatcher is truly global', t => { }) test('stats', async t => { - const p = tspl(t, { plan: 8 }) + const p = tspl(t, { plan: 7 }) const wanted = 'payload' const server = http.createServer({ joinDuplicateHeaders: true }, (req, res) => { @@ -831,13 +831,12 @@ test('stats', async t => { request(`http://localhost:${server.address().port}`, { dispatcher }) .then(({ statusCode, headers, body }) => { p.strictEqual(statusCode, 200) - const agentStats = dispatcher.stats[0] - const [origin, stats] = agentStats - p.strictEqual(origin, `http://localhost:${server.address().port}`) - p.strictEqual(stats.connected, 1) - p.strictEqual(stats.pending, 0) - p.strictEqual(stats.running, 0) - p.strictEqual(stats.size, 0) + const originForStats = `http://localhost:${server.address().port}` + const agentStats = dispatcher.stats[originForStats] + p.strictEqual(agentStats.connected, 1) + p.strictEqual(agentStats.pending, 0) + p.strictEqual(agentStats.running, 0) + p.strictEqual(agentStats.size, 0) }) .catch(err => { p.fail(err) diff --git a/types/agent.d.ts b/types/agent.d.ts index 5d271545af2..2132560744a 100644 --- a/types/agent.d.ts +++ b/types/agent.d.ts @@ -15,7 +15,7 @@ declare class Agent extends Dispatcher { /** Dispatches a request. */ dispatch (options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandler): boolean /** Aggregate stats for a Agent by origin. */ - readonly stats: [string, [TClientStats | TPoolStats]][] + readonly stats: Record } declare namespace Agent { From 7ac13ffd0274bc28ab5967f82db43ca8500b6023 Mon Sep 17 00:00:00 2001 From: tdeekens Date: Tue, 29 Apr 2025 22:51:07 +0200 Subject: [PATCH 17/17] Refactor to not use getters but object --- lib/core/symbols.js | 3 +- lib/dispatcher/client.js | 7 ++--- lib/dispatcher/pool-base.js | 6 ++-- lib/util/stats.js | 55 +++++++------------------------------ 4 files changed, 15 insertions(+), 56 deletions(-) diff --git a/lib/core/symbols.js b/lib/core/symbols.js index cfdd8f05f0b..f3b563a5419 100644 --- a/lib/core/symbols.js +++ b/lib/core/symbols.js @@ -64,6 +64,5 @@ module.exports = { kMaxConcurrentStreams: Symbol('max concurrent streams'), kNoProxyAgent: Symbol('no proxy agent'), kHttpProxyAgent: Symbol('http proxy agent'), - kHttpsProxyAgent: Symbol('https proxy agent'), - kStats: Symbol('stats') + kHttpsProxyAgent: Symbol('https proxy agent') } diff --git a/lib/dispatcher/client.js b/lib/dispatcher/client.js index 89e35a31251..0b0990206e7 100644 --- a/lib/dispatcher/client.js +++ b/lib/dispatcher/client.js @@ -52,8 +52,7 @@ const { kOnError, kHTTPContext, kMaxConcurrentStreams, - kResume, - kStats + kResume } = require('../core/symbols.js') const connectH1 = require('./client-h1.js') const connectH2 = require('./client-h2.js') @@ -251,8 +250,6 @@ class Client extends DispatcherBase { this[kResume] = (sync) => resume(this, sync) this[kOnError] = (err) => onError(this, err) - - this[kStats] = new ClientStats(this) } get pipelining () { @@ -265,7 +262,7 @@ class Client extends DispatcherBase { } get stats () { - return this[kStats] + return new ClientStats(this) } get [kPending] () { diff --git a/lib/dispatcher/pool-base.js b/lib/dispatcher/pool-base.js index 043e2cd67ef..4b7b6a26f1d 100644 --- a/lib/dispatcher/pool-base.js +++ b/lib/dispatcher/pool-base.js @@ -3,7 +3,7 @@ const { PoolStats } = require('../util/stats.js') const DispatcherBase = require('./dispatcher-base') const FixedQueue = require('./fixed-queue') -const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch, kStats } = require('../core/symbols') +const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = require('../core/symbols') const kClients = Symbol('clients') const kNeedDrain = Symbol('needDrain') @@ -66,8 +66,6 @@ class PoolBase extends DispatcherBase { this[kOnConnectionError] = (origin, targets, err) => { pool.emit('connectionError', origin, [pool, ...targets], err) } - - this[kStats] = new PoolStats(this) } get [kBusy] () { @@ -107,7 +105,7 @@ class PoolBase extends DispatcherBase { } get stats () { - return this[kStats] + return new PoolStats(this) } async [kClose] () { diff --git a/lib/util/stats.js b/lib/util/stats.js index e6bb28dd47f..a13132e4ec8 100644 --- a/lib/util/stats.js +++ b/lib/util/stats.js @@ -9,58 +9,23 @@ const { kQueued } = require('../core/symbols') -const kPool = Symbol('pool') -const kClient = Symbol('client') - class ClientStats { constructor (client) { - this[kClient] = client - } - - get connected () { - return this[kClient][kConnected] - } - - get pending () { - return this[kClient][kPending] - } - - get running () { - return this[kClient][kRunning] - } - - get size () { - return this[kClient][kSize] + this.connected = client[kConnected] + this.pending = client[kPending] + this.running = client[kRunning] + this.size = client[kSize] } } class PoolStats { constructor (pool) { - this[kPool] = pool - } - - get connected () { - return this[kPool][kConnected] - } - - get free () { - return this[kPool][kFree] - } - - get pending () { - return this[kPool][kPending] - } - - get queued () { - return this[kPool][kQueued] - } - - get running () { - return this[kPool][kRunning] - } - - get size () { - return this[kPool][kSize] + this.connected = pool[kConnected] + this.free = pool[kFree] + this.pending = pool[kPending] + this.queued = pool[kQueued] + this.running = pool[kRunning] + this.size = pool[kSize] } }