diff --git a/README.md b/README.md index a02fa32..75508d9 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ var pool2 = new Pool({ min: 4, // set min pool size to 4 idleTimeoutMillis: 1000, // close idle clients after 1 second connectionTimeoutMillis: 1000, // return an error after 1 second if connection could not be established + maxCheckoutMillis: 1000 // receive a maxCheckoutExceeded event after 1 second }) //you can supply a custom client constructor @@ -79,7 +80,7 @@ const pool = new Pool(config); ssl: true } */ -``` +``` ### acquire clients with a promise @@ -296,6 +297,30 @@ setTimeout(function () { ``` +#### maxCheckoutExceeded + +Fired whenever a client is checked out longer then `maxCheckoutMillis` + +Example: + +This allows you to count the number of clients which have exceeded the checkout timeout. + +```js +var Pool = require('pg-pool') +var pool = new Pool({ maxCheckoutMillis : 1000 }) + +let checkoutExceededCount = 0 + +pool.on('maxCheckoutExceeded', function (client) { + checkoutExceededCount++; +}) + +setTimeout(function () { + console.log('checkout exceeded count:', checkoutExceededCount) // output: checkout exceeded count: 1 +}, 1500) + +``` + ### environment variables pg-pool & node-postgres support some of the same environment variables as `psql` supports. The most common are: diff --git a/index.js b/index.js index fe921cb..eb140a4 100644 --- a/index.js +++ b/index.js @@ -125,7 +125,8 @@ class Pool extends EventEmitter { const client = idleItem.client client.release = release.bind(this, client) this.emit('acquire', client) - return waiter.callback(undefined, client, client.release) + + return this._callCallback(client, waiter.callback) } if (!this._isFull()) { return this.newClient(waiter) @@ -148,6 +149,20 @@ class Pool extends EventEmitter { this.emit('remove', client) } + _callCallback (client, callback) { + if (!this.options.maxCheckoutMillis) { + return callback(undefined, client, client.release) + } + + const tid = setTimeout(() => { + this.emit('maxCheckoutExceeded', client) + }, this.options.maxCheckoutMillis) + return callback(undefined, client, (err) => { + clearTimeout(tid) + client.release(err) + }) + } + connect (cb) { if (this.ending) { const err = new Error('Cannot use a pool after calling end on the pool') @@ -250,9 +265,11 @@ class Pool extends EventEmitter { this.emit('acquire', client) if (!pendingItem.timedOut) { if (this.options.verify) { - this.options.verify(client, pendingItem.callback) + this._callCallback(client, (_, client) => { + this.options.verify(client, pendingItem.callback) + }) } else { - pendingItem.callback(undefined, client, client.release) + this._callCallback(client, pendingItem.callback) } } else { if (this.options.verify) { diff --git a/test/connection-timeout.js b/test/connection-timeout.js index 2c4d68f..4f4b778 100644 --- a/test/connection-timeout.js +++ b/test/connection-timeout.js @@ -218,4 +218,63 @@ describe('connection timeout', () => { }) }) }) + + it('emits a maxCheckoutExceeded event on new connections', (done) => { + const pool = new Pool({ + maxCheckoutMillis: 1000, + max: 1 + }) + + let maxCheckoutExceededCount = 0 + + pool.on('maxCheckoutExceeded', () => { + maxCheckoutExceededCount++ + }) + + // First connect with a slow checkout + pool.connect((err, client, release) => { + expect(err).to.be(undefined) + + client.query('select pg_sleep(2)', (err) => { + expect(err).not.to.be.ok() + + release() + expect(maxCheckoutExceededCount).to.be(1) + pool.end(done) + }) + }) + }) + + it('emits a maxCheckoutExceeded event on queued connections', (done) => { + const pool = new Pool({ + maxCheckoutMillis: 1000, + max: 1 + }) + + let maxCheckoutExceededCount = 0 + + pool.on('maxCheckoutExceeded', () => { + maxCheckoutExceededCount++ + }) + + // First connect with a fast checkout + pool.connect((err, client, release) => { + expect(err).to.be(undefined) + + // Slow checkout queued afterwards + pool.connect((err, client, release) => { + expect(err).to.be(undefined) + + client.query('select pg_sleep(2)', (err) => { + expect(err).not.to.be.ok() + + release() + expect(maxCheckoutExceededCount).to.be(1) + pool.end(done) + }) + }) + + release() + }) + }) })