Skip to content

Commit c0d3905

Browse files
committed
Initial commit
0 parents  commit c0d3905

File tree

5 files changed

+272
-0
lines changed

5 files changed

+272
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

index.js

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
var genericPool = require('generic-pool')
2+
var util = require('util')
3+
var EventEmitter = require('events').EventEmitter
4+
var debug = require('debug')
5+
6+
var Pool = module.exports = function(options) {
7+
EventEmitter.call(this)
8+
this.options = options || {}
9+
this.log = this.options.log || debug('pg:pool')
10+
this.Client = this.options.Client || require('pg').Client
11+
this.Promise = this.options.Promise || Promise
12+
13+
this.options.max = this.options.max || this.options.poolSize || 10
14+
this.options.create = this.options.create || this._create.bind(this)
15+
this.options.destroy = this.options.destroy || this._destroy.bind(this)
16+
this.pool = new genericPool.Pool(this.options)
17+
}
18+
19+
util.inherits(Pool, EventEmitter)
20+
21+
Pool.prototype._destroy = function(client) {
22+
if (client._destroying) return
23+
client._destroying = true
24+
client.end()
25+
}
26+
27+
Pool.prototype._create = function(cb) {
28+
this.log('connecting new client')
29+
var client = new this.Client(this.options)
30+
31+
client.on('error', function(e) {
32+
this.log('connected client error:', e)
33+
this.pool.destroy(client)
34+
e.client = client
35+
this.emit('error', e)
36+
}.bind(this))
37+
38+
client.connect(function(err) {
39+
this.log('client connected')
40+
if (err) {
41+
this.log('client connection error:', e)
42+
cb(err)
43+
}
44+
45+
client.queryAsync = function(text, values) {
46+
return new this.Promise((resolve, reject) => {
47+
client.query(text, values, function(err, res) {
48+
err ? reject(err) : resolve(res)
49+
})
50+
})
51+
}.bind(this)
52+
53+
cb(err, err ? null : client)
54+
}.bind(this))
55+
}
56+
57+
Pool.prototype.connect = function(cb) {
58+
return new this.Promise(function(resolve, reject) {
59+
this.log('acquire client begin')
60+
this.pool.acquire(function(err, client) {
61+
if (err) {
62+
this.log('acquire client. error:', err)
63+
if (cb) {
64+
cb(err, null, function() { })
65+
}
66+
return reject(err)
67+
}
68+
69+
this.log('acquire client')
70+
71+
client.release = function(err) {
72+
if (err) {
73+
this.log('release client. error:', err)
74+
this.pool.destroy(client)
75+
}
76+
this.log('release client')
77+
delete client.release
78+
this.pool.release(client)
79+
}.bind(this)
80+
81+
if (cb) {
82+
cb(null, client, client.release)
83+
}
84+
85+
return resolve(client)
86+
}.bind(this))
87+
}.bind(this))
88+
}
89+
90+
Pool.prototype.take = Pool.prototype.connect
91+
92+
Pool.prototype.query = function(text, values) {
93+
return this.take().then(function(client) {
94+
return client.queryAsync(text, values)
95+
.then(function(res) {
96+
client.release()
97+
return res
98+
}).catch(function(error) {
99+
client.release(error)
100+
throw error
101+
})
102+
})
103+
}
104+
105+
Pool.prototype.end = function(cb) {
106+
this.log('draining pool')
107+
return new this.Promise(function(resolve, reject) {
108+
this.pool.drain(function() {
109+
this.log('pool drained, calling destroy all now')
110+
this.pool.destroyAllNow(function() {
111+
if(cb) {
112+
cb()
113+
}
114+
resolve()
115+
})
116+
}.bind(this))
117+
}.bind(this))
118+
}

package.json

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "pg-pool",
3+
"version": "0.0.1",
4+
"description": "Connection pool for node-postgres",
5+
"main": "index.js",
6+
"directories": {
7+
"test": "test"
8+
},
9+
"scripts": {
10+
"test": "node_modules/.bin/mocha"
11+
},
12+
"repository": {
13+
"type": "git",
14+
"url": "git://github.com/brianc/node-pg-pool.git"
15+
},
16+
"keywords": [
17+
"pg",
18+
"postgres",
19+
"pool",
20+
"database"
21+
],
22+
"author": "Brian M. Carlson",
23+
"license": "MIT",
24+
"bugs": {
25+
"url": "https://github.com/brianc/node-pg-pool/issues"
26+
},
27+
"homepage": "https://github.com/brianc/node-pg-pool#readme",
28+
"devDependencies": {
29+
"bluebird": "3.4.0",
30+
"co": "4.6.0",
31+
"expect.js": "0.3.1",
32+
"lodash": "4.13.1",
33+
"mocha": "^2.3.3",
34+
"pg": "4.5.6"
35+
},
36+
"dependencies": {
37+
"debug": "^2.2.0",
38+
"generic-pool": "2.4.2"
39+
}
40+
}

