Skip to content
This repository
Browse code

Merge pull request #36 from cosbynator/master

"pooled" method to perform function decoration for pooled methods
  • Loading branch information...
commit df2579ee650a8eaed4487956050e4929cd2dd383 2 parents 1ed52d3 + b142d07
James Cooper authored June 17, 2012
31  README.md
Source Rendered
@@ -110,7 +110,7 @@
110 110
 ## Priority Queueing
111 111
 
112 112
 The pool now supports optional priority queueing.  This becomes relevant when no resources 
113  
-are available and the caller has to wait. acquire() accepts an optional priority int which 
  113
+are available and the caller has to wait. `acquire()` accepts an optional priority int which 
114 114
 specifies the caller's relative position in the queue.
115 115
 
116 116
      // create pool with priorityRange of 3
@@ -158,6 +158,35 @@ with `drain()`:
158 158
 One side-effect of calling `drain()` is that subsequent calls to `acquire()`
159 159
 will throw an Error.
160 160
 
  161
+## Pooled function decoration
  162
+
  163
+To transparently handle object acquisition for a function, 
  164
+one can use `pooled()`:
  165
+
  166
+    var privateFn, publicFn;
  167
+    publicFn = pool.pooled(privateFn = function(client, arg, cb) {
  168
+        // Do something with the client and arg. Client is auto-released when cb is called
  169
+        cb(null, arg);
  170
+    });
  171
+
  172
+Keeping both private and public versions of each function allows for pooled 
  173
+functions to call other pooled functions with the same member. This is a handy
  174
+pattern for database transactions:
  175
+
  176
+    var privateTop, privateBottom, publicTop, publicBottom;
  177
+    publicBottom = pool.pooled(privateBottom = function(client, arg, cb) {
  178
+        //Use client, assumed auto-release 
  179
+    });
  180
+
  181
+    publicTop = pool.pooled(privateTop = function(client, cb) {
  182
+        // e.g., open a database transaction
  183
+        privateBottom(client, "arg", function(err, retVal) {
  184
+            if(err) { return cb(err); }
  185
+            // e.g., close a transaction
  186
+            cb();
  187
+        });
  188
+    });
  189
+
161 190
 ## Pool info
162 191
 
163 192
 The following functions will let you get information about the pool:
39  lib/generic-pool.js
@@ -348,6 +348,44 @@ exports.Pool = function (factory) {
348 348
     }
349 349
   };
350 350
 
  351
+  /**
  352
+   * Decorates a function to use a acquired client from the object pool when called.
  353
+   *
  354
+   * @param {Function} decorated
  355
+   *   The decorated function, accepting a client as the first argument and 
  356
+   *   (optionally) a callback as the final argument.
  357
+   *
  358
+   * @param {Number} priority
  359
+   *   Optional.  Integer between 0 and (priorityRange - 1).  Specifies the priority
  360
+   *   of the caller if there are no available resources.  Lower numbers mean higher
  361
+   *   priority.
  362
+   */
  363
+  me.pooled = function(decorated, priority) {
  364
+    return function() {
  365
+      var callerArgs = arguments;
  366
+      var callerCallback = callerArgs[callerArgs.length - 1];
  367
+      var callerHasCallback = typeof callerCallback === 'function';
  368
+      me.acquire(function(err, client) {
  369
+        if(err) {
  370
+          if(callerHasCallback) {
  371
+            callerCallback(err);
  372
+          }
  373
+          return;
  374
+        }
  375
+
  376
+        var args = [client].concat(Array.prototype.slice.call(callerArgs, 0, callerHasCallback ? -1 : undefined));
  377
+        args.push(function() {
  378
+          me.release(client);
  379
+          if(callerHasCallback) {
  380
+            callerCallback.apply(null, arguments);
  381
+          }
  382
+        });
  383
+        
  384
+        decorated.apply(null, args);
  385
+      }, priority);
  386
+    };
  387
+  };
  388
+
351 389
   me.getPoolSize = function() {
352 390
     return count;
353 391
   };
@@ -364,5 +402,6 @@ exports.Pool = function (factory) {
364 402
     return waitingClients.size();
365 403
   };
366 404
 
  405
+
367 406
   return me;
368 407
 };
111  test/generic-pool.test.js
@@ -195,6 +195,117 @@ module.exports = {
195 195
         });
196 196
     },
197 197
 
  198
