Skip to content

Commit 411869d

Browse files
hjr3asadbek2021
andauthored
pool.end() resolves before the last pool.query() (#3461)
* Pass callback to client.end * Add test for pool.end method * fix: remove excessive _pulseQueue call * fix: context problem * fix: test resolve should be called when the last client is removed * fix: wait for pool.end() Because when you don't pass a callback to .end() it always returns a promise * fix: handle idle timeout test data race --------- Co-authored-by: Asadbek Raimov <asadbekraimov642@gmail.com>
1 parent 26ace0a commit 411869d

File tree

3 files changed

+34
-13
lines changed

3 files changed

+34
-13
lines changed

packages/pg-pool/index.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,22 @@ class Pool extends EventEmitter {
161161
throw new Error('unexpected condition')
162162
}
163163

164-
_remove(client) {
164+
_remove(client, callback) {
165165
const removed = removeWhere(this._idle, (item) => item.client === client)
166166

167167
if (removed !== undefined) {
168168
clearTimeout(removed.timeoutId)
169169
}
170170

171171
this._clients = this._clients.filter((c) => c !== client)
172-
client.end()
173-
this.emit('remove', client)
172+
const context = this
173+
client.end(() => {
174+
context.emit('remove', client)
175+
176+
if (typeof callback === 'function') {
177+
callback()
178+
}
179+
})
174180
}
175181

176182
connect(cb) {
@@ -351,26 +357,23 @@ class Pool extends EventEmitter {
351357
if (client._poolUseCount >= this.options.maxUses) {
352358
this.log('remove expended client')
353359
}
354-
this._remove(client)
355-
this._pulseQueue()
356-
return
360+
361+
return this._remove(client, this._pulseQueue.bind(this))
357362
}
358363

359364
const isExpired = this._expired.has(client)
360365
if (isExpired) {
361366
this.log('remove expired client')
362367
this._expired.delete(client)
363-
this._remove(client)
364-
this._pulseQueue()
365-
return
368+
return this._remove(client, this._pulseQueue.bind(this))
366369
}
367370

368371
// idle timeout
369372
let tid
370373
if (this.options.idleTimeoutMillis && this._isAboveMin()) {
371374
tid = setTimeout(() => {
372375
this.log('remove idle client')
373-
this._remove(client)
376+
this._remove(client, this._pulseQueue.bind(this))
374377
}, this.options.idleTimeoutMillis)
375378

376379
if (this.options.allowExitOnIdle) {

packages/pg-pool/test/ending.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,14 @@ describe('pool ending', () => {
3737
expect(res.rows[0].name).to.equal('brianc')
3838
})
3939
)
40+
41+
it('pool.end() - finish pending queries', async () => {
42+
const pool = new Pool({ max: 20 })
43+
let completed = 0
44+
for (let x = 1; x <= 20; x++) {
45+
pool.query('SELECT $1::text as name', ['brianc']).then(() => completed++)
46+
}
47+
await pool.end()
48+
expect(completed).to.equal(20)
49+
})
4050
})

packages/pg-pool/test/idle-timeout.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,19 @@ describe('idle timeout', () => {
2828
const pool = new Pool({ idleTimeoutMillis: 10 })
2929
const clientA = yield pool.connect()
3030
const clientB = yield pool.connect()
31-
clientA.release()
32-
clientB.release(new Error())
31+
clientA.release() // this will put clientA in the idle pool
32+
clientB.release(new Error()) // an error will cause clientB to be removed immediately
3333

3434
const removal = new Promise((resolve) => {
35-
pool.on('remove', () => {
35+
pool.on('remove', (client) => {
36+
// clientB's stream may take a while to close, so we may get a remove
37+
// event for it
38+
// we only want to handle the remove event for clientA when it times out
39+
// due to being idle
40+
if (client !== clientA) {
41+
return
42+
}
43+
3644
expect(pool.idleCount).to.equal(0)
3745
expect(pool.totalCount).to.equal(0)
3846
resolve()

0 commit comments

Comments
 (0)