From 169c6dc3cec0575d4605b0e25e3bc540a9541a1c Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Thu, 9 Dec 2010 18:10:42 -0600 Subject: [PATCH] Pool object for use in connection pooling --- lib/utils.js | 55 ++++++++++++++++++++++++++++ test/test-helper.js | 2 +- test/unit/utils-tests.js | 77 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 131 insertions(+), 3 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index c43bfa2..94cdd46 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -9,3 +9,58 @@ if(typeof events.EventEmitter.prototype.once !== 'function') { }); }; } +var Pool = function(maxSize, createFn) { + this.maxSize = maxSize; + this.createFn = createFn; + this.items = []; + this.waits = []; +} + +var p = Pool.prototype; + +p.checkOut = function(callback) { + var len = 0; + for(var i = 0, len = this.items.length; i < len; i++) { + var item = this.items[i]; + if(item.checkedIn) { + return this._pulse(item, callback); + } + } + //check if we can create a new item + if(len < this.maxSize && this.createFn) { + var item = {ref: this.createFn()} + this.items.push(item); + return this._pulse(item, callback) + } + this.waits.push(callback); + return false; //did not execute sync +} + +p.checkIn = function(item) { + //scan current items + for(var i = 0, len = this.items.length; i < len; i++) { + var currentItem = this.items[i]; + if(currentItem.ref == item) { + currentItem.checkedIn = true; + return this._pulse(currentItem); + } + } + //add new item + var newItem = {ref: item, checkedIn: true}; + this.items.push(newItem); + return this._pulse(newItem); +} + +p._pulse = function(item, cb) { + cb = cb || this.waits.pop() + if(cb) { + item.checkedIn = false; + cb(null, item.ref) + return true; + } + return false; +} + +module.exports = { + Pool: Pool +} diff --git a/test/test-helper.js b/test/test-helper.js index 209fc0e..9cfb6ca 100644 --- a/test/test-helper.js +++ b/test/test-helper.js @@ -85,7 +85,7 @@ assert.length = function(actual, expectedLength) { var expect = function(callback) { var executed = false; var id = setTimeout(function() { - assert.ok(executed, "Expected execution never fired"); + assert.ok(executed, "Expected execution of " + callback + " fired"); }, 1000) return function(err, queryResult) { diff --git a/test/unit/utils-tests.js b/test/unit/utils-tests.js index 63e233d..dd71244 100644 --- a/test/unit/utils-tests.js +++ b/test/unit/utils-tests.js @@ -1,10 +1,11 @@ require(__dirname + '/test-helper'); +var Pool = require("utils").Pool; //this tests the monkey patching //to ensure comptability with older //versions of node test("EventEmitter.once", function() { - + //an event emitter var stream = new MemoryStream(); @@ -12,8 +13,80 @@ test("EventEmitter.once", function() { stream.once('single', function() { callCount++; }); - + stream.emit('single'); stream.emit('single'); assert.equal(callCount, 1); }); + +test('an empty pool', function() { + test('with no creation method', function() { + var pool = new Pool(10); + var brian = {name:'brian'}; + + test('can set and get an item', function() { + pool.checkIn(brian); + var sync = pool.checkOut(assert.calls(function(err, item) { + assert.equal(brian, item) + assert.same(brian, item) + })) + assert.ok(sync, "should have fired sync") + }) + + test('checkout blocks until item checked back in', function() { + var called = false; + var sync = pool.checkOut(assert.calls(function(err, item) { + called = true; + assert.equal(brian, item) + assert.same(brian, item) + })) + assert.ok(sync === false, "Should not have fired sync") + assert.ok(called === false, "Should not have fired callback yet") + pool.checkIn(brian) + }) + + }) + + test('with a creation method', function() { + var customName = "first"; + var callCount = 0; + var pool = new Pool(3, function() { + return {name: customName + (++callCount)}; + }); + + test('creates if pool is not at max size', function() { + var sync = pool.checkOut(assert.calls(function(err, item) { + assert.equal(item.name, "first1"); + })) + assert.ok(sync, "Should have generated item & called callback in sync") + }) + + test('creates again if item is checked out', function() { + var sync = pool.checkOut(assert.calls(function(err, item) { + assert.equal(item.name, "first2") + })) + assert.ok(sync, "Should have called in sync again") + }) + var external = {name: 'boom'}; + test('can add another item', function() { + pool.checkIn(external) + var sync = pool.checkOut(assert.calls(function(err, item) { + assert.equal(item.name, 'boom') + })) + assert.ok(sync, "Should have fired 3rd in sync") + }) + + test('after pool is full, create is not called again', function() { + var called = false; + var sync = pool.checkOut(assert.calls(function(err, item) { + called = true; + assert.equal(item.name, 'boom') + })) + assert.ok(sync === false, "should not be sync") + assert.ok(called === false, "should not have called callback") + pool.checkIn(external); + }) + }) + +}) +