test/index.js

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
var expect = require('expect.js')
2+
var Client = require('pg').Client
3+
var co = require('co')
4+
var Promise = require('bluebird')
5+
var _ = require('lodash')
6+
7+
var Pool = require('../')
8+
9+
describe('pool', function() {
10+
11+
describe('with callbacks', function() {
12+
it('works totally unconfigured', function(done) {
13+
const pool = new Pool()
14+
pool.connect(function(err, client, release) {
15+
if (err) return done(err)
16+
client.query('SELECT NOW()', function(err, res) {
17+
release()
18+
if (err) return done(err)
19+
expect(res.rows).to.have.length(1)
20+
pool.end(done)
21+
})
22+
})
23+
})
24+
25+
it('passes props to clients', function(done) {
26+
const pool = new Pool({ binary: true })
27+
pool.connect(function(err, client, release) {
28+
release()
29+
expect(client.binary).to.eql(true)
30+
pool.end(done)
31+
})
32+
})
33+
34+
it('removes client if it errors in background', function(done) {
35+
const pool = new Pool()
36+
pool.connect(function(err, client, release) {
37+
release()
38+
client.testString = 'foo'
39+
setTimeout(function() {
40+
client.emit('error', new Error('on purpose'))
41+
}, 10)
42+
})
43+
pool.on('error', function(err) {
44+
expect(err.message).to.be('on purpose')
45+
expect(err.client).to.not.be(undefined)
46+
expect(err.client.testString).to.be('foo')
47+
err.client.connection.stream.on('end', function() {
48+
pool.end(done)
49+
})
50+
})
51+
})
52+
})
53+
54+
describe('with promises', function() {
55+
it('connects and disconnects', co.wrap(function*() {
56+
var pool = new Pool()
57+
var client = yield pool.connect()
58+
expect(pool.pool.availableObjectsCount()).to.be(0)
59+
var res = yield client.queryAsync('select $1::text as name', ['hi'])
60+
expect(res.rows).to.eql([{ name: 'hi' }])
61+
client.release()
62+
expect(pool.pool.getPoolSize()).to.be(1)
63+
expect(pool.pool.availableObjectsCount()).to.be(1)
64+
return yield pool.end()
65+
}))
66+
67+
it('properly pools clients', co.wrap(function*() {
68+
var pool = new Pool({ poolSize: 9 })
69+
var count = 0
70+
while (count < 30) {
71+
count++
72+
pool.connect().then(function(client) {
73+
client.queryAsync('select $1::text as name', ['hi']).then(function(res) {
74+
client.release()
75+
})
76+
})
77+
}
78+
yield Promise.delay(100)
79+
expect(pool.pool.getPoolSize()).to.be(9)
80+
return yield pool.end()
81+
}))
82+
83+
it('supports just running queries', co.wrap(function*() {
84+
var pool = new Pool({ poolSize: 9 })
85+
var count = 0
86+
var queries = _.times(30).map(function() {
87+
return pool.query('SELECT $1::text as name', ['hi'])
88+
})
89+
console.log('executing')
90+
yield queries
91+
expect(pool.pool.getPoolSize()).to.be(9)
92+
expect(pool.pool.availableObjectsCount()).to.be(9)
93+
return yield pool.end()
94+
}))
95+
96+
it('recovers from all errors', co.wrap(function*() {
97+
var pool = new Pool({ poolSize: 9 })
98+
var count = 0
99+
100+
while(count++ < 30) {
101+
try {
102+
yield pool.query('SELECT lksjdfd')
103+
} catch(e) {
104+
}
105+
}
106+
var res = yield pool.query('SELECT $1::text as name', ['hi'])
107+
expect(res.rows).to.eql([{ name: 'hi' }])
108+
return yield pool.end()
109+
}))
110+
})
111+
})

test/mocha.opts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
--no-exit
2+
--bail

0 commit comments

Comments
 (0)