initial work on new pool

This commit is contained in:
bmc 2013-02-15 16:13:28 -06:00
parent d046ffc921
commit 971eb5d1ef
2 changed files with 181 additions and 0 deletions

78
lib/pool.js Normal file
View File

@ -0,0 +1,78 @@
var defaults = require(__dirname + '/defaults');
var genericPool = require('generic-pool');
//takes the same config structure as client
var createPool = function(config) {
config = config || {};
var name = JSON.stringify(config);
var pool = createPool.all[name];
if(pool) {
return pool;
}
pool = genericPool.Pool({
name: name,
max: defaults.poolSize,
create: function(cb) {
var client = new createPool.Client(config);
client.connect(function(err) {
return cb(err, client);
});
},
destroy: function(client) {
client.end();
}
});
createPool.all[name] = pool;
pool.connect = function(cb) {
pool.acquire(function(err, client) {
//TODO: on connection error should we remove this client automatically?
if(err) {
return cb(err);
}
if(cb.length > 2) {
return newConnect(pool, client, cb);
}
return oldConnect(pool, client, cb);
});
};
return pool;
}
//the old connect method of the pool
//would automatically subscribe to the 'drain'
//event and automatically return the client to
//the pool once 'drain' fired once. This caused
//a bunch of problems, but for backwards compatibility
//we're leaving it in
var alarmDuration = 1000;
var errorMessage = ['A client has been checked out from the pool for longer than ' + alarmDuration + ' ms.',
'You might have a leak!',
'You should use the following new way to check out clients','pg.connect(function(err, client, done)) {',
' //do something',
' done(); //call done() to signal you are finished with the client',
'}'].join(require('os').EOL);
var oldConnect = function(pool, client, cb) {
var tid = setTimeout(function() {
console.error(errorMessage);
}, alarmDuration);
var release = function() {
clearTimeout(tid);
pool.release(client);
};
client.once('drain', release);
cb(null, client);
};
var newConnect = function(pool, client, cb) {
cb(null, client, function() {
pool.release(client);
});
};
//list of all created pools
createPool.all = {};
//reference to client constructor
createPool.Client = require(__dirname + '/client');
module.exports = createPool;

View File

@ -0,0 +1,103 @@
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var libDir = __dirname + '/../../../lib';
var defaults = require(libDir + '/defaults');
var pool = require(libDir + '/pool');
var poolId = 0;
require(__dirname + '/../../test-helper');
var FakeClient = function() {
EventEmitter.call(this);
}
util.inherits(FakeClient, EventEmitter);
FakeClient.prototype.connect = function(cb) {
process.nextTick(cb);
}
FakeClient.prototype.end = function() {
}
//Hangs the event loop until 'end' is called on client
var HangingClient = function(config) {
EventEmitter.call(this);
this.config = config;
}
util.inherits(HangingClient, EventEmitter);
HangingClient.prototype.connect = function(cb) {
this.intervalId = setInterval(function() {
console.log('hung client...');
}, 1000);
process.nextTick(cb);
}
HangingClient.prototype.end = function() {
clearInterval(this.intervalId);
}
pool.Client = FakeClient;
test('no pools exist', function() {
assert.empty(Object.keys(pool.all));
});
test('pool creates pool on miss', function() {
var p = pool();
assert.ok(p);
assert.equal(Object.keys(pool.all).length, 1);
var p2 = pool();
assert.equal(p, p2);
assert.equal(Object.keys(pool.all).length, 1);
var p3 = pool("pg://postgres:password@localhost:5432/postgres");
assert.notEqual(p, p3);
assert.equal(Object.keys(pool.all).length, 2);
});
test('pool follows default limits', function() {
var p = pool(poolId++);
for(var i = 0; i < 100; i++) {
p.acquire(function(err, client) {
});
}
assert.equal(p.getPoolSize(), defaults.poolSize);
});
test('pool#connect with 2 parameters (legacy, for backwards compat)', function() {
var p = pool(poolId++);
p.connect(assert.success(function(client) {
assert.ok(client);
assert.equal(p.availableObjectsCount(), 0);
assert.equal(p.getPoolSize(), 1);
client.emit('drain');
assert.equal(p.availableObjectsCount(), 1);
assert.equal(p.getPoolSize(), 1);
p.destroyAllNow();
}));
});
test('pool#connect with 3 parameters', function() {
var p = pool(poolId++);
var tid = setTimeout(function() {
throw new Error("Connection callback was never called");
}, 100);
p.connect(function(err, client, done) {
clearTimeout(tid);
assert.equal(err, null);
assert.ok(client);
assert.equal(p.availableObjectsCount(), 0);
assert.equal(p.getPoolSize(), 1);
client.emit('drain');
assert.equal(p.availableObjectsCount(), 0);
assert.equal(p.getPoolSize(), 1);
done();
assert.equal(p.availableObjectsCount(), 1);
assert.equal(p.getPoolSize(), 1);
p.destroyAllNow();
});
});