node-postgres/index.js
Charmander fd802a385c Don’t create promises when callbacks are provided (#31)
* Revert "When connection fail, emit the error. (#28)"

This reverts commit 6a7edabc22e36db7386c97ee93f08f957364f37d.

The callback passed to `Pool.prototype.connect` should be responsible for handling connection errors. The `error` event is documented to be:

> Emitted whenever an idle client in the pool encounters an error.

This isn’t the case of an idle client in the pool; it never makes it into the pool.

It also breaks tests on pg’s master because of nonspecific dependencies.

* Don’t create promises when callbacks are provided

It’s incorrect to do so. One consequence is that a rejected promise will be unhandled, which is currently annoying, but also dangerous in the future:

> DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

The way callbacks are used currently also causes #24 (hiding of errors thrown synchronously from the callback). One fix for that would be to call them asynchronously from inside the `new Promise()` executor:

    process.nextTick(cb, error);

I don’t think it’s worth implementing, though, since it would still be backwards-incompatible – just less obvious about it.

Also fixes a bug where the `Pool.prototype.connect` callback would be called twice if there was an error.

* Use Node-0.10-compatible `process.nextTick`
2016-12-04 17:20:24 -06:00

147 lines
3.6 KiB
JavaScript

var genericPool = require('generic-pool')
var util = require('util')
var EventEmitter = require('events').EventEmitter
var objectAssign = require('object-assign')
var Pool = module.exports = function (options, Client) {
if (!(this instanceof Pool)) {
return new Pool(options, Client)
}
EventEmitter.call(this)
this.options = objectAssign({}, options)
this.log = this.options.log || function () { }
this.Client = this.options.Client || Client || require('pg').Client
this.Promise = this.options.Promise || Promise
this.options.max = this.options.max || this.options.poolSize || 10
this.options.create = this.options.create || this._create.bind(this)
this.options.destroy = this.options.destroy || this._destroy.bind(this)
this.pool = new genericPool.Pool(this.options)
this.onCreate = this.options.onCreate
}
util.inherits(Pool, EventEmitter)
Pool.prototype._promise = function (cb, executor) {
if (!cb) {
return new this.Promise(executor)
}
function resolved (value) {
process.nextTick(function () {
cb(null, value)
})
}
function rejected (error) {
process.nextTick(function () {
cb(error)
})
}
executor(resolved, rejected)
}
Pool.prototype._promiseNoCallback = function (callback, executor) {
return callback
? executor()
: new this.Promise(executor)
}
Pool.prototype._destroy = function (client) {
if (client._destroying) return
client._destroying = true
client.end()
}
Pool.prototype._create = function (cb) {
this.log('connecting new client')
var client = new this.Client(this.options)
client.on('error', function (e) {
this.log('connected client error:', e)
this.pool.destroy(client)
e.client = client
this.emit('error', e)
}.bind(this))
client.connect(function (err) {
if (err) {
this.log('client connection error:', err)
cb(err)
} else {
this.log('client connected')
this.emit('connect', client)
cb(null, client)
}
}.bind(this))
}
Pool.prototype.connect = function (cb) {
return this._promiseNoCallback(cb, function (resolve, reject) {
this.log('acquire client begin')
this.pool.acquire(function (err, client) {
if (err) {
this.log('acquire client. error:', err)
if (cb) {
cb(err, null, function () {})
} else {
reject(err)
}
return
}
this.log('acquire client')
this.emit('acquire', client)
client.release = function (err) {
delete client.release
if (err) {
this.log('destroy client. error:', err)
this.pool.destroy(client)
} else {
this.log('release client')
this.pool.release(client)
}
}.bind(this)
if (cb) {
cb(null, client, client.release)
} else {
resolve(client)
}
}.bind(this))
}.bind(this))
}
Pool.prototype.take = Pool.prototype.connect
Pool.prototype.query = function (text, values, cb) {
if (typeof values === 'function') {
cb = values
values = undefined
}
return this._promise(cb, function (resolve, reject) {
this.connect(function (err, client, done) {
if (err) {
return reject(err)
}
client.query(text, values, function (err, res) {
done(err)
err ? reject(err) : resolve(res)
})
})
}.bind(this))
}
Pool.prototype.end = function (cb) {
this.log('draining pool')
return this._promise(cb, function (resolve, reject) {
this.pool.drain(function () {
this.log('pool drained, calling destroy all now')
this.pool.destroyAllNow(resolve)
}.bind(this))
}.bind(this))
}