Skip to content

Commit

Permalink
feat: expose pool aggregate statistics (nodejs#1255)
Browse files Browse the repository at this point in the history
  • Loading branch information
mnutt authored and KhafraDev committed Jun 23, 2022
1 parent a494b8a commit b300407
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 4 deletions.
4 changes: 4 additions & 0 deletions docs/api/BalancedPool.md
Expand Up @@ -34,6 +34,10 @@ Implements [Client.closed](Client.md#clientclosed)

Implements [Client.destroyed](Client.md#clientdestroyed)

### `Pool.stats`

Returns [`PoolStats`](PoolStats.md) instance for this pool.

## Instance Methods

### `BalancedPool.addUpstream(upstream)`
Expand Down
4 changes: 4 additions & 0 deletions docs/api/Pool.md
Expand Up @@ -30,6 +30,10 @@ Implements [Client.closed](Client.md#clientclosed)

Implements [Client.destroyed](Client.md#clientdestroyed)

### `Pool.stats`

Returns [`PoolStats`](PoolStats.md) instance for this pool.

## Instance Methods

### `Pool.close([callback])`
Expand Down
35 changes: 35 additions & 0 deletions docs/api/PoolStats.md
@@ -0,0 +1,35 @@
# Class: PoolStats

Aggregate stats for a [Pool](Pool.md) or [BalancedPool](BalancedPool.md).

## `new PoolStats(pool)`

Arguments:

* **pool** `Pool` - Pool or BalancedPool from which to return stats.

## Instance Properties

### `PoolStats.connected`

Number of open socket connections in this pool.

### `PoolStats.free`

Number of open socket connections in this pool that do not have an active request.

### `PoolStats.pending`

Number of pending requests across all clients in this pool.

### `PoolStats.queued`

Number of queued requests across all clients in this pool.

### `PoolStats.running`

Number of currently active requests across all clients in this pool.

### `PoolStats.size`

Number of active, pending, or queued requests across all clients in this pool.
2 changes: 2 additions & 0 deletions lib/core/symbols.js
Expand Up @@ -22,6 +22,8 @@ module.exports = {
kPending: Symbol('pending'),
kSize: Symbol('size'),
kBusy: Symbol('busy'),
kQueued: Symbol('queued'),
kFree: Symbol('free'),
kConnected: Symbol('connected'),
kClosed: Symbol('closed'),
kNeedDrain: Symbol('need drain'),
Expand Down
19 changes: 17 additions & 2 deletions lib/pool-base.js
Expand Up @@ -7,7 +7,8 @@ const {
InvalidArgumentError
} = require('./core/errors')
const FixedQueue = require('./node/fixed-queue')
const { kSize, kRunning, kPending, kBusy, kUrl } = require('./core/symbols')
const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl } = require('./core/symbols')
const PoolStats = require('./pool-stats')

const kClients = Symbol('clients')
const kNeedDrain = Symbol('needDrain')
Expand All @@ -19,10 +20,10 @@ const kOnDrain = Symbol('onDrain')
const kOnConnect = Symbol('onConnect')
const kOnDisconnect = Symbol('onDisconnect')
const kOnConnectionError = Symbol('onConnectionError')
const kQueued = Symbol('queued')
const kGetDispatcher = Symbol('get dispatcher')
const kAddClient = Symbol('add client')
const kRemoveClient = Symbol('remove client')
const kStats = Symbol('stats')

class PoolBase extends Dispatcher {
constructor () {
Expand Down Expand Up @@ -77,12 +78,22 @@ class PoolBase extends Dispatcher {
this[kOnConnectionError] = (origin, targets, err) => {
pool.emit('connectionError', origin, [pool, ...targets], err)
}

this[kStats] = new PoolStats(this)
}

get [kBusy] () {
return this[kNeedDrain]
}

get [kConnected] () {
return this[kClients].filter(client => client[kConnected]).length
}

get [kFree] () {
return this[kClients].filter(client => client[kConnected] && !client[kNeedDrain]).length
}

get [kPending] () {
let ret = this[kQueued]
for (const { [kPending]: pending } of this[kClients]) {
Expand All @@ -107,6 +118,10 @@ class PoolBase extends Dispatcher {
return ret
}

get stats() {
return this[kStats];
}

get destroyed () {
return this[kDestroyed]
}
Expand Down
34 changes: 34 additions & 0 deletions lib/pool-stats.js
@@ -0,0 +1,34 @@
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
13 changes: 11 additions & 2 deletions test/pool.js
Expand Up @@ -343,7 +343,7 @@ test('backpressure algorithm', (t) => {
})

test('busy', (t) => {
t.plan(8 * 10 + 2 + 1)
t.plan(8 * 16 + 2 + 1)

const server = createServer((req, res) => {
t.equal('/', req.url)
Expand All @@ -353,9 +353,11 @@ test('busy', (t) => {
})
t.teardown(server.close.bind(server))

const connections = 2;

server.listen(0, async () => {
const client = new Pool(`http://localhost:${server.address().port}`, {
connections: 2,
connections,
pipelining: 2
})
client.on('drain', () => {
Expand Down Expand Up @@ -383,6 +385,13 @@ test('busy', (t) => {
t.equal(client[kBusy], n > 1)
t.equal(client[kSize], n)
t.equal(client[kRunning], 0)

t.equal(client.stats.connected, 0)
t.equal(client.stats.free, 0)
t.equal(client.stats.queued, Math.max(n - connections, 0))
t.equal(client.stats.pending, n)
t.equal(client.stats.size, n)
t.equal(client.stats.running, 0)
}
})
})
Expand Down

0 comments on commit b300407

Please sign in to comment.