generic-pool
Advanced tools
Comparing version 2.3.1 to 2.4.0
@@ -1,76 +0,80 @@ | ||
var PriorityQueue = function(size) { | ||
var me = {}, slots, i, total = null; | ||
/** | ||
* @class | ||
* @private | ||
*/ | ||
function PriorityQueue (size) { | ||
if (!(this instanceof PriorityQueue)) { | ||
return new PriorityQueue() | ||
} | ||
this._size = size | ||
this._slots = null | ||
this._total = null | ||
// initialize arrays to hold queue elements | ||
size = Math.max(+size | 0, 1); | ||
slots = []; | ||
for (i = 0; i < size; i += 1) { | ||
slots.push([]); | ||
size = Math.max(+size | 0, 1) | ||
this._slots = [] | ||
for (var i = 0; i < size; i += 1) { | ||
this._slots.push([]) | ||
} | ||
} | ||
// Public methods | ||
me.size = function () { | ||
var i; | ||
if (total === null) { | ||
total = 0; | ||
for (i = 0; i < size; i += 1) { | ||
total += slots[i].length; | ||
} | ||
PriorityQueue.prototype.size = function size () { | ||
if (this._total === null) { | ||
this._total = 0 | ||
for (var i = 0; i < this._size; i += 1) { | ||
this._total += this._slots[i].length | ||
} | ||
return total; | ||
}; | ||
} | ||
return this._total | ||
} | ||
me.enqueue = function (obj, priority) { | ||
var priorityOrig; | ||
PriorityQueue.prototype.enqueue = function enqueue (obj, priority) { | ||
var priorityOrig | ||
// Convert to integer with a default value of 0. | ||
priority = priority && + priority | 0 || 0; | ||
// Convert to integer with a default value of 0. | ||
priority = priority && +priority | 0 || 0 | ||
// Clear cache for total. | ||
total = null; | ||
if (priority) { | ||
priorityOrig = priority; | ||
if (priority < 0 || priority >= size) { | ||
priority = (size - 1); | ||
// put obj at the end of the line | ||
console.error("invalid priority: " + priorityOrig + " must be between 0 and " + priority); | ||
} | ||
// Clear cache for total. | ||
this._total = null | ||
if (priority) { | ||
priorityOrig = priority | ||
if (priority < 0 || priority >= this._size) { | ||
priority = (this._size - 1) | ||
// put obj at the end of the line | ||
console.error('invalid priority: ' + priorityOrig + ' must be between 0 and ' + priority) | ||
} | ||
} | ||
slots[priority].push(obj); | ||
}; | ||
this._slots[priority].push(obj) | ||
} | ||
me.dequeue = function (callback) { | ||
var obj = null, i, sl = slots.length; | ||
PriorityQueue.prototype.dequeue = function dequeue (callback) { | ||
var obj = null | ||
// Clear cache for total. | ||
this._total = null | ||
for (var i = 0, sl = this._slots.length; i < sl; i += 1) { | ||
if (this._slots[i].length) { | ||
obj = this._slots[i].shift() | ||
break | ||
} | ||
} | ||
return obj | ||
} | ||
// Clear cache for total. | ||
total = null; | ||
for (i = 0; i < sl; i += 1) { | ||
if (slots[i].length) { | ||
obj = slots[i].shift(); | ||
break; | ||
} | ||
function doWhileAsync (conditionFn, iterateFn, callbackFn) { | ||
var next = function () { | ||
if (conditionFn()) { | ||
iterateFn(next) | ||
} else { | ||
callbackFn() | ||
} | ||
return obj; | ||
}; | ||
return me; | ||
}; | ||
function doWhileAsync( conditionFn, iterateFn, callbackFn ) { | ||
var next = function() { | ||
if ( conditionFn() ) { | ||
iterateFn( next ); | ||
} else { | ||
callbackFn(); | ||
} | ||
}; | ||
next(); | ||
} | ||
next() | ||
} | ||
/** | ||
* Generate an Object pool with a specified `factory`. | ||
* | ||
* @class | ||
* @param {Object} factory | ||
@@ -118,437 +122,444 @@ * Factory to be used for generating and destorying the items. | ||
* Returns released object to head of available objects list | ||
* @returns {Object} An Object pool that works with the supplied `factory`. | ||
*/ | ||
exports.Pool = function (factory) { | ||
var me = {}, | ||
function Pool (factory) { | ||
if (!(this instanceof Pool)) { | ||
return new Pool(factory) | ||
} | ||
idleTimeoutMillis = factory.idleTimeoutMillis || 30000, | ||
reapInterval = factory.reapIntervalMillis || 1000, | ||
refreshIdle = ('refreshIdle' in factory) ? factory.refreshIdle : true, | ||
availableObjects = [], | ||
inUseObjects = [], | ||
waitingClients = new PriorityQueue(factory.priorityRange || 1), | ||
count = 0, | ||
removeIdleScheduled = false, | ||
removeIdleTimer = null, | ||
draining = false, | ||
returnToHead = factory.returnToHead || false, | ||
// Prepare a logger function. | ||
log = factory.log ? | ||
(function (str, level) { | ||
if (typeof factory.log === 'function') { | ||
factory.log(str, level); | ||
} | ||
else { | ||
console.log(level.toUpperCase() + " pool " + factory.name + " - " + str); | ||
} | ||
} | ||
) : | ||
function () {}; | ||
if ( factory.validate && factory.validateAsync ) { | ||
throw new Error( "Only one of validate or validateAsync may be specified" ); | ||
if (factory.validate && factory.validateAsync) { | ||
throw new Error('Only one of validate or validateAsync may be specified') | ||
} | ||
factory.validate = factory.validate || function() { return true; }; | ||
factory.validateAsync = factory.validateAsync; | ||
// defaults | ||
factory.idleTimeoutMillis = factory.idleTimeoutMillis || 30000 | ||
factory.returnToHead = factory.returnToHead || false | ||
factory.refreshIdle = ('refreshIdle' in factory) ? factory.refreshIdle : true | ||
factory.reapInterval = factory.reapIntervalMillis || 1000 | ||
factory.priorityRange = factory.priorityRange || 1 | ||
factory.validate = factory.validate || function () { return true } | ||
factory.max = parseInt(factory.max, 10); | ||
factory.min = parseInt(factory.min, 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(isNaN(factory.max) ? 1 : factory.max, 1) | ||
factory.min = Math.min(isNaN(factory.min) ? 0 : factory.min, factory.max - 1) | ||
/////////////// | ||
this._factory = factory | ||
this._inUseObjects = [] | ||
this._draining = false | ||
this._waitingClients = new PriorityQueue(factory.priorityRange) | ||
this._availableObjects = [] | ||
this._count = 0 | ||
this._removeIdleTimer = null | ||
this._removeIdleScheduled = false | ||
/** | ||
* 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; | ||
if (count < 0) count = 0; | ||
availableObjects = availableObjects.filter(function(objWithTimeout) { | ||
return (objWithTimeout.obj !== obj); | ||
}); | ||
// create initial resources (if factory.min > 0) | ||
this._ensureMinimum() | ||
} | ||
inUseObjects = inUseObjects.filter(function(obj) { | ||
return (obj !== obj); | ||
}); | ||
/** | ||
* logs to console or user defined log function | ||
* @private | ||
* @param {string} str | ||
* @param {string} level | ||
*/ | ||
Pool.prototype._log = function log (str, level) { | ||
if (typeof this._factory.log === 'function') { | ||
this._factory.log(str, level) | ||
} else if (this._factory.log) { | ||
console.log(level.toUpperCase() + ' pool ' + this._factory.name + ' - ' + str) | ||
} | ||
} | ||
factory.destroy(obj); | ||
/** | ||
* 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. | ||
*/ | ||
Pool.prototype.destroy = function destroy (obj) { | ||
this._count -= 1 | ||
if (this._count < 0) this._count = 0 | ||
this._availableObjects = this._availableObjects.filter(function (objWithTimeout) { | ||
return (objWithTimeout.obj !== obj) | ||
}) | ||
ensureMinimum(); | ||
}; | ||
this._inUseObjects = this._inUseObjects.filter(function (objInUse) { | ||
return (objInUse !== obj) | ||
}) | ||
/** | ||
* Checks and removes the available (idle) clients that have timed out. | ||
*/ | ||
function removeIdle() { | ||
var toRemove = [], | ||
now = new Date().getTime(), | ||
i, | ||
al, tr, | ||
timeout; | ||
this._factory.destroy(obj) | ||
removeIdleScheduled = false; | ||
this._ensureMinimum() | ||
} | ||
// Go through the available (idle) items, | ||
// check if they have timed out | ||
for (i = 0, al = availableObjects.length; i < al && (refreshIdle || (count - factory.min > toRemove.length)); i += 1) { | ||
timeout = availableObjects[i].timeout; | ||
if (now >= timeout) { | ||
// Client timed out, so destroy it. | ||
log("removeIdle() destroying obj - now:" + now + " timeout:" + timeout, 'verbose'); | ||
toRemove.push(availableObjects[i].obj); | ||
} | ||
} | ||
/** | ||
* Checks and removes the available (idle) clients that have timed out. | ||
* @private | ||
*/ | ||
Pool.prototype._removeIdle = function removeIdle () { | ||
var toRemove = [] | ||
var now = new Date().getTime() | ||
var i | ||
var al | ||
var tr | ||
var timeout | ||
for (i = 0, tr = toRemove.length; i < tr; i += 1) { | ||
me.destroy(toRemove[i]); | ||
} | ||
this._removeIdleScheduled = false | ||
// Replace the available items with the ones to keep. | ||
al = availableObjects.length; | ||
if (al > 0) { | ||
log("availableObjects.length=" + al, 'verbose'); | ||
scheduleRemoveIdle(); | ||
} else { | ||
log("removeIdle() all objects removed", 'verbose'); | ||
// Go through the available (idle) items, | ||
// check if they have timed out | ||
for (i = 0, al = this._availableObjects.length; i < al && (this._factory.refreshIdle || (this._count - this._factory.min > toRemove.length)); i += 1) { | ||
timeout = this._availableObjects[i].timeout | ||
if (now >= timeout) { | ||
// Client timed out, so destroy it. | ||
this._log('removeIdle() destroying obj - now:' + now + ' timeout:' + timeout, 'verbose') | ||
toRemove.push(this._availableObjects[i].obj) | ||
} | ||
} | ||
for (i = 0, tr = toRemove.length; i < tr; i += 1) { | ||
this.destroy(toRemove[i]) | ||
} | ||
/** | ||
* Schedule removal of idle items in the pool. | ||
* | ||
* More schedules cannot run concurrently. | ||
*/ | ||
function scheduleRemoveIdle() { | ||
if (!removeIdleScheduled) { | ||
removeIdleScheduled = true; | ||
removeIdleTimer = setTimeout(removeIdle, reapInterval); | ||
} | ||
// Replace the available items with the ones to keep. | ||
al = this._availableObjects.length | ||
if (al > 0) { | ||
this._log('this._availableObjects.length=' + al, 'verbose') | ||
this._scheduleRemoveIdle() | ||
} else { | ||
this._log('removeIdle() all objects removed', 'verbose') | ||
} | ||
} | ||
/** | ||
* Handle callbacks with either the [obj] or [err, obj] arguments in an | ||
* adaptive manner. Uses the `cb.length` property to determine the number | ||
* of arguments expected by `cb`. | ||
*/ | ||
function adjustCallback(cb, err, obj) { | ||
if (!cb) return; | ||
if (cb.length <= 1) { | ||
cb(obj); | ||
} else { | ||
cb(err, obj); | ||
} | ||
/** | ||
* Schedule removal of idle items in the pool. | ||
* | ||
* More schedules cannot run concurrently. | ||
*/ | ||
Pool.prototype._scheduleRemoveIdle = function scheduleRemoveIdle () { | ||
var self = this | ||
if (!this._removeIdleScheduled) { | ||
this._removeIdleScheduled = true | ||
this._removeIdleTimer = setTimeout(function () { | ||
self._removeIdle() | ||
}, this._factory.reapInterval) | ||
} | ||
} | ||
/** | ||
* Try to get a new client to work, and clean up pool unused (idle) items. | ||
* | ||
* - If there are available clients waiting, shift the first one out (LIFO), | ||
* and call its callback. | ||
* - If there are no waiting clients, try to create one if it won't exceed | ||
* the maximum number of clients. | ||
* - If creating a new client would exceed the maximum, add the client to | ||
* the wait list. | ||
*/ | ||
function dispense() { | ||
var obj = null, | ||
objWithTimeout = null, | ||
err = null, | ||
clientCb = null, | ||
waitingCount = waitingClients.size(); | ||
/** | ||
* Try to get a new client to work, and clean up pool unused (idle) items. | ||
* | ||
* - If there are available clients waiting, shift the first one out (LIFO), | ||
* and call its callback. | ||
* - If there are no waiting clients, try to create one if it won't exceed | ||
* the maximum number of clients. | ||
* - If creating a new client would exceed the maximum, add the client to | ||
* the wait list. | ||
* @private | ||
*/ | ||
Pool.prototype._dispense = function dispense () { | ||
var self = this | ||
var objWithTimeout = null | ||
var err = null | ||
var clientCb = null | ||
var waitingCount = this._waitingClients.size() | ||
log("dispense() clients=" + waitingCount + " available=" + availableObjects.length, 'info'); | ||
if (waitingCount > 0) { | ||
this._log('dispense() clients=' + waitingCount + ' available=' + this._availableObjects.length, 'info') | ||
if (waitingCount > 0) { | ||
if (this._factory.validateAsync) { | ||
doWhileAsync(function () { | ||
return self._availableObjects.length > 0 | ||
}, function (next) { | ||
self._log('dispense() - reusing obj', 'verbose') | ||
objWithTimeout = self._availableObjects[0] | ||
if ( factory.validateAsync ) { | ||
doWhileAsync( function() { | ||
return availableObjects.length > 0; | ||
}, function( next ) { | ||
log("dispense() - reusing obj", 'verbose'); | ||
objWithTimeout = availableObjects[0]; | ||
factory.validateAsync( objWithTimeout.obj, function( valid ) { | ||
if ( !valid ) { | ||
me.destroy(objWithTimeout.obj); | ||
next(); | ||
} else { | ||
availableObjects.shift(); | ||
clientCb = waitingClients.dequeue(); | ||
clientCb(err, objWithTimeout.obj); | ||
} | ||
} ) | ||
}, function() { | ||
if (count < factory.max) { | ||
createResource(); | ||
self._factory.validateAsync(objWithTimeout.obj, function (valid) { | ||
if (!valid) { | ||
self.destroy(objWithTimeout.obj) | ||
next() | ||
} else { | ||
self._availableObjects.shift() | ||
clientCb = self._waitingClients.dequeue() | ||
clientCb(err, objWithTimeout.obj) | ||
} | ||
} ); | ||
}) | ||
}, function () { | ||
if (self._count < self._factory.max) { | ||
self._createResource() | ||
} | ||
}) | ||
return; | ||
} | ||
return | ||
} | ||
while (availableObjects.length > 0) { | ||
log("dispense() - reusing obj", 'verbose'); | ||
objWithTimeout = availableObjects[0]; | ||
if (!factory.validate(objWithTimeout.obj)) { | ||
me.destroy(objWithTimeout.obj); | ||
continue; | ||
} | ||
availableObjects.shift(); | ||
inUseObjects.push(objWithTimeout.obj); | ||
clientCb = waitingClients.dequeue(); | ||
return clientCb(err, objWithTimeout.obj); | ||
while (this._availableObjects.length > 0) { | ||
this._log('dispense() - reusing obj', 'verbose') | ||
objWithTimeout = this._availableObjects[0] | ||
if (!this._factory.validate(objWithTimeout.obj)) { | ||
this.destroy(objWithTimeout.obj) | ||
continue | ||
} | ||
if (count < factory.max) { | ||
createResource(); | ||
} | ||
this._availableObjects.shift() | ||
this._inUseObjects.push(objWithTimeout.obj) | ||
clientCb = this._waitingClients.dequeue() | ||
return clientCb(err, objWithTimeout.obj) | ||
} | ||
if (this._count < this._factory.max) { | ||
this._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]; | ||
/** | ||
* @private | ||
*/ | ||
Pool.prototype._createResource = function _createResource () { | ||
this._count += 1 | ||
this._log('createResource() - creating obj - count=' + this._count + ' min=' + this._factory.min + ' max=' + this._factory.max, 'verbose') | ||
var self = this | ||
this._factory.create(function () { | ||
var err, obj | ||
var clientCb = self._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) { | ||
self._count -= 1 | ||
if (self._count < 0) self._count = 0 | ||
if (clientCb) { | ||
clientCb(err, obj) | ||
} | ||
if (err) { | ||
count -= 1; | ||
if (count < 0) count = 0; | ||
if (clientCb) { | ||
clientCb(err, obj); | ||
} | ||
process.nextTick(function(){ | ||
dispense(); | ||
}); | ||
process.nextTick(function () { | ||
self._dispense() | ||
}) | ||
} else { | ||
self._inUseObjects.push(obj) | ||
if (clientCb) { | ||
clientCb(err, obj) | ||
} else { | ||
inUseObjects.push(obj); | ||
if (clientCb) { | ||
clientCb(err, obj); | ||
} else { | ||
me.release(obj); | ||
} | ||
self.release(obj) | ||
} | ||
}); | ||
} | ||
} | ||
}) | ||
} | ||
function ensureMinimum() { | ||
var i, diff; | ||
if (!draining && (count < factory.min)) { | ||
diff = factory.min - count; | ||
for (i = 0; i < diff; i++) { | ||
createResource(); | ||
} | ||
/** | ||
* @private | ||
*/ | ||
Pool.prototype._ensureMinimum = function _ensureMinimum () { | ||
var i, diff | ||
if (!this._draining && (this._count < this._factory.min)) { | ||
diff = this._factory.min - this._count | ||
for (i = 0; i < diff; i++) { | ||
this._createResource() | ||
} | ||
} | ||
} | ||
/** | ||
* Request a new client. The callback will be called, | ||
* when a new client will be availabe, passing the client to it. | ||
* | ||
* @param {Function} callback | ||
* Callback function to be called after the acquire is successful. | ||
* The function will receive the acquired item as the first parameter. | ||
* | ||
* @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. | ||
* | ||
* @returns {Object} `true` if the pool is not fully utilized, `false` otherwise. | ||
*/ | ||
me.acquire = function (callback, priority) { | ||
if (draining) { | ||
throw new Error("pool is draining and cannot accept work"); | ||
} | ||
waitingClients.enqueue(callback, priority); | ||
dispense(); | ||
return (count < factory.max); | ||
}; | ||
/** | ||
* Request a new client. The callback will be called, | ||
* when a new client will be availabe, passing the client to it. | ||
* | ||
* @param {Function} callback | ||
* Callback function to be called after the acquire is successful. | ||
* The function will receive the acquired item as the first parameter. | ||
* | ||
* @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. | ||
* | ||
* @returns {boolean} `true` if the pool is not fully utilized, `false` otherwise. | ||
*/ | ||
Pool.prototype.acquire = function acquire (callback, priority) { | ||
if (this._draining) { | ||
throw new Error('pool is draining and cannot accept work') | ||
} | ||
this._waitingClients.enqueue(callback, priority) | ||
this._dispense() | ||
return (this._count < this._factory.max) | ||
} | ||
me.borrow = function (callback, priority) { | ||
log("borrow() is deprecated. use acquire() instead", 'warn'); | ||
me.acquire(callback, priority); | ||
}; | ||
/** | ||
* @deprecated | ||
*/ | ||
Pool.prototype.borrow = function borrow (callback, priority) { | ||
this._log('borrow() is deprecated. use acquire() instead', 'warn') | ||
this.acquire(callback, priority) | ||
} | ||
/** | ||
* Return the client to the pool, in case it is no longer required. | ||
* | ||
* @param {Object} obj | ||
* The acquired object to be put back to the pool. | ||
*/ | ||
me.release = function (obj) { | ||
// check to see if this object has already been released (i.e., is back in the pool of availableObjects) | ||
if (availableObjects.some(function(objWithTimeout) { return (objWithTimeout.obj === obj); })) { | ||
log("release called twice for the same resource: " + (new Error().stack), 'error'); | ||
return; | ||
} | ||
/** | ||
* Return the client to the pool, in case it is no longer required. | ||
* | ||
* @param {Object} obj | ||
* The acquired object to be put back to the pool. | ||
*/ | ||
Pool.prototype.release = function release (obj) { | ||
// check to see if this object has already been released (i.e., is back in the pool of this._availableObjects) | ||
if (this._availableObjects.some(function (objWithTimeout) { return (objWithTimeout.obj === obj) })) { | ||
this._log('release called twice for the same resource: ' + (new Error().stack), 'error') | ||
return | ||
} | ||
// check to see if this object exists in the `in use` list and remove it | ||
var index = inUseObjects.indexOf(obj); | ||
if (index < 0) { | ||
log("attempt to release an invalid resource: " + (new Error().stack), 'error'); | ||
return; | ||
} | ||
// check to see if this object exists in the `in use` list and remove it | ||
var index = this._inUseObjects.indexOf(obj) | ||
if (index < 0) { | ||
this._log('attempt to release an invalid resource: ' + (new Error().stack), 'error') | ||
return | ||
} | ||
//log("return to pool"); | ||
inUseObjects.splice(index, 1); | ||
var objWithTimeout = { obj: obj, timeout: (new Date().getTime() + idleTimeoutMillis) }; | ||
if(returnToHead){ | ||
availableObjects.splice(0, 0, objWithTimeout); | ||
} | ||
else{ | ||
availableObjects.push(objWithTimeout); | ||
} | ||
log("timeout: " + objWithTimeout.timeout, 'verbose'); | ||
dispense(); | ||
scheduleRemoveIdle(); | ||
}; | ||
// this._log("return to pool") | ||
this._inUseObjects.splice(index, 1) | ||
var objWithTimeout = { obj: obj, timeout: (new Date().getTime() + this._factory.idleTimeoutMillis) } | ||
if (this._factory.returnToHead) { | ||
this._availableObjects.splice(0, 0, objWithTimeout) | ||
} else { | ||
this._availableObjects.push(objWithTimeout) | ||
} | ||
this._log('timeout: ' + objWithTimeout.timeout, 'verbose') | ||
this._dispense() | ||
this._scheduleRemoveIdle() | ||
} | ||
me.returnToPool = function (obj) { | ||
log("returnToPool() is deprecated. use release() instead", 'warn'); | ||
me.release(obj); | ||
}; | ||
/** | ||
* @deprecated | ||
*/ | ||
Pool.prototype.returnToPool = function returnToPool (obj) { | ||
this._log('returnToPool() is deprecated. use release() instead', 'warn') | ||
this.release(obj) | ||
} | ||
/** | ||
* Disallow any new requests and let the request backlog dissapate. | ||
* | ||
* @param {Function} callback | ||
* Optional. Callback invoked when all work is done and all clients have been | ||
* released. | ||
*/ | ||
me.drain = function(callback) { | ||
log("draining", 'info'); | ||
/** | ||
* Disallow any new requests and let the request backlog dissapate. | ||
* | ||
* @param {Function} callback | ||
* Optional. Callback invoked when all work is done and all clients have been | ||
* released. | ||
*/ | ||
Pool.prototype.drain = function drain (callback) { | ||
this._log('draining', 'info') | ||
// disable the ability to put more work on the queue. | ||
draining = true; | ||
// disable the ability to put more work on the queue. | ||
this._draining = true | ||
var check = function() { | ||
if (waitingClients.size() > 0) { | ||
// wait until all client requests have been satisfied. | ||
setTimeout(check, 100); | ||
} else if (availableObjects.length != count) { | ||
// wait until all objects have been released. | ||
setTimeout(check, 100); | ||
} else { | ||
if (callback) { | ||
callback(); | ||
var self = this | ||
var check = function () { | ||
if (self._waitingClients.size() > 0) { | ||
// wait until all client requests have been satisfied. | ||
setTimeout(check, 100) | ||
} else if (self._availableObjects.length !== self._count) { | ||
// wait until all objects have been released. | ||
setTimeout(check, 100) | ||
} else if (callback) { | ||
callback() | ||
} | ||
} | ||
check() | ||
} | ||
/** | ||
* Forcibly destroys all clients regardless of timeout. Intended to be | ||
* 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. | ||
*/ | ||
Pool.prototype.destroyAllNow = function destroyAllNow (callback) { | ||
this._log('force destroying all objects', 'info') | ||
var willDie = this._availableObjects | ||
this._availableObjects = [] | ||
var obj = willDie.shift() | ||
while (obj !== null && obj !== undefined) { | ||
this.destroy(obj.obj) | ||
obj = willDie.shift() | ||
} | ||
this._removeIdleScheduled = false | ||
clearTimeout(this._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. | ||
*/ | ||
Pool.prototype.pooled = function pooled (decorated, priority) { | ||
var self = this | ||
return function () { | ||
var callerArgs = arguments | ||
var callerCallback = callerArgs[callerArgs.length - 1] | ||
var callerHasCallback = typeof callerCallback === 'function' | ||
self.acquire(function (err, client) { | ||
if (err) { | ||
if (callerHasCallback) { | ||
callerCallback(err) | ||
} | ||
return | ||
} | ||
}; | ||
check(); | ||
}; | ||
/** | ||
* Forcibly destroys all clients regardless of timeout. Intended to be | ||
* 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. | ||
*/ | ||
me.destroyAllNow = function(callback) { | ||
log("force destroying all objects", 'info'); | ||
var willDie = availableObjects; | ||
availableObjects = []; | ||
var obj = willDie.shift(); | ||
while (obj !== null && obj !== undefined) { | ||
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 () { | ||
self.release(client) | ||
if (callerHasCallback) { | ||
callerCallback.apply(null, arguments) | ||
} | ||
}) | ||
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) | ||
} | ||
} | ||
decorated.apply(null, args); | ||
}, priority); | ||
}; | ||
}; | ||
Pool.prototype.getPoolSize = function getPoolSize () { | ||
return this._count | ||
} | ||
me.getPoolSize = function() { | ||
return count; | ||
}; | ||
Pool.prototype.getName = function getName () { | ||
return this._factory.name | ||
} | ||
me.getName = function() { | ||
return factory.name; | ||
}; | ||
Pool.prototype.availableObjectsCount = function availableObjectsCount () { | ||
return this._availableObjects.length | ||
} | ||
me.availableObjectsCount = function() { | ||
return availableObjects.length; | ||
}; | ||
Pool.prototype.inUseObjectsCount = function inUseObjectsCount () { | ||
return this._inUseObjects.length | ||
} | ||
me.inUseObjectsCount = function() { | ||
return inUseObjects.length; | ||
}; | ||
Pool.prototype.waitingClientsCount = function waitingClientsCount () { | ||
return this._waitingClients.size() | ||
} | ||
me.waitingClientsCount = function() { | ||
return waitingClients.size(); | ||
}; | ||
Pool.prototype.getMaxPoolSize = function getMaxPoolSize () { | ||
return this._factory.max | ||
} | ||
me.getMaxPoolSize = function(){ | ||
return factory.max; | ||
}; | ||
Pool.prototype.getMinPoolSize = function getMinPoolSize () { | ||
return this._factory.min | ||
} | ||
// create initial resources (if factory.min > 0) | ||
ensureMinimum(); | ||
return me; | ||
}; | ||
exports.Pool = Pool |
{ | ||
"name": "generic-pool", | ||
"description": "Generic resource pooling for Node.JS", | ||
"version": "2.3.1", | ||
"version": "2.4.0", | ||
"author": "James Cooper <james@bitmechanic.com>", | ||
"contributors": [ | ||
{ "name": "James Cooper", "email": "james@bitmechanic.com" }, | ||
{ "name": "Peter Galiba", "email": "poetro@poetro.hu", "url": "http://poetro.hu/" }, | ||
{ "name": "Gary Dusbabek" }, | ||
{ "name": "Tom MacWright", "url" : "http://www.developmentseed.org/" }, | ||
{ "name": "Douglas Christopher Wilson", "email": "doug@somethingdoug.com", "url" : "http://somethingdoug.com/" }, | ||
{ "name": "calibr"}, | ||
{ "name": "Justin Robinson", "email":"jrobinson@redventures.com>"}, | ||
{ "name": "Nayana Hettiarachchi", "email":"nayana@corp-gems.com"}, | ||
{ "name": "Felipe Machado", "email": "felipou@gmail.com"} | ||
{ | ||
"name": "James Cooper", | ||
"email": "james@bitmechanic.com" | ||
}, | ||
{ | ||
"name": "Peter Galiba", | ||
"email": "poetro@poetro.hu", | ||
"url": "http://poetro.hu/" | ||
}, | ||
{ | ||
"name": "Gary Dusbabek" | ||
}, | ||
{ | ||
"name": "Tom MacWright", | ||
"url": "http://www.developmentseed.org/" | ||
}, | ||
{ | ||
"name": "Douglas Christopher Wilson", | ||
"email": "doug@somethingdoug.com", | ||
"url": "http://somethingdoug.com/" | ||
}, | ||
{ | ||
"name": "calibr" | ||
}, | ||
{ | ||
"name": "Justin Robinson", | ||
"email": "jrobinson@redventures.com>" | ||
}, | ||
{ | ||
"name": "Nayana Hettiarachchi", | ||
"email": "nayana@corp-gems.com" | ||
}, | ||
{ | ||
"name": "Felipe Machado", | ||
"email": "felipou@gmail.com" | ||
}, | ||
{ | ||
"name": "Felix Becker", | ||
"email": "felix.b@outlook.com" | ||
}, | ||
{ | ||
"name": "sandfox", | ||
"email": "james.butler@sandfox.co.uk" | ||
} | ||
], | ||
"keywords": ["pool", "pooling", "throttle"], | ||
"keywords": [ | ||
"pool", | ||
"pooling", | ||
"throttle" | ||
], | ||
"main": "lib/generic-pool.js", | ||
@@ -24,9 +63,13 @@ "repository": { | ||
"devDependencies": { | ||
"expresso": ">0.0.0" | ||
"expresso": ">0.0.0" | ||
}, | ||
"engines": { "node": ">= 0.2.0" }, | ||
"engines": { | ||
"node": ">= 0.2.0" | ||
}, | ||
"scripts": { | ||
"test": "expresso -I lib test/*.js" | ||
"lint": "eslint lib test", | ||
"lint-install": "npm install eslint@^1.10.2 eslint-config-standard@^4.4.0 eslint-plugin-standard@^1.3.1" , | ||
"test": "expresso -I lib test/*.js" | ||
}, | ||
"license": "MIT" | ||
} |
116
README.md
@@ -14,96 +14,4 @@ [![build status](https://secure.travis-ci.org/coopernurse/node-pool.png)](http://travis-ci.org/coopernurse/node-pool) | ||
2.3.1 - January 7 2016 | ||
- Documentation fixes and widened number of nodejs versions tested on travis | ||
The history has been moved to the [CHANGELOG](CHANGELOG.md) | ||
2.3.0 - January 1 2016 | ||
- Merged #105 - allow asynchronous validate functions (contributed by @felipou) | ||
2.2.2 - December 13 2015 | ||
- Merged #106 - fix condition where non "resource pool" created objects could be returned to the pool. (contributed by @devzer01) | ||
2.2.1 - October 30 2015 | ||
- Merged #104 - fix #103 - condition where pool can create > specified max number of connections (contributed by @devzer01) | ||
2.2.0 - March 26 2015 | ||
- Merged #92 - add getMaxPoolSize function (contributed by platypusMaximus) | ||
2.1.1 - July 5 2015 | ||
- fix README error about priority queueing (spotted by @kmdm) | ||
2.1.0 - June 19 2014 | ||
- Merged #72 - Add optional returnToHead flag, if true, resources are returned to head of queue (stack like | ||
behaviour) upon release (contributed by calibr), also see #68 for further discussion. | ||
2.0.4 - July 27 2013 | ||
- Merged #64 - Fix for not removing idle objects (contributed by PiotrWpl) | ||
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) | ||
- Merged #26 (log levels - contributed by JoeZ99) | ||
1.0.8 - Nov 16 2011 | ||
- Merged #21 (add getter methods to see pool size, etc. - contributed by BryanDonovan) | ||
1.0.7 - Oct 17 2011 | ||
- Merged #19 (prevent release on the same obj twice - contributed by tkrynski) | ||
- Merged #20 (acquire() returns boolean indicating whether pool is full - contributed by tilgovi) | ||
1.0.6 - May 23 2011 | ||
- Merged #13 (support error variable in acquire callback - contributed by tmcw) | ||
- Note: This change is backwards compatible. But new code should use the two | ||
parameter callback format in pool.create() functions from now on. | ||
- Merged #15 (variable scope issue in dispense() - contributed by eevans) | ||
1.0.5 - Apr 20 2011 | ||
- Merged #12 (ability to drain pool - contributed by gdusbabek) | ||
1.0.4 - Jan 25 2011 | ||
- Fixed #6 (objects reaped with undefined timeouts) | ||
- Fixed #7 (objectTimeout issue) | ||
1.0.3 - Dec 9 2010 | ||
- Added priority queueing (thanks to sylvinus) | ||
- Contributions from Poetro | ||
- Name changes to match conventions described here: http://en.wikipedia.org/wiki/Object_pool_pattern | ||
- borrow() renamed to acquire() | ||
- returnToPool() renamed to release() | ||
- destroy() removed from public interface | ||
- added JsDoc comments | ||
- Priority queueing enhancements | ||
1.0.2 - Nov 9 2010 | ||
- First NPM release | ||
## Example | ||
@@ -116,4 +24,4 @@ | ||
// 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({ | ||
var Pool = require('generic-pool').Pool; | ||
var pool = new Pool({ | ||
name : 'mysql', | ||
@@ -238,3 +146,3 @@ create : function(callback) { | ||
// borrowers can specify a priority 0 to 2 | ||
var pool = poolModule.Pool({ | ||
var pool = new Pool({ | ||
name : 'mysql', | ||
@@ -338,2 +246,6 @@ create : function(callback) { | ||
pool.getMaxPoolSize() | ||
// returns number of minimum number of resources allowed by ppol | ||
pool.getMinPoolSize() | ||
``` | ||
@@ -344,4 +256,14 @@ | ||
$ npm install expresso | ||
$ expresso -I lib test/*.js | ||
$ npm test | ||
## Linting | ||
We use eslint and the `standard` ruleset. At the moment linting is not done as part of the test suite but this will probably change in the future. You should ideally lint your code before making any PR's patches etc. | ||
Becuase the linting tools require nodejs >= `0.10` but we test against `0.8` and `0.6` installation of the tools is done via `npm run lint-install`. Some kind of optionalDevDependencies would be great! | ||
$ npm run lint-install | ||
$ npm run lint | ||
## License | ||
@@ -348,0 +270,0 @@ |
@@ -1,695 +0,700 @@ | ||
var assert = require('assert'); | ||
var poolModule = require('..'); | ||
var assert = require('assert') | ||
var poolModule = require('..') | ||
module.exports = { | ||
'expands to max limit': function (beforeExit) { | ||
var createCount = 0 | ||
var destroyCount = 0 | ||
var borrowCount = 0 | ||
'expands to max limit' : function (beforeExit) { | ||
var createCount = 0; | ||
var destroyCount = 0; | ||
var borrowCount = 0; | ||
var factory = { | ||
name: 'test1', | ||
create: function (callback) { | ||
callback(null, { count: ++createCount }) | ||
}, | ||
destroy: function (client) { destroyCount++ }, | ||
max: 2, | ||
idleTimeoutMillis: 100 | ||
} | ||
var factory = { | ||
name : 'test1', | ||
create : function(callback) { | ||
callback(null, { count: ++createCount }); | ||
}, | ||
destroy : function(client) { destroyCount++; }, | ||
max : 2, | ||
idleTimeoutMillis : 100 | ||
}; | ||
var pool = poolModule.Pool(factory) | ||
var pool = poolModule.Pool(factory); | ||
for (var i = 0; i < 10; i++) { | ||
var full = !pool.acquire(function(err, obj) { | ||
return function(err, obj) { | ||
assert.equal(typeof obj.count, 'number'); | ||
setTimeout(function() { | ||
borrowCount++; | ||
pool.release(obj); | ||
}, 100); | ||
}; | ||
}()); | ||
assert.ok((i < 1) ^ full); | ||
for (var i = 0; i < 10; i++) { | ||
var full = !pool.acquire(function () { | ||
return function (err, obj) { | ||
assert.ifError(err) | ||
assert.equal(typeof obj.count, 'number') | ||
setTimeout(function () { | ||
borrowCount++ | ||
pool.release(obj) | ||
}, 100) | ||
} | ||
}()) | ||
assert.ok((i < 1) ^ full) | ||
} | ||
beforeExit(function() { | ||
assert.equal(0, factory.min); | ||
assert.equal(2, createCount); | ||
assert.equal(2, destroyCount); | ||
assert.equal(10, borrowCount); | ||
}); | ||
}, | ||
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; | ||
'respects min limit': function (beforeExit) { | ||
var createCount = 0 | ||
var destroyCount = 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(); | ||
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); | ||
}); | ||
}, | ||
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); | ||
'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); | ||
}); | ||
}, | ||
beforeExit(function () { | ||
assert.equal(1, pool.getMaxPoolSize()) | ||
assert.equal(0, pool.getMinPoolSize()) | ||
}) | ||
}, | ||
'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); | ||
'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); | ||
}); | ||
}, | ||
beforeExit(function () { | ||
assert.equal(1, pool.getMaxPoolSize()) | ||
assert.equal(0, pool.getMinPoolSize()) | ||
}) | ||
}, | ||
'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(); | ||
'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); | ||
}); | ||
}, | ||
beforeExit(function () { | ||
assert.equal(3, pool.getMaxPoolSize()) | ||
assert.equal(2, pool.getMinPoolSize()) | ||
}) | ||
}, | ||
'supports priority on borrow' : function(beforeExit) { | ||
var borrowTimeLow = 0; | ||
var borrowTimeHigh = 0; | ||
var borrowCount = 0; | ||
var i; | ||
'supports priority on borrow': function (beforeExit) { | ||
var borrowTimeLow = 0 | ||
var borrowTimeHigh = 0 | ||
var borrowCount = 0 | ||
var i | ||
var pool = poolModule.Pool({ | ||
name : 'test2', | ||
create : function(callback) { callback(); }, | ||
destroy : function(client) { }, | ||
max : 1, | ||
idleTimeoutMillis : 100, | ||
priorityRange : 2 | ||
}); | ||
var pool = poolModule.Pool({ | ||
name: 'test2', | ||
create: function (callback) { callback() }, | ||
destroy: function (client) {}, | ||
max: 1, | ||
idleTimeoutMillis: 100, | ||
priorityRange: 2 | ||
}) | ||
for (i = 0; i < 10; i++) { | ||
pool.acquire(function(err, obj) { | ||
return function() { | ||
setTimeout(function() { | ||
var t = new Date().getTime(); | ||
if (t > borrowTimeLow) { borrowTimeLow = t; } | ||
borrowCount++; | ||
pool.release(obj); | ||
}, 50); | ||
}; | ||
}(), 1); | ||
for (i = 0; i < 10; i++) { | ||
pool.acquire((function (err, obj) { | ||
return function () { | ||
setTimeout(function () { | ||
assert.ifError(err) | ||
var t = new Date().getTime() | ||
if (t > borrowTimeLow) { borrowTimeLow = t } | ||
borrowCount++ | ||
pool.release(obj) | ||
}, 50) | ||
} | ||
}()), 1) | ||
} | ||
for (i = 0; i < 10; i++) { | ||
pool.acquire(function(obj) { | ||
return function() { | ||
setTimeout(function() { | ||
var t = new Date().getTime(); | ||
if (t > borrowTimeHigh) { borrowTimeHigh = t; } | ||
borrowCount++; | ||
pool.release(obj); | ||
}, 50); | ||
}; | ||
}(), 0); | ||
for (i = 0; i < 10; i++) { | ||
pool.acquire((function (obj) { | ||
return function () { | ||
setTimeout(function () { | ||
var t = new Date().getTime() | ||
if (t > borrowTimeHigh) { borrowTimeHigh = t } | ||
borrowCount++ | ||
pool.release(obj) | ||
}, 50) | ||
} | ||
}()), 0) | ||
} | ||
beforeExit(function() { | ||
assert.equal(20, borrowCount); | ||
assert.equal(true, borrowTimeLow > borrowTimeHigh); | ||
}); | ||
}, | ||
beforeExit(function () { | ||
assert.equal(20, borrowCount) | ||
assert.equal(true, borrowTimeLow > borrowTimeHigh) | ||
}) | ||
}, | ||
'removes correct object on reap' : function (beforeExit) { | ||
var destroyed = []; | ||
var clientCount = 0; | ||
'removes correct object on reap': function (beforeExit) { | ||
var destroyed = [] | ||
var clientCount = 0 | ||
var pool = poolModule.Pool({ | ||
name : 'test3', | ||
create : function(callback) { callback(null, { id : ++clientCount }); }, | ||
destroy : function(client) { destroyed.push(client.id); }, | ||
max : 2, | ||
idleTimeoutMillis : 100 | ||
}); | ||
var pool = poolModule.Pool({ | ||
name: 'test3', | ||
create: function (callback) { callback(null, { id: ++clientCount }) }, | ||
destroy: function (client) { destroyed.push(client.id) }, | ||
max: 2, | ||
idleTimeoutMillis: 100 | ||
}) | ||
pool.acquire(function(err, client) { | ||
assert.equal(typeof client.id, 'number'); | ||
// should be removed second | ||
setTimeout(function() { pool.release(client); }, 5); | ||
}); | ||
pool.acquire(function(err, client) { | ||
assert.equal(typeof client.id, 'number'); | ||
// should be removed first | ||
pool.release(client); | ||
}); | ||
pool.acquire(function (err, client) { | ||
assert.ifError(err) | ||
assert.equal(typeof client.id, 'number') | ||
// should be removed second | ||
setTimeout(function () { pool.release(client) }, 5) | ||
}) | ||
pool.acquire(function (err, client) { | ||
assert.ifError(err) | ||
assert.equal(typeof client.id, 'number') | ||
// should be removed first | ||
pool.release(client) | ||
}) | ||
setTimeout(function() { }, 102); | ||
setTimeout(function () {}, 102) | ||
beforeExit(function() { | ||
assert.equal(2, destroyed[0]); | ||
assert.equal(1, destroyed[1]); | ||
}); | ||
}, | ||
beforeExit(function () { | ||
assert.equal(2, destroyed[0]) | ||
assert.equal(1, destroyed[1]) | ||
}) | ||
}, | ||
'tests drain' : function (beforeExit) { | ||
var created = 0; | ||
var destroyed = 0; | ||
var count = 5; | ||
var acquired = 0; | ||
'tests drain': function (beforeExit) { | ||
var created = 0 | ||
var destroyed = 0 | ||
var count = 5 | ||
var acquired = 0 | ||
var pool = poolModule.Pool({ | ||
name : 'test4', | ||
create : function(callback) { callback(null, {id: ++created}); }, | ||
destroy : function(client) { destroyed += 1; }, | ||
max : 2, | ||
idletimeoutMillis : 300000 | ||
}); | ||
var pool = poolModule.Pool({ | ||
name: 'test4', | ||
create: function (callback) { callback(null, {id: ++created}) }, | ||
destroy: function (client) { destroyed += 1 }, | ||
max: 2, | ||
idletimeoutMillis: 300000 | ||
}) | ||
for (var i = 0; i < count; i++) { | ||
pool.acquire(function(err, client) { | ||
acquired += 1; | ||
assert.equal(typeof client.id, 'number'); | ||
setTimeout(function() { pool.release(client); }, 250); | ||
}); | ||
} | ||
for (var i = 0; i < count; i++) { | ||
pool.acquire(function (err, client) { | ||
assert.ifError(err) | ||
acquired += 1 | ||
assert.equal(typeof client.id, 'number') | ||
setTimeout(function () { pool.release(client) }, 250) | ||
}) | ||
} | ||
assert.notEqual(count, acquired); | ||
pool.drain(function() { | ||
assert.equal(count, acquired); | ||
// short circuit the absurdly long timeouts above. | ||
pool.destroyAllNow(); | ||
beforeExit(function() {}); | ||
}); | ||
assert.notEqual(count, acquired) | ||
pool.drain(function () { | ||
assert.equal(count, acquired) | ||
// short circuit the absurdly long timeouts above. | ||
pool.destroyAllNow() | ||
beforeExit(function () {}) | ||
}) | ||
// subsequent calls to acquire should return an error. | ||
assert.throws(function() { | ||
pool.acquire(function(client) {}); | ||
}, Error); | ||
}, | ||
// subsequent calls to acquire should return an error. | ||
assert.throws(function () { | ||
pool.acquire(function (client) {}) | ||
}, Error) | ||
}, | ||
'handle creation errors' : function (beforeExit) { | ||
var created = 0; | ||
var pool = poolModule.Pool({ | ||
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); | ||
}); | ||
'handle creation errors': function (beforeExit) { | ||
var created = 0 | ||
var pool = poolModule.Pool({ | ||
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) | ||
}) | ||
} | ||
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); | ||
}); | ||
}, | ||
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 for delayed creates' : function (beforeExit) { | ||
var created = 0; | ||
var pool = poolModule.Pool({ | ||
name : 'test6', | ||
create : function(callback) { | ||
if (created < 5) { | ||
setTimeout(function() { | ||
callback(new Error('Error occurred.')); | ||
}, 0); | ||
} else { | ||
setTimeout(function() { | ||
callback({ id : created }); | ||
}, 0); | ||
} | ||
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); | ||
}); | ||
'handle creation errors for delayed creates': function (beforeExit) { | ||
var created = 0 | ||
var pool = poolModule.Pool({ | ||
name: 'test6', | ||
create: function (callback) { | ||
if (created < 5) { | ||
setTimeout(function () { | ||
callback(new Error('Error occurred.')) | ||
}, 0) | ||
} else { | ||
setTimeout(function () { | ||
callback({ id: created }) | ||
}, 0) | ||
} | ||
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); | ||
}); | ||
}, | ||
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) | ||
}) | ||
} | ||
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 | ||
}); | ||
'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(); | ||
}); | ||
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; | ||
assert.equal(pool.getPoolSize(), 0) | ||
assertion_count += 1 | ||
pooledFn(function(err) { | ||
if (err) { throw err; } | ||
assert.ok(true); | ||
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); | ||
}); | ||
}, | ||
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 | ||
}); | ||
'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"); | ||
}); | ||
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; | ||
}); | ||
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); | ||
}); | ||
}, | ||
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 | ||
}); | ||
'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(); | ||
}); | ||
var pooledFn = pool.pooled(function (client, arg, cb) { | ||
assert.equal(arg, 'Arg!') | ||
assertion_count += 1 | ||
cb() | ||
}) | ||
pooledFn("Arg!"); | ||
pooledFn('Arg!') | ||
beforeExit(function() { | ||
assert.equal(pool.getPoolSize(), 0); | ||
assert.equal(assertion_count, 1); | ||
}); | ||
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 | ||
}) | ||
'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") | ||
}) | ||
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 | ||
}) | ||
pooledFn(function(err, obj) { | ||
assert.equal(err.message, 'Pool error'); | ||
assertion_count += 1; | ||
}); | ||
beforeExit(function () { | ||
assert.equal(assertion_count, 1) | ||
}) | ||
}, | ||
beforeExit(function() { | ||
assert.equal(assertion_count, 1); | ||
}); | ||
}, | ||
'getPoolSize': 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: 2, | ||
idleTimeoutMillis: 100 | ||
}) | ||
'getPoolSize' : 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 : 2, | ||
idleTimeoutMillis : 100 | ||
}); | ||
assert.equal(pool.getPoolSize(), 0) | ||
assertion_count += 1 | ||
pool.acquire(function (err, obj1) { | ||
if (err) { throw err } | ||
assert.equal(pool.getPoolSize(), 1) | ||
assertion_count += 1 | ||
pool.acquire(function (err, obj2) { | ||
if (err) { throw err } | ||
assert.equal(pool.getPoolSize(), 2) | ||
assertion_count += 1 | ||
assert.equal(pool.getPoolSize(), 0); | ||
assertion_count += 1; | ||
pool.acquire(function(err, obj1) { | ||
if (err) { throw err; } | ||
assert.equal(pool.getPoolSize(), 1); | ||
assertion_count += 1; | ||
pool.acquire(function(err, obj2) { | ||
if (err) { throw err; } | ||
assert.equal(pool.getPoolSize(), 2); | ||
assertion_count += 1; | ||
pool.release(obj1) | ||
pool.release(obj2) | ||
pool.release(obj1); | ||
pool.release(obj2); | ||
pool.acquire(function (err, obj3) { | ||
if (err) { throw err } | ||
// should still be 2 | ||
assert.equal(pool.getPoolSize(), 2) | ||
assertion_count += 1 | ||
pool.release(obj3) | ||
}) | ||
}) | ||
}) | ||
pool.acquire(function(err, obj3) { | ||
if (err) { throw err; } | ||
// should still be 2 | ||
assert.equal(pool.getPoolSize(), 2); | ||
assertion_count += 1; | ||
pool.release(obj3); | ||
}); | ||
}); | ||
}); | ||
beforeExit(function () { | ||
assert.equal(assertion_count, 4) | ||
}) | ||
}, | ||
beforeExit(function() { | ||
assert.equal(assertion_count, 4); | ||
}); | ||
}, | ||
'availableObjectsCount': 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: 2, | ||
idleTimeoutMillis: 100 | ||
}) | ||
'availableObjectsCount' : 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 : 2, | ||
idleTimeoutMillis : 100 | ||
}); | ||
assert.equal(pool.availableObjectsCount(), 0) | ||
assertion_count += 1 | ||
pool.acquire(function (err, obj1) { | ||
if (err) { throw err } | ||
assert.equal(pool.availableObjectsCount(), 0) | ||
assertion_count += 1 | ||
assert.equal(pool.availableObjectsCount(), 0); | ||
assertion_count += 1; | ||
pool.acquire(function(err, obj1) { | ||
if (err) { throw err; } | ||
assert.equal(pool.availableObjectsCount(), 0); | ||
assertion_count += 1; | ||
pool.acquire(function (err, obj2) { | ||
if (err) { throw err } | ||
assert.equal(pool.availableObjectsCount(), 0) | ||
assertion_count += 1 | ||
pool.acquire(function(err, obj2) { | ||
if (err) { throw err; } | ||
assert.equal(pool.availableObjectsCount(), 0); | ||
assertion_count += 1; | ||
pool.release(obj1) | ||
assert.equal(pool.availableObjectsCount(), 1) | ||
assertion_count += 1 | ||
pool.release(obj1); | ||
assert.equal(pool.availableObjectsCount(), 1); | ||
assertion_count += 1; | ||
pool.release(obj2) | ||
assert.equal(pool.availableObjectsCount(), 2) | ||
assertion_count += 1 | ||
pool.release(obj2); | ||
assert.equal(pool.availableObjectsCount(), 2); | ||
assertion_count += 1; | ||
pool.acquire(function (err, obj3) { | ||
if (err) { throw err } | ||
assert.equal(pool.availableObjectsCount(), 1) | ||
assertion_count += 1 | ||
pool.release(obj3) | ||
pool.acquire(function(err, obj3) { | ||
if (err) { throw err; } | ||
assert.equal(pool.availableObjectsCount(), 1); | ||
assertion_count += 1; | ||
pool.release(obj3); | ||
assert.equal(pool.availableObjectsCount(), 2) | ||
assertion_count += 1 | ||
}) | ||
}) | ||
}) | ||
assert.equal(pool.availableObjectsCount(), 2); | ||
assertion_count += 1; | ||
}); | ||
}); | ||
}); | ||
beforeExit(function () { | ||
assert.equal(assertion_count, 7) | ||
}) | ||
}, | ||
beforeExit(function() { | ||
assert.equal(assertion_count, 7); | ||
}); | ||
}, | ||
'logPassesLogLevel': function (beforeExit) { | ||
var loglevels = {'verbose': 0, 'info': 1, 'warn': 2, 'error': 3} | ||
var logmessages = {verbose: [], info: [], warn: [], error: []} | ||
var factory = { | ||
name: 'test1', | ||
create: function (callback) { callback(null, {id: Math.floor(Math.random() * 1000)}) }, | ||
destroy: function (client) {}, | ||
max: 2, | ||
idleTimeoutMillis: 100, | ||
log: function (msg, level) { testlog(msg, level) } | ||
} | ||
var testlog = function (msg, level) { | ||
assert.ok(level in loglevels) | ||
logmessages[level].push(msg) | ||
} | ||
var pool = poolModule.Pool(factory) | ||
'logPassesLogLevel': function(beforeExit){ | ||
var loglevels = {'verbose':0, 'info':1, 'warn':2, 'error':3}; | ||
var logmessages = {verbose:[], info:[], warn:[], error:[]}; | ||
var factory = { | ||
name : 'test1', | ||
create : function(callback) {callback(null, {id:Math.floor(Math.random()*1000)}); }, | ||
destroy : function(client) {}, | ||
max : 2, | ||
idleTimeoutMillis: 100, | ||
log : function(msg, level) {testlog(msg, level);} | ||
}; | ||
var testlog = function(msg, level){ | ||
assert.ok(level in loglevels); | ||
logmessages[level].push(msg); | ||
}; | ||
var pool = poolModule.Pool(factory); | ||
var pool2 = poolModule.Pool({ | ||
name: 'testNoLog', | ||
create: function (callback) { callback(null, {id: Math.floor(Math.random() * 1000)}) }, | ||
destroy: function (client) {}, | ||
max: 2, | ||
idleTimeoutMillis: 100 | ||
}) | ||
assert.equal(pool2.getName(), 'testNoLog') | ||
var pool2 = poolModule.Pool({ | ||
name : 'testNoLog', | ||
create : function(callback) {callback(null, {id:Math.floor(Math.random()*1000)}); }, | ||
destroy : function(client) {}, | ||
max : 2, | ||
idleTimeoutMillis: 100 | ||
}); | ||
assert.equal(pool2.getName(), 'testNoLog'); | ||
pool.acquire(function (err, obj) { | ||
assert.ifError(err) | ||
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 = [] | ||
pool2.borrow(function (err, obj) { | ||
assert.ifError(err) | ||
assert.equal(logmessages.info.length, 0) | ||
assert.equal(logmessages.verbose.length, 0) | ||
assert.equal(logmessages.warn.length, 0) | ||
}) | ||
}) | ||
}, | ||
pool.acquire(function(err, obj){ | ||
if (err) {throw err;} | ||
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 = []; | ||
pool2.borrow(function(err, obj){ | ||
assert.equal(logmessages.info.length, 0); | ||
assert.equal(logmessages.verbose.length, 0); | ||
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 | ||
} | ||
'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) { | ||
assert.ifError(err) | ||
pool.destroy(obj) | ||
}) | ||
assert.equal(destroyCalled, true) | ||
assert.equal(pool.availableObjectsCount(), 0) | ||
}, | ||
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 | ||
var validateCalled = false | ||
var 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 | ||
} | ||
'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) { | ||
assert.ifError(err) | ||
pool.release(obj) | ||
assert.equal(obj.count, 0) | ||
var pool = poolModule.Pool(factory); | ||
pool.acquire(function(err, obj){ | ||
pool.release(obj); | ||
assert.equal(obj.count, 0); | ||
pool.acquire(function (err, obj) { | ||
assert.ifError(err) | ||
pool.release(obj) | ||
assert.equal(obj.count, 1) | ||
}) | ||
}) | ||
assert.equal(validateCalled, true) | ||
assert.equal(destroyCalled, 0) | ||
assert.equal(pool.availableObjectsCount(), 1) | ||
}, | ||
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); | ||
}, | ||
'removes from available objects on async validation failure': function (beforeExit) { | ||
var destroyCalled = false | ||
var validateCalled = false | ||
var count = 0 | ||
var factory = { | ||
name: 'test', | ||
create: function (callback) { callback(null, {count: count++}) }, | ||
destroy: function (client) { destroyCalled = client.count }, | ||
validateAsync: function (client, callback) { validateCalled = true; callback(client.count > 0) }, | ||
max: 2, | ||
idleTimeoutMillis: 100 | ||
} | ||
'removes from available objects on async 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; }, | ||
validateAsync: function(client, callback) {validateCalled = true; callback( client.count != 0 );}, | ||
max: 2, | ||
idleTimeoutMillis: 100 | ||
}; | ||
var pool = poolModule.Pool(factory) | ||
pool.acquire(function (err, obj) { | ||
assert.ifError(err) | ||
pool.release(obj) | ||
assert.equal(obj.count, 0) | ||
var pool = poolModule.Pool(factory); | ||
pool.acquire(function(err, obj){ | ||
pool.release(obj); | ||
assert.equal(obj.count, 0); | ||
pool.acquire(function (err, obj) { | ||
assert.ifError(err) | ||
pool.release(obj) | ||
assert.equal(obj.count, 1) | ||
}) | ||
}) | ||
assert.equal(validateCalled, true) | ||
assert.equal(destroyCalled, 0) | ||
assert.equal(pool.availableObjectsCount(), 1) | ||
}, | ||
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); | ||
}, | ||
'error on setting both validate functions': function (beforeExit) { | ||
var noop = function () {} | ||
var factory = { | ||
name: 'test', | ||
create: noop, | ||
destroy: noop, | ||
validate: noop, | ||
validateAsync: noop | ||
} | ||
'error on setting both validate functions': 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; }, | ||
validateAsync: function(client, callback) {validateCalled = true; callback( client.count != 0 );}, | ||
max: 2, | ||
idleTimeoutMillis: 100 | ||
}; | ||
try { | ||
poolModule.Pool(factory) | ||
} catch (err) { | ||
assert.equal(err.message, 'Only one of validate or validateAsync may be specified') | ||
} | ||
}, | ||
try { | ||
var pool = poolModule.Pool(factory); | ||
} catch ( err ) { | ||
assert.equal(err.message, "Only one of validate or validateAsync may be specified"); | ||
} | ||
}, | ||
'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 | ||
} | ||
'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) | ||
}) | ||
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) | ||
}) | ||
}, | ||
beforeExit(function() { | ||
assert.equal(getFlag, 1); | ||
}); | ||
}, | ||
'returns only valid object to the pool': function (beforeExit) { | ||
var pool = poolModule.Pool({ | ||
name: 'test', | ||
create: function (callback) { | ||
process.nextTick(function () { | ||
callback(null, { id: 'validId' }) | ||
}) | ||
}, | ||
destroy: function (client) {}, | ||
max: 1, | ||
idleTimeoutMillis: 100 | ||
}) | ||
'returns only valid object to the pool': function(beforeExit){ | ||
var pool = poolModule.Pool({ | ||
name: 'test', | ||
create: function(callback) { | ||
process.nextTick(function(){ | ||
callback(null, { id: 'validId' }); | ||
}); | ||
}, | ||
destroy: function(client) {}, | ||
max: 1, | ||
idleTimeoutMillis: 100 | ||
}); | ||
pool.acquire(function (err, obj) { | ||
assert.ifError(err) | ||
assert.equal(pool.availableObjectsCount(), 0) | ||
assert.equal(pool.inUseObjectsCount(), 1) | ||
pool.acquire(function(err, obj){ | ||
assert.equal(pool.availableObjectsCount(), 0); | ||
assert.equal(pool.inUseObjectsCount(), 1); | ||
// Invalid release | ||
pool.release({}) | ||
assert.equal(pool.availableObjectsCount(), 0) | ||
assert.equal(pool.inUseObjectsCount(), 1) | ||
// Invalid release | ||
pool.release({}); | ||
assert.equal(pool.availableObjectsCount(), 0); | ||
assert.equal(pool.inUseObjectsCount(), 1); | ||
// Valid release | ||
pool.release(obj); | ||
assert.equal(pool.availableObjectsCount(), 1); | ||
assert.equal(pool.inUseObjectsCount(), 0); | ||
}); | ||
} | ||
}; | ||
// Valid release | ||
pool.release(obj) | ||
assert.equal(pool.availableObjectsCount(), 1) | ||
assert.equal(pool.inUseObjectsCount(), 0) | ||
}) | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
10
1131
54862
288
1