Comparing version 0.1.0 to 0.1.1
132
index.js
var Promise = require('bluebird'); | ||
var RECURSION_LIMIT = 100; | ||
@@ -26,16 +27,16 @@ function Tarn(opt) { | ||
if (!checkOptionalTime(opt.acquireTimeoutMs)) { | ||
throw new Error('Tarn: invalid opt.acquireTimeoutMs ' + JSON.stringify(opt.acquireTimeoutMs)); | ||
if (!checkOptionalTime(opt.acquireTimeoutMillis)) { | ||
throw new Error('Tarn: invalid opt.acquireTimeoutMillis ' + JSON.stringify(opt.acquireTimeoutMillis)); | ||
} | ||
if (!checkOptionalTime(opt.createTimeoutMs)) { | ||
throw new Error('Tarn: invalid opt.createTimeoutMs ' + JSON.stringify(opt.createTimeoutMs)); | ||
if (!checkOptionalTime(opt.createTimeoutMillis)) { | ||
throw new Error('Tarn: invalid opt.createTimeoutMillis ' + JSON.stringify(opt.createTimeoutMillis)); | ||
} | ||
if (!checkOptionalTime(opt.idleTimeoutMs)) { | ||
throw new Error('Tarn: invalid opt.idleTimeoutMs ' + JSON.stringify(opt.idleTimeoutMs)); | ||
if (!checkOptionalTime(opt.idleTimeoutMillis)) { | ||
throw new Error('Tarn: invalid opt.idleTimeoutMillis ' + JSON.stringify(opt.idleTimeoutMillis)); | ||
} | ||
if (!checkOptionalTime(opt.reapIntervalMs)) { | ||
throw new Error('Tarn: invalid opt.reapIntervalMs ' + JSON.stringify(opt.reapIntervalMs)); | ||
if (!checkOptionalTime(opt.reapIntervalMillis)) { | ||
throw new Error('Tarn: invalid opt.reapIntervalMillis ' + JSON.stringify(opt.reapIntervalMillis)); | ||
} | ||
@@ -45,8 +46,9 @@ | ||
this.destroyer = opt.destroy; | ||
this.validate = typeof opt.validate === 'function' ? opt.validate : function () { return true; }; | ||
this.log = opt.log || function () {}; | ||
this.acquireTimeoutMs = opt.acquireTimeoutMs || 30000; | ||
this.createTimeoutMs = opt.createTimeoutMs || 30000; | ||
this.idleTimeoutMs = opt.idleTimeoutMs || 30000; | ||
this.reapIntervalMs = opt.reapIntervalMs || 1000; | ||
this.acquireTimeoutMillis = opt.acquireTimeoutMillis || 30000; | ||
this.createTimeoutMillis = opt.createTimeoutMillis || 30000; | ||
this.idleTimeoutMillis = opt.idleTimeoutMillis || 30000; | ||
this.reapIntervalMillis = opt.reapIntervalMillis || 1000; | ||
@@ -65,3 +67,3 @@ this.min = opt.min; | ||
self.check(); | ||
}, this.reapIntervalMs); | ||
}, this.reapIntervalMillis); | ||
} | ||
@@ -88,3 +90,3 @@ | ||
var pendingAcquire = new PendingOperation(this.acquireTimeoutMs); | ||
var pendingAcquire = new PendingOperation(this.acquireTimeoutMillis); | ||
this.pendingAcquires.push(pendingAcquire); | ||
@@ -97,3 +99,3 @@ | ||
this._tryAcquireNext(); | ||
this._tryAcquireNext(0); | ||
return pendingAcquire; | ||
@@ -109,3 +111,3 @@ }; | ||
this.free.push(new FreeResource(used.resource)); | ||
this._tryAcquireNext(); | ||
this._tryAcquireNext(0); | ||
return true; | ||
@@ -128,3 +130,3 @@ } | ||
if (duration(timestamp, free.timestamp) > this.idleTimeoutMs && numDestroyed < maxDestroy) { | ||
if (duration(timestamp, free.timestamp) > this.idleTimeoutMillis && numDestroyed < maxDestroy) { | ||
numDestroyed++; | ||
@@ -144,4 +146,6 @@ this._destroy(free.resource); | ||
Tarn.prototype._tryAcquireNext = function () { | ||
if (this.used.length >= this.max || this.pendingAcquires.length === 0) { | ||
Tarn.prototype._tryAcquireNext = function (recursion) { | ||
recursion = (recursion || 0) + 1; | ||
if (this.used.length >= this.max || this.pendingAcquires.length === 0 || recursion > RECURSION_LIMIT) { | ||
// Nothing to do. | ||
@@ -152,3 +156,3 @@ return; | ||
if (this.free.length > 0) { | ||
this._acquireNext(); | ||
this._acquireNext(recursion); | ||
} else if (this.used.length + this.pendingCreates.length < this.max && this.pendingCreates.length < this.pendingAcquires.length) { | ||
@@ -158,5 +162,5 @@ var self = this; | ||
this._create().promise.then(function () { | ||
self._tryAcquireNext(); | ||
self._tryAcquireNext(recursion); | ||
}).catch(function (err) { | ||
self.log('Tarn: resource creator threw an exception', err.stack); | ||
self.log('Tarn: resource creator threw an exception ' + err.stack, 'warn'); | ||
}); | ||
@@ -166,13 +170,29 @@ } | ||
Tarn.prototype._acquireNext = function () { | ||
Tarn.prototype._acquireNext = function (recursion) { | ||
while (this.free.length > 0 && this.pendingAcquires.length > 0) { | ||
var pendingAcquire = this.pendingAcquires.shift(); | ||
var pendingAcquire = this.pendingAcquires[0]; | ||
var free = this.free[0]; | ||
if (!pendingAcquire.isRejected()) { | ||
var free = this.free.shift(); | ||
if (pendingAcquire.isRejected()) { | ||
this.pendingAcquires.shift(); | ||
continue; | ||
} | ||
this.used.push(new UsedResource(free.resource)); | ||
pendingAcquire._resolve(free.resource); | ||
if (!this.validate(free.resource)) { | ||
this.free.shift(); | ||
this._destroy(free.resource); | ||
continue; | ||
} | ||
this.pendingAcquires.shift(); | ||
this.free.shift(); | ||
this.used.push(new UsedResource(free.resource)); | ||
pendingAcquire._resolve(free.resource); | ||
} | ||
// If we destroyed invalid resources, we may need to create new ones. | ||
if (this.pendingAcquires.length > this.pendingCreates.length) { | ||
this._tryAcquireNext(recursion); | ||
} | ||
}; | ||
@@ -183,27 +203,21 @@ | ||
var pendingCreate = new PendingOperation(this.createTimeoutMs); | ||
var pendingCreate = new PendingOperation(this.createTimeoutMillis); | ||
this.pendingCreates.push(pendingCreate); | ||
// nextTick is needed to make sure the creation happens asynchronously. | ||
try { | ||
self.creator(function (err, resource) { | ||
remove(self.pendingCreates, pendingCreate); | ||
callbackOrPromise(self.creator, []).then(function (resource) { | ||
remove(self.pendingCreates, pendingCreate); | ||
if (err) { | ||
pendingCreate._reject(err); | ||
} else { | ||
if (pendingCreate.isRejected()) { | ||
// This happens if the pending operation times out or is aborted. In any case, | ||
// we need to destroy the resource since no-one will ever use it. | ||
this._destroy(resource); | ||
} else { | ||
self.free.push(new FreeResource(resource)); | ||
pendingCreate._resolve(resource); | ||
} | ||
} | ||
}); | ||
} catch (err) { | ||
if (pendingCreate.isRejected()) { | ||
// This happens if the pending operation times out or is aborted. In any case, | ||
// we need to destroy the resource since no-one will ever use it. | ||
this._destroy(resource); | ||
} else { | ||
self.free.push(new FreeResource(resource)); | ||
pendingCreate._resolve(resource); | ||
} | ||
}).catch(function (err) { | ||
remove(self.pendingCreates, pendingCreate); | ||
pendingCreate._reject(err); | ||
} | ||
}); | ||
@@ -217,3 +231,3 @@ return pendingCreate; | ||
} catch (err) { | ||
this.log('Tarn: resource destroyer threw an exception', err.stack); | ||
this.log('Tarn: resource destroyer threw an exception ' + err.stack, 'warn'); | ||
} | ||
@@ -283,4 +297,24 @@ }; | ||
function callbackOrPromise(func, args) { | ||
return new Promise(function (resolve, reject) { | ||
args.push(function (err, resource) { | ||
if (err) { | ||
reject(err); | ||
} else { | ||
resolve(resource); | ||
} | ||
}); | ||
Promise.try(function () { | ||
return func.apply(undefined, args); | ||
}).then(function (res) { | ||
if (res) { | ||
resolve(res); | ||
} | ||
}).catch(reject); | ||
}); | ||
} | ||
module.exports = { | ||
Tarn: Tarn | ||
}; |
{ | ||
"name": "tarn", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"description": "Simple and robust resource pool for node.js", | ||
@@ -8,3 +8,4 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "mocha --slow 10 --timeout 5000 --reporter spec tests.js" | ||
"test": "mocha --slow 10 --timeout 5000 --reporter spec tests.js", | ||
"test-bail": "mocha --slow 10 --timeout 5000 --reporter spec --bail tests.js" | ||
}, | ||
@@ -11,0 +12,0 @@ "author": { |
[![Build Status](https://travis-ci.org/Vincit/tarn.js.svg?branch=master)](https://travis-ci.org/Vincit/tarn.js) | ||
## Why yet another pool for node? | ||
## Why yet another resource pool? | ||
Tarn is focused in robustness and ability to recover from errors. Tarn has timeouts for all operations | ||
Tarn is focused on robustness and ability to recover from errors. Tarn has timeouts for all operations | ||
that can fail or timeout so that you should never end up with pool full of crap. Tarn has a comprehensive | ||
@@ -24,3 +24,5 @@ test suite and we are committed to adding tests and fixing all bugs that are found. | ||
// function that creates a resource | ||
// function that creates a resource. You can either pass the resource | ||
// to the callback or return a promise that resolves the resource | ||
// (but not both). | ||
create: (cb) => { | ||
@@ -30,3 +32,11 @@ cb(null, new SomeResource()); | ||
// function that destroys a resource | ||
// validates a connection before it is used. Return true or false | ||
// from it. If false is returned, the resource is destroyed and a | ||
// another one is acquired. | ||
validate: (resource) { | ||
return true; | ||
}, | ||
// function that destroys a resource. This is always synchronous | ||
// as nothing waits for the return value. | ||
destroy: (someResource) => { | ||
@@ -44,13 +54,13 @@ someResource.cleanup(); | ||
// if a resource cannot be acquired | ||
acquireTimeoutMs: 30000, | ||
acquireTimeoutMillis: 30000, | ||
// create operations are cancelled after this many milliseconds | ||
// if a resource cannot be acquired | ||
createTimeoutMs: 30000, | ||
createTimeoutMillis: 30000, | ||
// free resouces are destroyed after this many milliseconds | ||
idleTimeoutMs: 30000, | ||
idleTimeoutMillis: 30000, | ||
// how often to check for idle resources to destroy | ||
reapIntervalMs: 1000 | ||
reapIntervalMillis: 1000 | ||
}); | ||
@@ -57,0 +67,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
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
11727
240
83