Add connection lifetime limit option and tests (#2698)

Co-authored-by: ChrisG0x20 <position0x45@hotmail.com>
This commit is contained in:
ChrisWritable 2022-01-28 15:17:48 -08:00 committed by GitHub
parent 5508c0ee6b
commit 8392918d7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 73 additions and 0 deletions

View File

@ -84,6 +84,7 @@ class Pool extends EventEmitter {
this.options.max = this.options.max || this.options.poolSize || 10
this.options.maxUses = this.options.maxUses || Infinity
this.options.allowExitOnIdle = this.options.allowExitOnIdle || false
this.options.maxLifetimeSeconds = this.options.maxLifetimeSeconds || 0
this.log = this.options.log || function () {}
this.Client = this.options.Client || Client || require('pg').Client
this.Promise = this.options.Promise || global.Promise
@ -94,6 +95,7 @@ class Pool extends EventEmitter {
this._clients = []
this._idle = []
this._expired = new WeakSet()
this._pendingQueue = []
this._endCallback = undefined
this.ending = false
@ -123,6 +125,7 @@ class Pool extends EventEmitter {
}
return
}
// if we don't have any waiting, do nothing
if (!this._pendingQueue.length) {
this.log('no queued requests')
@ -248,6 +251,17 @@ class Pool extends EventEmitter {
} else {
this.log('new client connected')
if (this.options.maxLifetimeSeconds !== 0) {
setTimeout(() => {
this.log('ending client due to expired lifetime')
this._expired.add(client)
const idleIndex = this._idle.findIndex(idleItem => idleItem.client === client)
if (idleIndex !== -1) {
this._acquireClient(client, new PendingItem((err, client, clientRelease) => clientRelease()), idleListener, false)
}
}, this.options.maxLifetimeSeconds * 1000)
}
return this._acquireClient(client, pendingItem, idleListener, true)
}
})
@ -318,6 +332,15 @@ class Pool extends EventEmitter {
return
}
const isExpired = this._expired.has(client)
if (isExpired) {
this.log('remove expired client')
this._expired.delete(client)
this._remove(client)
this._pulseQueue()
return
}
// idle timeout
let tid
if (this.options.idleTimeoutMillis) {
@ -414,6 +437,10 @@ class Pool extends EventEmitter {
return this._idle.length
}
get expiredCount() {
return this._clients.reduce((acc, client) => acc + (this._expired.has(client) ? 1 : 0), 0)
}
get totalCount() {
return this._clients.length
}

View File

@ -0,0 +1,46 @@
'use strict'
const co = require('co')
const expect = require('expect.js')
const describe = require('mocha').describe
const it = require('mocha').it
const path = require('path')
const Pool = require('../')
describe('lifetime timeout', () => {
it('connection lifetime should expire and remove the client', (done) => {
const pool = new Pool({ maxLifetimeSeconds: 1 })
pool.query('SELECT NOW()')
pool.on('remove', () => {
console.log('expired while idle - on-remove event')
expect(pool.expiredCount).to.equal(0)
expect(pool.totalCount).to.equal(0)
done()
})
})
it('connection lifetime should expire and remove the client after the client is done working', (done) => {
const pool = new Pool({ maxLifetimeSeconds: 1 })
pool.query('SELECT pg_sleep(1.01)')
pool.on('remove', () => {
console.log('expired while busy - on-remove event')
expect(pool.expiredCount).to.equal(0)
expect(pool.totalCount).to.equal(0)
done()
})
})
it('can remove expired clients and recreate them',
co.wrap(function* () {
const pool = new Pool({ maxLifetimeSeconds: 1 })
let query = pool.query('SELECT pg_sleep(1)')
expect(pool.expiredCount).to.equal(0)
expect(pool.totalCount).to.equal(1)
yield query
expect(pool.expiredCount).to.equal(0)
expect(pool.totalCount).to.equal(0)
yield pool.query('SELECT NOW()')
expect(pool.expiredCount).to.equal(0)
expect(pool.totalCount).to.equal(1)
})
)
})