+    'pooled decorator should acquire and release' : function (beforeExit) {
  199
+        var assertion_count = 0;
  200
+        var destroyed_count = 0;
  201
+        var pool = poolModule.Pool({
  202
+            name     : 'test1',
  203
+            create   : function(callback) { callback({id: Math.floor(Math.random()*1000)}); },
  204
+            destroy  : function(client) { destroyed_count += 1; },
  205
+            max : 1,
  206
+            idleTimeoutMillis : 100
  207
+        });
  208
+
  209
+        var pooledFn = pool.pooled(function(client, cb) {
  210
+          assert.equal(typeof client.id, 'number');
  211
+          assert.equal(pool.getPoolSize(), 1);
  212
+          assertion_count += 2;
  213
+          cb();
  214
+        });
  215
+
  216
+        assert.equal(pool.getPoolSize(), 0);
  217
+        assertion_count += 1;
  218
+
  219
+        pooledFn(function(err) {
  220
+          if (err) { throw err; }
  221
+          assert.ok(true);
  222
+          assertion_count += 1;
  223
+        });
  224
+
  225
+        beforeExit(function() {
  226
+          assert.equal(assertion_count, 4);
  227
+          assert.equal(destroyed_count, 1); 
  228
+        });
  229
+    },
  230
+    
  231
+    'pooled decorator should pass arguments and return values' : function(beforeExit) {
  232
+        var assertion_count = 0;
  233
+        var pool = poolModule.Pool({
  234
+            name     : 'test1',
  235
+            create   : function(callback) { callback({id: Math.floor(Math.random()*1000)}); },
  236
+            destroy  : function(client) { },
  237
+            max : 1,
  238
+            idleTimeoutMillis : 100
  239
+        });
  240
+
  241
+        var pooledFn = pool.pooled(function(client, arg1, arg2, cb) {
  242
+          assert.equal(arg1, "First argument");
  243
+          assert.equal(arg2, "Second argument");
  244
+          assertion_count += 2;
  245
+          cb(null, "First return", "Second return");
  246
+        });
  247
+
  248
+        pooledFn("First argument", "Second argument", function(err, retVal1, retVal2) {
  249
+          if(err) { throw err; }
  250
+          assert.equal(retVal1, "First return");
  251
+          assert.equal(retVal2, "Second return");
  252
+          assertion_count += 2;
  253
+        });
  254
+
  255
+        beforeExit(function() {
  256
+          assert.equal(assertion_count, 4);
  257
+        });
  258
+    },
  259
+
  260
+    'pooled decorator should allow undefined callback' : function(beforeExit) {
  261
+        var assertion_count = 0;
  262
+        var pool = poolModule.Pool({
  263
+            name     : 'test1',
  264
+            create   : function(callback) { callback({id: Math.floor(Math.random()*1000)}); },
  265
+            destroy  : function(client) { },
  266
+            max : 1,
  267
+            idleTimeoutMillis : 100
  268
+        });
  269
+
  270
+        var pooledFn = pool.pooled(function(client, arg, cb) {
  271
+          assert.equal(arg, "Arg!");
  272
+          assertion_count += 1;
  273
+          cb();
  274
+        });
  275
+
  276
+        pooledFn("Arg!");
  277
+
  278
+        beforeExit(function() {
  279
+          assert.equal(pool.getPoolSize(), 0);
  280
+          assert.equal(assertion_count, 1);
  281
+        });
  282
+
  283
+    },
  284
+
  285
+    'pooled decorator should forward pool errors' : function(beforeExit) {
  286
+        var assertion_count = 0;
  287
+        var pool = poolModule.Pool({
  288
+            name     : 'test1',
  289
+            create   : function(callback) { callback(new Error('Pool error')); },
  290
+            destroy  : function(client) { },
  291
+            max : 1,
  292
+            idleTimeoutMillis : 100
  293
+        });
  294
+
  295
+        var pooledFn = pool.pooled(function(cb) {
  296
+          assert.ok(false, "Pooled function shouldn't be called due to a pool error");
  297
+        });
  298
+
  299
+        pooledFn(function(err, obj) {
  300
+          assert.equal(err.message, 'Pool error');
  301
+          assertion_count += 1;
  302
+        });
  303
+
  304
+        beforeExit(function() {
  305
+          assert.equal(assertion_count, 1);
  306
+        });
  307
+    },
  308
+
198 309
     'getPoolSize' : function (beforeExit) {
199 310
         var assertion_count = 0;
200 311
         var pool = poolModule.Pool({

0 notes on commit df2579e

Please sign in to comment.
Something went wrong with that request. Please try again.