Brian Crowell 684cd09bce
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.
2021-07-27 11:29:07 -05:00

119 lines
3.5 KiB
JavaScript

'use strict'
const co = require('co')
const expect = require('expect.js')
const describe = require('mocha').describe
const it = require('mocha').it
const { fork } = require('child_process')
const path = require('path')
const Pool = require('../')
const wait = (time) => new Promise((resolve) => setTimeout(resolve, time))
describe('idle timeout', () => {
it('should timeout and remove the client', (done) => {
const pool = new Pool({ idleTimeoutMillis: 10 })
pool.query('SELECT NOW()')
pool.on('remove', () => {
expect(pool.idleCount).to.equal(0)
expect(pool.totalCount).to.equal(0)
done()
})
})
it(
'times out and removes clients when others are also removed',
co.wrap(function* () {
const pool = new Pool({ idleTimeoutMillis: 10 })
const clientA = yield pool.connect()
const clientB = yield pool.connect()
clientA.release()
clientB.release(new Error())
const removal = new Promise((resolve) => {
pool.on('remove', () => {
expect(pool.idleCount).to.equal(0)
expect(pool.totalCount).to.equal(0)
resolve()
})
})
const timeout = wait(100).then(() => Promise.reject(new Error('Idle timeout failed to occur')))
try {
yield Promise.race([removal, timeout])
} finally {
pool.end()
}
})
)
it(
'can remove idle clients and recreate them',
co.wrap(function* () {
const pool = new Pool({ idleTimeoutMillis: 1 })
const results = []
for (var i = 0; i < 20; i++) {
let query = pool.query('SELECT NOW()')
expect(pool.idleCount).to.equal(0)
expect(pool.totalCount).to.equal(1)
results.push(yield query)
yield wait(2)
expect(pool.idleCount).to.equal(0)
expect(pool.totalCount).to.equal(0)
}
expect(results).to.have.length(20)
})
)
it(
'does not time out clients which are used',
co.wrap(function* () {
const pool = new Pool({ idleTimeoutMillis: 1 })
const results = []
for (var i = 0; i < 20; i++) {
let client = yield pool.connect()
expect(pool.totalCount).to.equal(1)
expect(pool.idleCount).to.equal(0)
yield wait(10)
results.push(yield client.query('SELECT NOW()'))
client.release()
expect(pool.idleCount).to.equal(1)
expect(pool.totalCount).to.equal(1)
}
expect(results).to.have.length(20)
return pool.end()
})
)
it('unrefs the connections and timeouts so the program can exit when idle when the allowExitOnIdle option is set', function (done) {
const child = fork(path.join(__dirname, 'idle-timeout-exit.js'), [], {
silent: true,
env: { ...process.env, ALLOW_EXIT_ON_IDLE: '1' },
})
let result = ''
child.stdout.setEncoding('utf8')
child.stdout.on('data', (chunk) => (result += chunk))
child.on('error', (err) => done(err))
child.on('close', () => {
expect(result).to.equal('completed first\ncompleted second\n')
done()
})
})
it('keeps old behavior when allowExitOnIdle option is not set', function (done) {
const child = fork(path.join(__dirname, 'idle-timeout-exit.js'), [], {
silent: true,
})
let result = ''
child.stdout.setEncoding('utf8')
child.stdout.on('data', (chunk) => (result += chunk))
child.on('error', (err) => done(err))
child.on('close', () => {
expect(result).to.equal('completed first\ncompleted second\nremoved\n')
done()
})
})
})