Skip to content

Commit 684cd09

Browse files
author
Brian Crowell
authored
Allow Node to exit if the pool is idle (#2568)
Based on the suggestion from #2078. This adds ref/unref methods to the Connection and Client classes and then uses them to allow the process to exit if all of the connections in the pool are idle. This behavior is controlled by the allowExitOnIdle flag to the Pool constructor; it defaults to the old behavior.
1 parent aedaa59 commit 684cd09

File tree

5 files changed

+74
-0
lines changed

5 files changed

+74
-0
lines changed

packages/pg-pool/index.js

+11
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class Pool extends EventEmitter {
8383

8484
this.options.max = this.options.max || this.options.poolSize || 10
8585
this.options.maxUses = this.options.maxUses || Infinity
86+
this.options.allowExitOnIdle = this.options.allowExitOnIdle || false
8687
this.log = this.options.log || function () {}
8788
this.Client = this.options.Client || Client || require('pg').Client
8889
this.Promise = this.options.Promise || global.Promise
@@ -136,6 +137,7 @@ class Pool extends EventEmitter {
136137
const idleItem = this._idle.pop()
137138
clearTimeout(idleItem.timeoutId)
138139
const client = idleItem.client
140+
client.ref()
139141
const idleListener = idleItem.idleListener
140142

141143
return this._acquireClient(client, pendingItem, idleListener, false)
@@ -323,6 +325,15 @@ class Pool extends EventEmitter {
323325
this.log('remove idle client')
324326
this._remove(client)
325327
}, this.options.idleTimeoutMillis)
328+
329+
if (this.options.allowExitOnIdle) {
330+
// allow Node to exit if this is all that's left
331+
tid.unref()
332+
}
333+
}
334+
335+
if (this.options.allowExitOnIdle) {
336+
client.unref()
326337
}
327338

328339
this._idle.push(new IdleItem(client, idleListener, tid))
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// This test is meant to be spawned from idle-timeout.js
2+
if (module === require.main) {
3+
const allowExitOnIdle = process.env.ALLOW_EXIT_ON_IDLE === '1'
4+
const Pool = require('../index')
5+
6+
const pool = new Pool({ idleTimeoutMillis: 200, ...(allowExitOnIdle ? { allowExitOnIdle: true } : {}) })
7+
pool.query('SELECT NOW()', (err, res) => console.log('completed first'))
8+
pool.on('remove', () => {
9+
console.log('removed')
10+
done()
11+
})
12+
13+
setTimeout(() => {
14+
pool.query('SELECT * from generate_series(0, 1000)', (err, res) => console.log('completed second'))
15+
}, 50)
16+
}

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

+31
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const expect = require('expect.js')
44

55
const describe = require('mocha').describe
66
const it = require('mocha').it
7+
const { fork } = require('child_process')
8+
const path = require('path')
79

810
const Pool = require('../')
911

@@ -84,4 +86,33 @@ describe('idle timeout', () => {
8486
return pool.end()
8587
})
8688
)
89+
90+
it('unrefs the connections and timeouts so the program can exit when idle when the allowExitOnIdle option is set', function (done) {
91+
const child = fork(path.join(__dirname, 'idle-timeout-exit.js'), [], {
92+
silent: true,
93+
env: { ...process.env, ALLOW_EXIT_ON_IDLE: '1' },
94+
})
95+
let result = ''
96+
child.stdout.setEncoding('utf8')
97+
child.stdout.on('data', (chunk) => (result += chunk))
98+
child.on('error', (err) => done(err))
99+
child.on('close', () => {
100+
expect(result).to.equal('completed first\ncompleted second\n')
101+
done()
102+
})
103+
})
104+
105+
it('keeps old behavior when allowExitOnIdle option is not set', function (done) {
106+
const child = fork(path.join(__dirname, 'idle-timeout-exit.js'), [], {
107+
silent: true,
108+
})
109+
let result = ''
110+
child.stdout.setEncoding('utf8')
111+
child.stdout.on('data', (chunk) => (result += chunk))
112+
child.on('error', (err) => done(err))
113+
child.on('close', () => {
114+
expect(result).to.equal('completed first\ncompleted second\nremoved\n')
115+
done()
116+
})
117+
})
87118
})

packages/pg/lib/client.js

+8
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,14 @@ class Client extends EventEmitter {
577577
return result
578578
}
579579

580+
ref() {
581+
this.connection.ref()
582+
}
583+
584+
unref() {
585+
this.connection.unref()
586+
}
587+
580588
end(cb) {
581589
this._ending = true
582590

packages/pg/lib/connection.js

+8
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ class Connection extends EventEmitter {
177177
this._send(syncBuffer)
178178
}
179179

180+
ref() {
181+
this.stream.ref()
182+
}
183+
184+
unref() {
185+
this.stream.unref()
186+
}
187+
180188
end() {
181189
// 0x58 = 'X'
182190
this._ending = true

0 commit comments

Comments
 (0)