[Pkg-javascript-commits] [node-generic-pool] 08/13: Imported Upstream version 2.0.3
Jérémy Lal
kapouer at alioth.debian.org
Mon Sep 2 21:54:25 UTC 2013
This is an automated email from the git hooks/post-receive script.
kapouer pushed a commit to branch master
in repository node-generic-pool.
commit 599a837cbe11a673194d437544d43178813a093f
Author: Jérémy Lal <kapouer at melix.org>
Date: Mon Sep 2 23:47:43 2013 +0200
Imported Upstream version 2.0.3
---
.gitignore | 1 +
.travis.yml | 4 +
Makefile | 4 +
README.md | 141 ++++++++++++++++++--
lib/generic-pool.js | 195 ++++++++++++++++++++-------
package.json | 13 +-
test/generic-pool.test.js | 325 ++++++++++++++++++++++++++++++++++++++++++---
7 files changed, 610 insertions(+), 73 deletions(-)
diff --git a/.gitignore b/.gitignore
index 06bf63f..fcb718b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
fabfile.pyc
node-pool.iml
node-pool.tmproj
+node_modules
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..8111245
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+ - 0.6
+ - 0.8
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..669888d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,4 @@
+all:
+
+check:
+ npm test
diff --git a/README.md b/README.md
index 4d1a6d1..15d1a40 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,18 @@
+[![build status](https://secure.travis-ci.org/coopernurse/node-pool.png)](http://travis-ci.org/coopernurse/node-pool)
# About
Generic resource pool. Can be used to reuse or throttle expensive resources such as
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
@@ -10,6 +20,37 @@
## History
+ 2.0.3 - January 16 2013
+ - Merged #56/#57 - Add optional refreshIdle flag. If false, idle resources at the pool minimum will not be
+ destroyed/re-created. (contributed by wshaver)
+ - Merged #54 - Factory can be asked to validate pooled objects (contributed by tikonen)
+
+ 2.0.2 - October 22 2012
+ - Fix #51, #48 - createResource() should check for null clientCb in err case (contributed by pooyasencha)
+ - Merged #52 - fix bug of infinite wait when create object aync error (contributed by windyrobin)
+ - Merged #53 - change the position of dispense and callback to ensure the time order (contributed by windyrobin)
+
+ 2.0.1 - August 29 2012
+ - Fix #44 - leak of 'err' and 'obj' in createResource()
+ - Add devDependencies block to package.json
+ - Add travis-ci.org integration
+
+ 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
+ - Merged #37 (Clear remove idle timer after destroyAllNow - contributed by dougwilson)
+
+ 1.0.11 - June 17 2012
+ - Merged #36 ("pooled" method to perform function decoration for pooled methods - contributed by cosbynator)
+
+ 1.0.10 - May 3 2012
+ - Merged #35 (Remove client from availbleObjects on destroy(client) - contributed by blax)
+
1.0.9 - Dec 18 2011
- Merged #25 (add getName() - contributed by BryanDonovan)
- Merged #27 (remove sys import - contributed by botker)
@@ -50,8 +91,10 @@
## Example
+### 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');
var pool = poolModule.Pool({
name : 'mysql',
@@ -69,19 +112,57 @@
},
destroy : function(client) { client.end(); },
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
// acquire connection - callback function is called
// once a resource becomes available
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
Pool() accepts an object with these slots:
@@ -91,12 +172,23 @@
should call callback() with the created resource
destroy : function that accepts a resource and destroys it
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)
+ refreshIdle : boolean that specifies whether idle resources at or below the min threshold
+ should be destroyed/re-created. optional (default=true)
idleTimeoutMillis : max milliseconds a resource can go unused before it should be destroyed
(default 30000)
reapIntervalMillis : frequency to check for idle resources (default 1000),
priorityRange : int between 1 and x - if set, borrowers can specify their
relative priority in the queue if no resources are available.
see example. (default 1)
+ validate : function that accepts a pooled resource and returns true if the resource
+ is OK to use, or false if the object is invalid. Invalid objects will be destroyed.
+ This function is called in acquire() before returning a resource from the pool.
+ Optional. Default function always returns true.
log : true/false or function -
If a log is a function, it will be called with two parameters:
- log string
@@ -107,7 +199,7 @@
## Priority Queueing
The pool now supports optional priority queueing. This becomes relevant when no resources
-are available and the caller has to wait. acquire() accepts an optional priority int which
+are available and the caller has to wait. `acquire()` accepts an optional priority int which
specifies the caller's relative position in the queue.
// create pool with priorityRange of 3
@@ -144,8 +236,8 @@ specifies the caller's relative position in the queue.
## Draining
-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()`:
pool.drain(function() {
@@ -155,6 +247,35 @@ with `drain()`:
One side-effect of calling `drain()` is that subsequent calls to `acquire()`
will throw an Error.
+## Pooled function decoration
+
+To transparently handle object acquisition for a function,
+one can use `pooled()`:
+
+ var privateFn, publicFn;
+ publicFn = pool.pooled(privateFn = function(client, arg, cb) {
+ // Do something with the client and arg. Client is auto-released when cb is called
+ cb(null, arg);
+ });
+
+Keeping both private and public versions of each function allows for pooled
+functions to call other pooled functions with the same member. This is a handy
+pattern for database transactions:
+
+ var privateTop, privateBottom, publicTop, publicBottom;
+ publicBottom = pool.pooled(privateBottom = function(client, arg, cb) {
+ //Use client, assumed auto-release
+ });
+
+ publicTop = pool.pooled(privateTop = function(client, cb) {
+ // e.g., open a database transaction
+ privateBottom(client, "arg", function(err, retVal) {
+ if(err) { return cb(err); }
+ // e.g., close a transaction
+ cb();
+ });
+ });
+
## Pool info
The following functions will let you get information about the pool:
@@ -182,7 +303,7 @@ The following functions will let you get information about the pool:
(The MIT License)
-Copyright (c) 2010-2011 James Cooper <james at bitmechanic.com>
+Copyright (c) 2010-2013 James Cooper <james at bitmechanic.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/lib/generic-pool.js b/lib/generic-pool.js
index a1b7cb6..6bd3aa9 100644
--- a/lib/generic-pool.js
+++ b/lib/generic-pool.js
@@ -70,9 +70,18 @@ var PriorityQueue = function(size) {
* @param {Function} factory.destroy
* Should gently close any resources that the item is using.
* Called before the items is destroyed.
+ * @param {Function} factory.validate
+ * Should return true if connection is still valid and false
+ * If it should be removed from pool. Called before item is
+ * acquired from pool.
* @param {Number} factory.max
- * Maximum numnber of items that can exist at the same time.
+ * Maximum number 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 number 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
* Delay in milliseconds after the idle items in the pool will be destroyed.
* And idle item is that is not acquired yet. Waiting items doesn't count here.
@@ -83,7 +92,8 @@ var PriorityQueue = function(size) {
* that will be used instead. The function expects the arguments msg, loglevel
* @param {Number} factory.priorityRange
* The range from 1 to be treated as a valid priority
- *
+ * @param {RefreshIdle} factory.refreshIdle
+ * Should idle resources be destroyed and recreated every idleTimeoutMillis? Default: true.
* @returns {Object} An Object pool that works with the supplied `factory`.
*/
exports.Pool = function (factory) {
@@ -91,66 +101,84 @@ exports.Pool = function (factory) {
idleTimeoutMillis = factory.idleTimeoutMillis || 30000,
reapInterval = factory.reapIntervalMillis || 1000,
-
+ refreshIdle = ('refreshIdle' in factory) ? factory.refreshIdle : true,
availableObjects = [],
waitingClients = new PriorityQueue(factory.priorityRange || 1),
count = 0,
removeIdleScheduled = false,
+ removeIdleTimer = null,
draining = false,
// Prepare a logger function.
log = factory.log ?
(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);
+ }
}
) :
function () {};
-
- factory.max = Math.max(factory.max, 1);
+ factory.validate = factory.validate || function() { return true; };
+
+ 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);
+
+ ///////////////
/**
* Request the client to be destroyed. The factory's destroy handler
* will also be called.
*
+ * This should be called within an acquire() block as an alternative to release().
+ *
* @param {Object} obj
* The acquired item to be destoyed.
*/
me.destroy = function(obj) {
count -= 1;
+ availableObjects = availableObjects.filter(function(objWithTimeout) {
+ return (objWithTimeout.obj !== obj);
+ });
factory.destroy(obj);
+
+ ensureMinimum();
};
/**
* Checks and removes the available (idle) clients that have timed out.
*/
function removeIdle() {
- var toKeep = [],
+ var toRemove = [],
now = new Date().getTime(),
i,
- al,
+ al, tr,
timeout;
removeIdleScheduled = false;
// Go through the available (idle) items,
// check if they have timed out
- for (i = 0, al = availableObjects.length; i < al; i += 1) {
+ for (i = 0, al = availableObjects.length; i < al && (refreshIdle || (count - factory.min)) > toRemove.length ; i += 1) {
timeout = availableObjects[i].timeout;
- if (now < timeout) {
- // Client hasn't timed out, so keep it.
- toKeep.push(availableObjects[i]);
- } else {
- // The client timed out, call its destroyer.
+ if (now >= timeout) {
+ // Client timed out, so destroy it.
log("removeIdle() destroying obj - now:" + now + " timeout:" + timeout, 'verbose');
- me.destroy(availableObjects[i].obj);
- }
+ toRemove.push(availableObjects[i].obj);
+ }
+ }
+
+ for (i = 0, tr = toRemove.length; i < tr; i += 1) {
+ me.destroy(toRemove[i]);
}
// Replace the available items with the ones to keep.
- availableObjects = toKeep;
al = availableObjects.length;
if (al > 0) {
@@ -170,7 +198,7 @@ exports.Pool = function (factory) {
function scheduleRemoveIdle() {
if (!removeIdleScheduled) {
removeIdleScheduled = true;
- setTimeout(removeIdle, reapInterval);
+ removeIdleTimer = setTimeout(removeIdle, reapInterval);
}
}
@@ -202,37 +230,65 @@ exports.Pool = function (factory) {
var obj = null,
objWithTimeout = null,
err = null,
+ clientCb = null,
waitingCount = waitingClients.size();
+
log("dispense() clients=" + waitingCount + " available=" + availableObjects.length, 'info');
if (waitingCount > 0) {
- if (availableObjects.length > 0) {
+ while (availableObjects.length > 0) {
log("dispense() - reusing obj", 'verbose');
- objWithTimeout = availableObjects.shift();
- adjustCallback(waitingClients.dequeue(), err, objWithTimeout.obj);
+ objWithTimeout = availableObjects[0];
+ if (!factory.validate(objWithTimeout.obj)) {
+ me.destroy(objWithTimeout.obj);
+ continue;
+ }
+ availableObjects.shift();
+ clientCb = waitingClients.dequeue();
+ return 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);
- }
- }
+ if (count < factory.max) {
+ createResource();
+ }
+ }
+ }
+
+ function createResource() {
+ count += 1;
+ log("createResource() - creating obj - count=" + count + " min=" + factory.min + " max=" + factory.max, 'verbose');
+ factory.create(function () {
+ var err, obj;
+ 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;
+ if (clientCb) {
+ clientCb(err, obj);
+ }
+ process.nextTick(function(){
+ dispense();
});
+ } 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();
}
}
}
@@ -325,6 +381,11 @@ exports.Pool = function (factory) {
* invoked as part of a drain. Does not prevent the creation of new
* clients as a result of subsequent calls to acquire.
*
+ * 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
* Optional. Callback invoked after all existing clients are destroyed.
*/
@@ -337,11 +398,51 @@ exports.Pool = function (factory) {
me.destroy(obj.obj);
obj = willDie.shift();
}
+ removeIdleScheduled = false;
+ clearTimeout(removeIdleTimer);
if (callback) {
callback();
}
};
+ /**
+ * Decorates a function to use a acquired client from the object pool when called.
+ *
+ * @param {Function} decorated
+ * The decorated function, accepting a client as the first argument and
+ * (optionally) a callback as the final argument.
+ *
+ * @param {Number} priority
+ * Optional. Integer between 0 and (priorityRange - 1). Specifies the priority
+ * of the caller if there are no available resources. Lower numbers mean higher
+ * priority.
+ */
+ me.pooled = function(decorated, priority) {
+ return function() {
+ var callerArgs = arguments;
+ var callerCallback = callerArgs[callerArgs.length - 1];
+ var callerHasCallback = typeof callerCallback === 'function';
+ me.acquire(function(err, client) {
+ if(err) {
+ if(callerHasCallback) {
+ callerCallback(err);
+ }
+ return;
+ }
+
+ var args = [client].concat(Array.prototype.slice.call(callerArgs, 0, callerHasCallback ? -1 : undefined));
+ args.push(function() {
+ me.release(client);
+ if(callerHasCallback) {
+ callerCallback.apply(null, arguments);
+ }
+ });
+
+ decorated.apply(null, args);
+ }, priority);
+ };
+ };
+
me.getPoolSize = function() {
return count;
};
@@ -358,5 +459,9 @@ exports.Pool = function (factory) {
return waitingClients.size();
};
+
+ // create initial resources (if factory.min > 0)
+ ensureMinimum();
+
return me;
};
diff --git a/package.json b/package.json
index 539c40b..f144731 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,14 @@
{
"name": "generic-pool",
"description": "Generic resource pooling for Node.JS",
- "version": "1.0.9",
+ "version": "2.0.3",
"author": "James Cooper <james at bitmechanic.com>",
"contributors": [
{ "name": "James Cooper", "email": "james at bitmechanic.com" },
{ "name": "Peter Galiba", "email": "poetro at poetro.hu", "url": "http://poetro.hu/" },
{ "name": "Gary Dusbabek" },
- { "name": "Tom MacWright", "url" : "http://www.developmentseed.org/" }
+ { "name": "Tom MacWright", "url" : "http://www.developmentseed.org/" },
+ { "name": "Douglas Christopher Wilson", "email": "doug at somethingdoug.com", "url" : "http://somethingdoug.com/" }
],
"keywords": ["pool", "pooling", "throttle"],
"main": "lib/generic-pool.js",
@@ -15,5 +16,11 @@
"type": "git",
"url": "http://github.com/coopernurse/node-pool.git"
},
- "engines": { "node": ">= 0.2.0" }
+ "devDependencies": {
+ "expresso": ">0.0.0"
+ },
+ "engines": { "node": ">= 0.2.0" },
+ "scripts": {
+ "test": "expresso -I lib test/*.js"
+ }
}
diff --git a/test/generic-pool.test.js b/test/generic-pool.test.js
index d59162f..d7ac17a 100644
--- a/test/generic-pool.test.js
+++ b/test/generic-pool.test.js
@@ -7,8 +7,8 @@ module.exports = {
var createCount = 0;
var destroyCount = 0;
var borrowCount = 0;
-
- var pool = poolModule.Pool({
+
+ var factory = {
name : 'test1',
create : function(callback) {
callback(null, { count: ++createCount });
@@ -16,7 +16,9 @@ module.exports = {
destroy : function(client) { destroyCount++; },
max : 2,
idleTimeoutMillis : 100
- });
+ };
+
+ var pool = poolModule.Pool(factory);
for (var i = 0; i < 10; i++) {
var full = !pool.acquire(function(err, obj) {
@@ -32,11 +34,86 @@ module.exports = {
}
beforeExit(function() {
+ assert.equal(0, factory.min);
assert.equal(2, createCount);
assert.equal(2, destroyCount);
assert.equal(10, borrowCount);
});
},
+
+ '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) {
var borrowTimeLow = 0;
@@ -152,29 +229,55 @@ module.exports = {
}, Error);
},
- 'supports single arg callbacks' : function (beforeExit) {
+ 'handle creation errors' : function (beforeExit) {
+ var created = 0;
var pool = poolModule.Pool({
- name : 'test5',
- create : function(callback) { callback({ id : 1 }); },
- destroy : function(client) { destroyed.push(client.id); },
- max : 2,
- idleTimeoutMillis : 100
+ name : 'test6',
+ create : function(callback) {
+ if (created < 5) {
+ callback(new Error('Error occurred.'));
+ } else {
+ callback({ id : created });
+ }
+ created++;
+ },
+ destroy : function(client) { },
+ max : 1,
+ idleTimeoutMillis : 1000
});
+ // ensure that creation errors do not populate the pool.
+ for (var i = 0; i < 5; i++) {
+ pool.acquire(function(err, client) {
+ assert.ok(err instanceof Error);
+ assert.ok(client === null);
+ });
+ }
- pool.acquire(function(client) {
- assert.equal(client.id, 1);
+ var called = false;
+ pool.acquire(function(err, client) {
+ assert.ok(err === null);
+ assert.equal(typeof client.id, 'number');
+ called = true;
+ });
+ beforeExit(function() {
+ assert.ok(called);
+ assert.equal(pool.waitingClientsCount(), 0);
});
},
- 'handle creation errors' : function (beforeExit) {
+ 'handle creation errors for delayed creates' : function (beforeExit) {
var created = 0;
var pool = poolModule.Pool({
name : 'test6',
create : function(callback) {
if (created < 5) {
- callback(new Error('Error occurred.'));
+ setTimeout(function() {
+ callback(new Error('Error occurred.'));
+ }, 0);
} else {
- callback({ id : created });
+ setTimeout(function() {
+ callback({ id : created });
+ }, 0);
}
created++;
},
@@ -189,9 +292,126 @@ module.exports = {
assert.ok(client === null);
});
}
+ var called = false;
pool.acquire(function(err, client) {
assert.ok(err === null);
assert.equal(typeof client.id, 'number');
+ called = true;
+ });
+ beforeExit(function() {
+ assert.ok(called);
+ assert.equal(pool.waitingClientsCount(), 0);
+ });
+ },
+
+ 'pooled decorator should acquire and release' : function (beforeExit) {
+ var assertion_count = 0;
+ var destroyed_count = 0;
+ var pool = poolModule.Pool({
+ name : 'test1',
+ create : function(callback) { callback({id: Math.floor(Math.random()*1000)}); },
+ destroy : function(client) { destroyed_count += 1; },
+ max : 1,
+ idleTimeoutMillis : 100
+ });
+
+ var pooledFn = pool.pooled(function(client, cb) {
+ assert.equal(typeof client.id, 'number');
+ assert.equal(pool.getPoolSize(), 1);
+ assertion_count += 2;
+ cb();
+ });
+
+ assert.equal(pool.getPoolSize(), 0);
+ assertion_count += 1;
+
+ pooledFn(function(err) {
+ if (err) { throw err; }
+ assert.ok(true);
+ assertion_count += 1;
+ });
+
+ beforeExit(function() {
+ assert.equal(assertion_count, 4);
+ assert.equal(destroyed_count, 1);
+ });
+ },
+
+ 'pooled decorator should pass arguments and return values' : function(beforeExit) {
+ var assertion_count = 0;
+ var pool = poolModule.Pool({
+ name : 'test1',
+ create : function(callback) { callback({id: Math.floor(Math.random()*1000)}); },
+ destroy : function(client) { },
+ max : 1,
+ idleTimeoutMillis : 100
+ });
+
+ var pooledFn = pool.pooled(function(client, arg1, arg2, cb) {
+ assert.equal(arg1, "First argument");
+ assert.equal(arg2, "Second argument");
+ assertion_count += 2;
+ cb(null, "First return", "Second return");
+ });
+
+ pooledFn("First argument", "Second argument", function(err, retVal1, retVal2) {
+ if(err) { throw err; }
+ assert.equal(retVal1, "First return");
+ assert.equal(retVal2, "Second return");
+ assertion_count += 2;
+ });
+
+ beforeExit(function() {
+ assert.equal(assertion_count, 4);
+ });
+ },
+
+ 'pooled decorator should allow undefined callback' : function(beforeExit) {
+ var assertion_count = 0;
+ var pool = poolModule.Pool({
+ name : 'test1',
+ create : function(callback) { callback({id: Math.floor(Math.random()*1000)}); },
+ destroy : function(client) { },
+ max : 1,
+ idleTimeoutMillis : 100
+ });
+
+ var pooledFn = pool.pooled(function(client, arg, cb) {
+ assert.equal(arg, "Arg!");
+ assertion_count += 1;
+ cb();
+ });
+
+ pooledFn("Arg!");
+
+ beforeExit(function() {
+ assert.equal(pool.getPoolSize(), 0);
+ assert.equal(assertion_count, 1);
+ });
+
+ },
+
+ 'pooled decorator should forward pool errors' : function(beforeExit) {
+ var assertion_count = 0;
+ var pool = poolModule.Pool({
+ name : 'test1',
+ create : function(callback) { callback(new Error('Pool error')); },
+ destroy : function(client) { },
+ max : 1,
+ idleTimeoutMillis : 100
+ });
+
+ var pooledFn = pool.pooled(function(cb) {
+ assert.ok(false, "Pooled function shouldn't be called due to a pool error");
+ });
+
+ pooledFn(function(err, obj) {
+ assert.equal(err.message, 'Pool error');
+ assertion_count += 1;
+ });
+
+ beforeExit(function() {
+ assert.equal(assertion_count, 1);
});
},
@@ -309,7 +529,7 @@ module.exports = {
pool.acquire(function(err, obj){
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');
logmessages.info = [];
logmessages.verbose = [];
@@ -319,6 +539,81 @@ module.exports = {
assert.equal(logmessages.warn.length, 0);
});
});
+ },
+
+ 'removes from available objects on destroy': function(beforeExit){
+ var destroyCalled = false;
+ var factory = {
+ name: 'test',
+ create: function(callback) {callback(null, {}); },
+ destroy: function(client) {destroyCalled = true; },
+ max: 2,
+ idleTimeoutMillis: 100
+ };
+
+ var pool = poolModule.Pool(factory);
+ pool.acquire(function(err, obj){
+ pool.destroy(obj);
+ });
+ assert.equal(destroyCalled, true);
+ assert.equal(pool.availableObjectsCount(), 0);
+ },
+
+ 'removes from available objects on validation failure': function(beforeExit){
+ var destroyCalled = false,
+ validateCalled = false,
+ count = 0;
+ var factory = {
+ name: 'test',
+ create: function(callback) {callback(null, {count: count++}); },
+ destroy: function(client) {destroyCalled = client.count; },
+ validate: function(client) {validateCalled = true; return client.count != 0;},
+ max: 2,
+ idleTimeoutMillis: 100
+ };
+
+ var pool = poolModule.Pool(factory);
+ pool.acquire(function(err, obj){
+ pool.release(obj);
+ assert.equal(obj.count, 0);
+
+ pool.acquire(function(err, obj){
+ pool.release(obj);
+ assert.equal(obj.count, 1);
+ });
+ });
+ assert.equal(validateCalled, true);
+ assert.equal(destroyCalled, 0);
+ assert.equal(pool.availableObjectsCount(), 1);
+ },
+
+ 'do schedule again if error occured when creating new Objects async': function(beforeExit){
+ var factory = {
+ name: 'test',
+ create: function(callback) {
+ process.nextTick(function(){
+ var err = new Error('Create Error');
+ callback(err);
+ })
+ },
+ destroy: function(client) {},
+ max: 1,
+ idleTimeoutMillis: 100
+ };
+
+ var getFlag = 0;
+ var pool = poolModule.Pool(factory);
+ pool.acquire(function(){});
+ pool.acquire(function(err, obj){
+ getFlag = 1;
+ assert(err);
+ assert.equal(pool.availableObjectsCount(), 0);
+ });
+
+ beforeExit(function() {
+ assert.equal(getFlag, 1);
+ });
}
+
};
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/collab-maint/node-generic-pool.git
More information about the Pkg-javascript-commits
mailing list