Skip to content

Commit 8392918

Browse files
Add connection lifetime limit option and tests (#2698)
Co-authored-by: ChrisG0x20 <[email protected]>
1 parent 5508c0e commit 8392918

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

packages/pg-pool/index.js

+27
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class Pool extends EventEmitter {
8484
this.options.max = this.options.max || this.options.poolSize || 10
8585
this.options.maxUses = this.options.maxUses || Infinity
8686
this.options.allowExitOnIdle = this.options.allowExitOnIdle || false
87+
this.options.maxLifetimeSeconds = this.options.maxLifetimeSeconds || 0
8788
this.log = this.options.log || function () {}
8889
this.Client = this.options.Client || Client || require('pg').Client
8990
this.Promise = this.options.Promise || global.Promise
@@ -94,6 +95,7 @@ class Pool extends EventEmitter {
9495

9596
this._clients = []
9697
this._idle = []
98+
this._expired = new WeakSet()
9799
this._pendingQueue = []
98100
this._endCallback = undefined
99101
this.ending = false
@@ -123,6 +125,7 @@ class Pool extends EventEmitter {
123125
}
124126
return
125127
}
128+
126129
// if we don't have any waiting, do nothing
127130
if (!this._pendingQueue.length) {
128131
this.log('no queued requests')
@@ -248,6 +251,17 @@ class Pool extends EventEmitter {
248251
} else {
249252
this.log('new client connected')
250253

254+
if (this.options.maxLifetimeSeconds !== 0) {
255+
setTimeout(() => {
256+
this.log('ending client due to expired lifetime')
257+
this._expired.add(client)
258+
const idleIndex = this._idle.findIndex(idleItem => idleItem.client === client)
259+
if (idleIndex !== -1) {
260+
this._acquireClient(client, new PendingItem((err, client, clientRelease) => clientRelease()), idleListener, false)
261+
}
262+
}, this.options.maxLifetimeSeconds * 1000)
263+
}
264+
251265
return this._acquireClient(client, pendingItem, idleListener, true)
252266
}
253267
})
@@ -318,6 +332,15 @@ class Pool extends EventEmitter {
318332
return
319333
}
320334

335+
const isExpired = this._expired.has(client)
336+
if (isExpired) {
337+
this.log('remove expired client')
338+
this._expired.delete(client)
339+
this._remove(client)
340+
this._pulseQueue()
341+
return
342+
}
343+
321344
// idle timeout
322345
let tid
323346
if (this.options.idleTimeoutMillis) {
@@ -414,6 +437,10 @@ class Pool extends EventEmitter {
414437
return this._idle.length
415438
}
416439

440+
get expiredCount() {
441+
return this._clients.reduce((acc, client) => acc + (this._expired.has(client) ? 1 : 0), 0)
442+
}
443+
417444
get totalCount() {
418445
return this._clients.length
419446
}
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use strict'
2+
const co = require('co')
3+
const expect = require('expect.js')
4+
5+
const describe = require('mocha').describe
6+
const it = require('mocha').it
7+
const path = require('path')
8+
9+
const Pool = require('../')
10+
11+
describe('lifetime timeout', () => {
12+
it('connection lifetime should expire and remove the client', (done) => {
13+
const pool = new Pool({ maxLifetimeSeconds: 1 })
14+
pool.query('SELECT NOW()')
15+
pool.on('remove', () => {
16+
console.log('expired while idle - on-remove event')
17+
expect(pool.expiredCount).to.equal(0)
18+
expect(pool.totalCount).to.equal(0)
19+
done()
20+
})
21+
})
22+
it('connection lifetime should expire and remove the client after the client is done working', (done) => {
23+
const pool = new Pool({ maxLifetimeSeconds: 1 })
24+
pool.query('SELECT pg_sleep(1.01)')
25+
pool.on('remove', () => {
26+
console.log('expired while busy - on-remove event')
27+
expect(pool.expiredCount).to.equal(0)
28+
expect(pool.totalCount).to.equal(0)
29+
done()
30+
})
31+
})
32+
it('can remove expired clients and recreate them',
33+
co.wrap(function* () {
34+
const pool = new Pool({ maxLifetimeSeconds: 1 })
35+
let query = pool.query('SELECT pg_sleep(1)')
36+
expect(pool.expiredCount).to.equal(0)
37+
expect(pool.totalCount).to.equal(1)
38+
yield query
39+
expect(pool.expiredCount).to.equal(0)
40+
expect(pool.totalCount).to.equal(0)
41+
yield pool.query('SELECT NOW()')
42+
expect(pool.expiredCount).to.equal(0)
43+
expect(pool.totalCount).to.equal(1)
44+
})
45+
)
46+
})

0 commit comments

Comments
 (0)