generic-pool
Advanced tools
Comparing version 1.0.12 to 2.0.0
@@ -74,4 +74,9 @@ var PriorityQueue = function(size) { | ||
* @param {Number} factory.max | ||
* Maximum numnber of items that can exist at the same time. | ||
* Maximum numnber of items that can exist at the same time. Default: 1. | ||
* Any further acquire requests will be pushed to the waiting list. | ||
* @param {Number} factory.min | ||
* Minimum numnber of items in pool (including in-use). Default: 0. | ||
* When the pool is created, or a resource destroyed, this minimum will | ||
* be checked. If the pool resource count is below the minimum, a new | ||
* resource will be created and added to the pool. | ||
* @param {Number} factory.idleTimeoutMillis | ||
@@ -106,5 +111,8 @@ * Delay in milliseconds after the idle items in the pool will be destroyed. | ||
(function (str, level) { | ||
typeof factory.log === 'function' ? | ||
factory.log(str, level) : | ||
console.log(level.toUpperCase() + " pool " + factory.name + " - " + str); | ||
if (typeof factory.log === 'function') { | ||
factory.log(str, level); | ||
} | ||
else { | ||
console.log(level.toUpperCase() + " pool " + factory.name + " - " + str); | ||
} | ||
} | ||
@@ -114,5 +122,10 @@ ) : | ||
factory.max = parseInt(factory.max, 10); | ||
factory.min = parseInt(factory.min, 10); | ||
factory.max = Math.max(isNaN(factory.max) ? 1 : factory.max, 1); | ||
factory.min = Math.min(isNaN(factory.min) ? 0 : factory.min, factory.max-1); | ||
/////////////// | ||
factory.max = Math.max(factory.max, 1); | ||
/** | ||
@@ -133,2 +146,4 @@ * Request the client to be destroyed. The factory's destroy handler | ||
factory.destroy(obj); | ||
ensureMinimum(); | ||
}; | ||
@@ -162,3 +177,2 @@ | ||
} | ||
@@ -217,3 +231,5 @@ // Replace the available items with the ones to keep. | ||
err = null, | ||
clientCb = null, | ||
waitingCount = waitingClients.size(); | ||
log("dispense() clients=" + waitingCount + " available=" + availableObjects.length, 'info'); | ||
@@ -224,30 +240,45 @@ if (waitingCount > 0) { | ||
objWithTimeout = availableObjects.shift(); | ||
adjustCallback(waitingClients.dequeue(), err, objWithTimeout.obj); | ||
clientCb = waitingClients.dequeue(); | ||
clientCb(err, objWithTimeout.obj); | ||
} | ||
else if (count < factory.max) { | ||
count += 1; | ||
log("dispense() - creating obj - count=" + count, 'verbose'); | ||
factory.create(function () { | ||
var cb = waitingClients.dequeue(); | ||
if (arguments.length > 1) { | ||
err = arguments[0]; | ||
obj = arguments[1]; | ||
} else { | ||
err = (arguments[0] instanceof Error) ? arguments[0] : null; | ||
obj = (arguments[0] instanceof Error) ? null : arguments[0]; | ||
} | ||
if (err) { | ||
count -= 1; | ||
adjustCallback(cb, err, obj); | ||
} else { | ||
if (cb) { | ||
adjustCallback(cb, err, obj); | ||
} else { | ||
me.release(obj); | ||
} | ||
} | ||
}); | ||
createResource(); | ||
} | ||
} | ||
} | ||
function createResource() { | ||
count += 1; | ||
log("createResource() - creating obj - count=" + count + " min=" + factory.min + " max=" + factory.max, 'verbose'); | ||
factory.create(function () { | ||
var clientCb = waitingClients.dequeue(); | ||
if (arguments.length > 1) { | ||
err = arguments[0]; | ||
obj = arguments[1]; | ||
} else { | ||
err = (arguments[0] instanceof Error) ? arguments[0] : null; | ||
obj = (arguments[0] instanceof Error) ? null : arguments[0]; | ||
} | ||
if (err) { | ||
count -= 1; | ||
clientCb(err, obj); | ||
} else { | ||
if (clientCb) { | ||
clientCb(err, obj); | ||
} else { | ||
me.release(obj); | ||
} | ||
} | ||
}); | ||
} | ||
function ensureMinimum() { | ||
var i, diff; | ||
if (!draining && (count < factory.min)) { | ||
diff = factory.min - count; | ||
for (i = 0; i < diff; i++) { | ||
createResource(); | ||
} | ||
} | ||
} | ||
@@ -342,2 +373,7 @@ /** | ||
* | ||
* Note that if factory.min > 0, the pool will destroy all idle resources | ||
* in the pool, but replace them with newly created resources up to the | ||
* specified factory.min value. If this is not desired, set factory.min | ||
* to zero before calling destroyAllNow() | ||
* | ||
* @param {Function} callback | ||
@@ -417,3 +453,6 @@ * Optional. Callback invoked after all existing clients are destroyed. | ||
// create initial resources (if factory.min > 0) | ||
ensureMinimum(); | ||
return me; | ||
}; |
{ | ||
"name": "generic-pool", | ||
"description": "Generic resource pooling for Node.JS", | ||
"version": "1.0.12", | ||
"version": "2.0.0", | ||
"author": "James Cooper <james@bitmechanic.com>", | ||
@@ -19,3 +19,6 @@ "contributors": [ | ||
}, | ||
"engines": { "node": ">= 0.2.0" } | ||
"engines": { "node": ">= 0.2.0" }, | ||
"scripts": { | ||
"test": "expresso -I lib test/*.js" | ||
} | ||
} |
@@ -6,3 +6,12 @@ | ||
database connections. | ||
## 2.0 Release Warning | ||
The 2.0.0 release removed support for variable argument callbacks. When you acquire | ||
a resource from the pool, your callback *must* accept two arguments: (err, obj) | ||
Previously this library attempted to determine the arity of the callback, but this resulted | ||
in a variety of issues. This change eliminates these issues, and makes the acquire callback | ||
parameter order consistent with the factory.create callback. | ||
## Installation | ||
@@ -14,2 +23,9 @@ | ||
2.0.0 - July 31 2012 | ||
- Non-backwards compatible change: remove adjustCallback | ||
- acquire() callback must accept two params: (err, obj) | ||
- Add optional 'min' param to factory object that specifies minimum number of | ||
resources to keep in pool | ||
- Merged #38 (package.json/Makefile changes - contributed by strk) | ||
1.0.12 - June 27 2012 | ||
@@ -64,4 +80,6 @@ - Merged #37 (Clear remove idle timer after destroyAllNow - contributed by dougwilson) | ||
### Step 1 - Create pool using a factory object | ||
// Create a MySQL connection pool with | ||
// a max of 10 connections and a 30 second max idle time | ||
// a max of 10 connections, a min of 2, and a 30 second max idle time | ||
var poolModule = require('generic-pool'); | ||
@@ -84,5 +102,11 @@ var pool = poolModule.Pool({ | ||
max : 10, | ||
// optional. if you set this, make sure to drain() (see step 3) | ||
min : 2, | ||
// specifies how long a resource can stay idle in pool before being removed | ||
idleTimeoutMillis : 30000, | ||
log : true | ||
// if true, logs via console.log - can also be a function | ||
log : true | ||
}); | ||
### Step 2 - Use pool in your code to acquire/release resources | ||
@@ -92,8 +116,40 @@ // acquire connection - callback function is called | ||
pool.acquire(function(err, client) { | ||
client.query("select * from foo", [], function() { | ||
// return object back to pool | ||
pool.release(client); | ||
}); | ||
if (err) { | ||
// handle error - this is generally the err from your | ||
// factory.create function | ||
} | ||
else { | ||
client.query("select * from foo", [], function() { | ||
// return object back to pool | ||
pool.release(client); | ||
}); | ||
} | ||
}); | ||
### Step 3 - Drain pool during shutdown (optional) | ||
If you are shutting down a long-lived process, you may notice | ||
that node fails to exit for 30 seconds or so. This is a side | ||
effect of the idleTimeoutMillis behavior -- the pool has a | ||
setTimeout() call registered that is in the event loop queue, so | ||
node won't terminate until all resources have timed out, and the pool | ||
stops trying to manage them. | ||
This behavior will be more problematic when you set factory.min > 0, | ||
as the pool will never become empty, and the setTimeout calls will | ||
never end. | ||
In these cases, use the pool.drain() function. This sets the pool | ||
into a "draining" state which will gracefully wait until all | ||
idle resources have timed out. For example, you can call: | ||
// Only call this once in your application -- at the point you want | ||
// to shutdown and stop using this pool. | ||
pool.drain(function() { | ||
pool.destroyAllNow(); | ||
}); | ||
If you do this, your node process will exit gracefully. | ||
## Documentation | ||
@@ -108,2 +164,7 @@ | ||
max : maximum number of resources to create at any given time | ||
optional (default=1) | ||
min : minimum number of resources to keep in pool at any given time | ||
if this is set > max, the pool will silently set the min | ||
to factory.max - 1 | ||
optional (default=0) | ||
idleTimeoutMillis : max milliseconds a resource can go unused before it should be destroyed | ||
@@ -162,4 +223,4 @@ (default 30000) | ||
If you know would like to terminate all the resources in your queue before | ||
their timeouts have been reached, you can use `shutdownNow()` in conjunction | ||
If you know would like to terminate all the resources in your pool before | ||
their timeouts have been reached, you can use `destroyAllNow()` in conjunction | ||
with `drain()`: | ||
@@ -166,0 +227,0 @@ |
@@ -10,4 +10,4 @@ var assert = require('assert'); | ||
var borrowCount = 0; | ||
var pool = poolModule.Pool({ | ||
var factory = { | ||
name : 'test1', | ||
@@ -20,4 +20,6 @@ create : function(callback) { | ||
idleTimeoutMillis : 100 | ||
}); | ||
}; | ||
var pool = poolModule.Pool(factory); | ||
for (var i = 0; i < 10; i++) { | ||
@@ -37,2 +39,3 @@ var full = !pool.acquire(function(err, obj) { | ||
beforeExit(function() { | ||
assert.equal(0, factory.min); | ||
assert.equal(2, createCount); | ||
@@ -43,3 +46,77 @@ assert.equal(2, destroyCount); | ||
}, | ||
'respects min limit' : function (beforeExit) { | ||
var createCount = 0; | ||
var destroyCount = 0; | ||
var borrowCount = 0; | ||
var pool = poolModule.Pool({ | ||
name : 'test-min', | ||
create : function(callback) { | ||
callback(null, { count: ++createCount }); | ||
}, | ||
destroy : function(client) { destroyCount++; }, | ||
min : 1, | ||
max : 2, | ||
idleTimeoutMillis : 100 | ||
}); | ||
pool.drain(); | ||
beforeExit(function() { | ||
assert.equal(0, pool.availableObjectsCount()); | ||
assert.equal(1, createCount); | ||
assert.equal(1, destroyCount); | ||
}); | ||
}, | ||
'min and max limit defaults' : function (beforeExit) { | ||
var factory = { | ||
name : "test-limit-defaults", | ||
create : function(callback) { callback(null, {}); }, | ||
destroy : function(client) { }, | ||
idleTimeoutMillis: 100 | ||
}; | ||
var pool = poolModule.Pool(factory); | ||
beforeExit(function() { | ||
assert.equal(1, factory.max); | ||
assert.equal(0, factory.min); | ||
}); | ||
}, | ||
'malformed min and max limits are ignored' : function (beforeExit) { | ||
var factory = { | ||
name : "test-limit-defaults2", | ||
create : function(callback) { callback(null, {}); }, | ||
destroy : function(client) { }, | ||
idleTimeoutMillis: 100, | ||
min : "asf", | ||
max : [ ] | ||
}; | ||
var pool = poolModule.Pool(factory); | ||
beforeExit(function() { | ||
assert.equal(1, factory.max); | ||
assert.equal(0, factory.min); | ||
}); | ||
}, | ||
'min greater than max sets to max minus one' : function (beforeExit) { | ||
var factory = { | ||
name : "test-limit-defaults3", | ||
create : function(callback) { callback(null, {}); }, | ||
destroy : function(client) { }, | ||
idleTimeoutMillis: 100, | ||
min : 5, | ||
max : 3 | ||
}; | ||
var pool = poolModule.Pool(factory); | ||
pool.drain(); | ||
beforeExit(function() { | ||
assert.equal(3, factory.max); | ||
assert.equal(2, factory.min); | ||
}); | ||
}, | ||
'supports priority on borrow' : function(beforeExit) { | ||
@@ -159,16 +236,2 @@ var borrowTimeLow = 0; | ||
'supports single arg callbacks' : function (beforeExit) { | ||
var pool = poolModule.Pool({ | ||
name : 'test5', | ||
create : function(callback) { callback({ id : 1 }); }, | ||
destroy : function(client) { destroyed.push(client.id); }, | ||
max : 2, | ||
idleTimeoutMillis : 100 | ||
}); | ||
pool.acquire(function(client) { | ||
assert.equal(client.id, 1); | ||
}); | ||
}, | ||
'handle creation errors' : function (beforeExit) { | ||
@@ -428,3 +491,3 @@ var created = 0; | ||
if (err) {throw err;} | ||
assert.equal(logmessages.verbose[0], 'dispense() - creating obj - count=1'); | ||
assert.equal(logmessages.verbose[0], 'createResource() - creating obj - count=1 min=0 max=2'); | ||
assert.equal(logmessages.info[0], 'dispense() clients=1 available=0'); | ||
@@ -445,4 +508,4 @@ logmessages.info = []; | ||
name: 'test', | ||
create: function(callback) {callback(null, {})}, | ||
destroy: function(client) {destroyCalled = true}, | ||
create: function(callback) {callback(null, {}); }, | ||
destroy: function(client) {destroyCalled = true; }, | ||
max: 2, | ||
@@ -449,0 +512,0 @@ idleTimeoutMillis: 100 |
43278
7
848
304