Permalink
Browse files

Merge pull request #36 from cosbynator/master

"pooled" method to perform function decoration for pooled methods
  • Loading branch information...
2 parents 1ed52d3 + b142d07 commit df2579ee650a8eaed4487956050e4929cd2dd383 @coopernurse committed Jun 18, 2012
Showing with 180 additions and 1 deletion.
  1. +30 −1 README.md
  2. +39 −0 lib/generic-pool.js
  3. +111 −0 test/generic-pool.test.js
View
@@ -110,7 +110,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
@@ -158,6 +158,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:
View
@@ -348,6 +348,44 @@ exports.Pool = function (factory) {
}
};
+ /**
+ * 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;
};
@@ -364,5 +402,6 @@ exports.Pool = function (factory) {
return waitingClients.size();
};
+
return me;
};
View
@@ -195,6 +195,117 @@ module.exports = {
});
},
+ '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);
+ });
+ },
+
'getPoolSize' : function (beforeExit) {
var assertion_count = 0;
var pool = poolModule.Pool({

0 comments on commit df2579e

Please sign in to comment.