Skip to content

Commit 727f1a0

Browse files
briancjohanneswuerbachcharmander
authored
Do not return broken clients to the pool (brianc#2083)
* Prevent requeuing a broken client If a client is not queryable, the pool should prevent requeuing instead of strictly enforcing errors to be propagated back to it. * Write tests for change * Use node 13.6 in travis Some weird behavior changed w/ async iteration in node 13.7...I'm not sure if this was an unintentional break or not but it definitely diverges in behavior from node 12 and earlier versions in node 13...so for now going to run tests on 13.6 to unblock the tests from running while I track this down. * Update packages/pg-pool/test/releasing-clients.js Co-Authored-By: Charmander <~@charmander.me> * Update .travis.yml Co-authored-by: Johannes Würbach <johannes.wuerbach@googlemail.com> Co-authored-by: Charmander <~@charmander.me>
1 parent 3f6760c commit 727f1a0

File tree

3 files changed

+65
-3
lines changed

3 files changed

+65
-3
lines changed

.travis.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ env:
1010
node_js:
1111
- lts/dubnium
1212
- lts/erbium
13-
- 13
13+
# node 13.7 seems to have changed behavior of async iterators exiting early on streams
14+
# if 13.8 still has this problem when it comes down I'll talk to the node team about the change
15+
# in the mean time...peg to 13.6
16+
- 13.6
1417

1518
addons:
1619
postgresql: "10"

packages/pg-pool/index.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ class PendingItem {
2525
}
2626
}
2727

28+
function throwOnDoubleRelease () {
29+
throw new Error('Release called on client which has already been released to the pool.')
30+
}
31+
2832
function promisify (Promise, callback) {
2933
if (callback) {
3034
return { callback: callback, result: undefined }
@@ -244,7 +248,7 @@ class Pool extends EventEmitter {
244248

245249
client.release = (err) => {
246250
if (released) {
247-
throw new Error('Release called on client which has already been released to the pool.')
251+
throwOnDoubleRelease()
248252
}
249253

250254
released = true
@@ -280,7 +284,8 @@ class Pool extends EventEmitter {
280284
_release (client, idleListener, err) {
281285
client.on('error', idleListener)
282286

283-
if (err || this.ending) {
287+
// TODO(bmc): expose a proper, public interface _queryable and _ending
288+
if (err || this.ending || !client._queryable || client._ending) {
284289
this._remove(client)
285290
this._pulseQueue()
286291
return
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
const Pool = require('../')
2+
3+
const expect = require('expect.js')
4+
const net = require('net')
5+
6+
describe('releasing clients', () => {
7+
it('removes a client which cannot be queried', async () => {
8+
// make a pool w/ only 1 client
9+
const pool = new Pool({ max: 1 })
10+
expect(pool.totalCount).to.eql(0)
11+
const client = await pool.connect()
12+
expect(pool.totalCount).to.eql(1)
13+
expect(pool.idleCount).to.eql(0)
14+
// reach into the client and sever its connection
15+
client.connection.end()
16+
17+
// wait for the client to error out
18+
const err = await new Promise((resolve) => client.once('error', resolve))
19+
expect(err).to.be.ok()
20+
expect(pool.totalCount).to.eql(1)
21+
expect(pool.idleCount).to.eql(0)
22+
23+
// try to return it to the pool - this removes it because its broken
24+
client.release()
25+
expect(pool.totalCount).to.eql(0)
26+
expect(pool.idleCount).to.eql(0)
27+
28+
// make sure pool still works
29+
const { rows } = await pool.query('SELECT NOW()')
30+
expect(rows).to.have.length(1)
31+
await pool.end()
32+
})
33+
34+
it('removes a client which is ending', async () => {
35+
// make a pool w/ only 1 client
36+
const pool = new Pool({ max: 1 })
37+
expect(pool.totalCount).to.eql(0)
38+
const client = await pool.connect()
39+
expect(pool.totalCount).to.eql(1)
40+
expect(pool.idleCount).to.eql(0)
41+
// end the client gracefully (but you shouldn't do this with pooled clients)
42+
client.end()
43+
44+
// try to return it to the pool
45+
client.release()
46+
expect(pool.totalCount).to.eql(0)
47+
expect(pool.idleCount).to.eql(0)
48+
49+
// make sure pool still works
50+
const { rows } = await pool.query('SELECT NOW()')
51+
expect(rows).to.have.length(1)
52+
await pool.end()
53+
})
54+
})

0 commit comments

Comments
 (0)