Skip to content

Commit

Permalink
Add connection lifetime limit option and tests (#2698)
Browse files Browse the repository at this point in the history
Co-authored-by: ChrisG0x20 <position0x45@hotmail.com>
  • Loading branch information
ChrisWritable and ChrisG0x20 committed Jan 28, 2022
1 parent 5508c0e commit 8392918
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 0 deletions.
27 changes: 27 additions & 0 deletions packages/pg-pool/index.js
Expand Up @@ -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
Expand All @@ -94,6 +95,7 @@ class Pool extends EventEmitter {

this._clients = []
this._idle = []
this._expired = new WeakSet()
this._pendingQueue = []
this._endCallback = undefined
this.ending = false
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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)
}
})
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}
Expand Down
46 changes: 46 additions & 0 deletions packages/pg-pool/test/lifetime-timeout.js
@@ -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)
})
)
})

0 comments on commit 8392918

Please sign in to comment.