generic-pool
Advanced tools
Comparing version 3.0.0-alpha.8 to 3.0.0-alpha.9
'use strict' | ||
const DoublyLinkedList = require('./DoublyLinkedList') | ||
const DLLArrayIterator = require('./DLLArrayIterator') | ||
/** | ||
@@ -12,3 +12,2 @@ * DoublyLinkedList backed array | ||
this._list = new DoublyLinkedList() | ||
this._length = 0 | ||
} | ||
@@ -28,3 +27,2 @@ | ||
this._length-- | ||
return node.data | ||
@@ -42,7 +40,6 @@ } | ||
this._list.insertBeginning(node) | ||
this._length++ | ||
} | ||
/** | ||
* adds one to the end of an array | ||
* adds one to the end of an array | ||
* @param {[type]} element [description] | ||
@@ -55,42 +52,18 @@ * @return {[type]} [description] | ||
this._list.insertEnd(node) | ||
this._length++ | ||
} | ||
// | ||
/** | ||
* Not quite compliant forEach impl - | ||
* maybe move to an iterator? | ||
* @param {Function} callback only given currentValue and index | ||
* @param {[type]} thisArg [description] | ||
*/ | ||
forEach (callback, thisArg) { | ||
const len = this._length | ||
let k = 0 | ||
const T = (arguments.length > 1) ? thisArg : undefined | ||
[Symbol.iterator] () { | ||
return new DLLArrayIterator(this._list) | ||
} | ||
if (this === null) { | ||
throw new TypeError('this is null or not defined') | ||
} | ||
iterator () { | ||
return new DLLArrayIterator(this._list) | ||
} | ||
if (typeof callback !== 'function') { | ||
throw new TypeError(callback + ' is not a function') | ||
} | ||
// if nothing in array just bail | ||
if (len <= k) { | ||
return | ||
} | ||
let currentNode = this._list.head | ||
while (k < len) { | ||
const kValue = currentNode.data | ||
callback.call(T, kValue, k) | ||
k++ | ||
currentNode = currentNode.next | ||
} | ||
reverseIterator () { | ||
return new DLLArrayIterator(this._list, true) | ||
} | ||
get length () { | ||
return this._length | ||
return this._list.length | ||
} | ||
@@ -97,0 +70,0 @@ } |
@@ -21,2 +21,3 @@ 'use strict' | ||
this.tail = null | ||
this.length = 0 | ||
} | ||
@@ -30,2 +31,3 @@ | ||
node.next = null | ||
this.length++ | ||
} else { | ||
@@ -53,2 +55,3 @@ this.insertBefore(this.head, node) | ||
node.next = newNode | ||
this.length++ | ||
} | ||
@@ -65,2 +68,3 @@ | ||
node.prev = newNode | ||
this.length++ | ||
} | ||
@@ -81,2 +85,3 @@ | ||
node.next = null | ||
this.length-- | ||
} | ||
@@ -83,0 +88,0 @@ |
123
lib/Pool.js
@@ -12,2 +12,3 @@ 'use strict' | ||
const DLLArray = require('./DLLArray') | ||
const DefaultEvictor = require('./DefaultEvictor') | ||
@@ -105,5 +106,16 @@ /** | ||
this._removeIdleTimer = null | ||
this._removeIdleScheduled = false | ||
/** | ||
* Infinitely looping iterator over available object | ||
* @type {DLLArrayIterator} | ||
*/ | ||
this._evictionIterator = this._availableObjects.iterator() | ||
this._evictor = new DefaultEvictor() | ||
/** | ||
* handle for setTimeout for next eviction run | ||
* @type {[type]} | ||
*/ | ||
this._scheduledEviction = null | ||
// create initial resources (if factory.min > 0) | ||
@@ -137,46 +149,2 @@ if (this._config.autostart === true) { | ||
/** | ||
* Checks and removes the available (idle) resources that have timed out. | ||
* @private | ||
*/ | ||
_removeIdle () { | ||
const toRemove = [] | ||
const now = Date.now() | ||
this._removeIdleScheduled = false | ||
// Go through the available (idle) items, | ||
// check if they have timed out | ||
for (let i = 0, al = this._availableObjects.length; i < al && (this._config.refreshIdle && (this._count - this._config.min > toRemove.length)); i += 1) { | ||
const idletime = now - this._availableObjects[i].lastReturnTime | ||
if (idletime >= this._config.idleTimeoutMillis) { | ||
// Client timed out, so destroy it. | ||
toRemove.push(this._availableObjects[i].obj) | ||
} | ||
} | ||
for (let j = 0, tr = toRemove.length; j < tr; j += 1) { | ||
this._destroy(toRemove[j]) | ||
} | ||
// Replace the available items with the ones to keep. | ||
const al = this._availableObjects.length | ||
if (al > 0) { | ||
this._scheduleRemoveIdle() | ||
} | ||
} | ||
/** | ||
* Schedule removal of idle items in the pool. | ||
* | ||
* More schedules cannot run concurrently. | ||
*/ | ||
_scheduleRemoveIdle () { | ||
if (!this._removeIdleScheduled) { | ||
this._removeIdleScheduled = true | ||
this._removeIdleTimer = setTimeout(() => this._removeIdle(), this._config.reapInterval) | ||
} | ||
} | ||
/** | ||
* Attempt to move an available resource into test and then onto a waiting client | ||
@@ -315,3 +283,2 @@ * @return {Boolean} could we move an available resource into test | ||
// or were somehow fulfilled. put our pooledResource back. | ||
pooledResource.idle() | ||
this._addPooledResourceToAvailableObjects(pooledResource) | ||
@@ -372,2 +339,53 @@ // TODO: do need to trigger anything before we leave? | ||
_evict () { | ||
const testsToRun = Math.min(this._config.numTestsPerEvictionRun, this._availableObjects.length) | ||
const evictionConfig = { | ||
softIdleTimeoutMillis: this._config.softIdleTimeoutMillis, | ||
idleTimeoutMillis: this._config.idleTimeoutMillis, | ||
min: this._config.min | ||
} | ||
for (let testsHaveRun = 0; testsHaveRun < testsToRun;) { | ||
const iterationResult = this._evictionIterator.next() | ||
// Safety check incase we could get stuck in infinite loop because we | ||
// somehow emptied the array after chekcing it's length | ||
if (iterationResult.done === true && this._availableObjects.length < 1) { | ||
this._evictionIterator.reset() | ||
return | ||
} | ||
// if this happens it should just mean we reached the end of the | ||
// list and can reset the cursor. | ||
if (iterationResult.done === true && this._availableObjects.length > 0) { | ||
this._evictionIterator.reset() | ||
break | ||
} | ||
const resource = iterationResult.value | ||
const shouldEvict = this._evictor.evict(evictionConfig, resource, this._availableObjects.length) | ||
testsHaveRun++ | ||
if (shouldEvict === true) { | ||
// take it out of the _availableObjects list | ||
this._evictionIterator.remove() | ||
this._destroy(resource) | ||
} | ||
} | ||
} | ||
_scheduleEvictorRun () { | ||
// Start eviction if set | ||
if (this._config.evictionRunIntervalMillis > 0) { | ||
this._scheduledEviction = setTimeout(() => { | ||
this.evict() | ||
this._scheduleEvictorRun() | ||
}, this._config.evictionRunIntervalMillis) | ||
} | ||
} | ||
_descheduleEvictorRun () { | ||
clearTimeout(this._scheduledEviction) | ||
this._scheduledEviction = null | ||
} | ||
start () { | ||
@@ -381,2 +399,3 @@ if (this._draining === true) { | ||
this._started = true | ||
this._scheduleEvictorRun() | ||
this._ensureMinimum() | ||
@@ -441,3 +460,2 @@ } | ||
this._scheduleRemoveIdle() | ||
this._dispense() | ||
@@ -473,4 +491,4 @@ return this._Promise.resolve() | ||
// FIXME: replace _availableObjects with a queue/stack impl that both have same interface | ||
_addPooledResourceToAvailableObjects (pooledResource) { | ||
pooledResource.idle() | ||
if (this._config.fifo === true) { | ||
@@ -527,5 +545,6 @@ this._availableObjects.push(pooledResource) | ||
clear () { | ||
this._removeIdleScheduled = false | ||
clearTimeout(this._removeIdleTimer) | ||
this._availableObjects.forEach(this._destroy, this) | ||
this._descheduleEvictorRun() | ||
for (const resource of this._availableObjects) { | ||
this._destroy(resource) | ||
} | ||
return this._Promise.all(this._factoryDestroyOperations) | ||
@@ -532,0 +551,0 @@ } |
@@ -9,3 +9,2 @@ 'use strict' | ||
constructor () { | ||
this.idleTimeoutMillis = 30000 | ||
this.fifo = true | ||
@@ -21,2 +20,7 @@ this.refreshIdle = true | ||
this.evictionRunIntervalMillis = 0 | ||
this.numTestsPerEvictionRun = 3 | ||
this.softIdleTimeoutMillis = -1 | ||
this.idleTimeoutMillis = 30000 | ||
// FIXME: no defaults! | ||
@@ -23,0 +27,0 @@ this.acquireTimeoutMillis = null |
@@ -19,5 +19,2 @@ 'use strict' | ||
* maximum number of queued requests allowed after which acquire calls will be rejected | ||
* @param {Number} config.idleTimeoutMillis | ||
* Delay in milliseconds after the idle items in the pool will be destroyed. | ||
* And idle item is that is not acquired yet. Waiting items doesn't count here. | ||
* @param {Number} config.reapIntervalMillis | ||
@@ -37,2 +34,13 @@ * Cleanup is scheduled in every `config.reapIntervalMillis` milliseconds. | ||
* Should the pool start creating resources etc once the constructor is called | ||
* @param {Number} opts.evictionRunIntervalMillis | ||
* How often to run eviction checks. Default: 0 (does not run). | ||
* @param {Number} opts.numTestsPerEvictionRun | ||
* Number of resources to check each eviction run. Default: 3. | ||
* @param {Number} opts.softIdleTimeoutMillis | ||
* amount of time an object may sit idle in the pool before it is eligible | ||
* for eviction by the idle object evictor (if any), with the extra condition | ||
* that at least "min idle" object instances remain in the pool. Default -1 (nothing can get evicted) | ||
* @param {Number} opts.idleTimeoutMillis | ||
* the minimum amount of time that an object may sit idle in the pool before it is eligible for eviction | ||
* due to idle time. Supercedes "softIdleTimeoutMillis" Default: 30000 | ||
* @param {Promise} [config.Promise=Promise] | ||
@@ -46,3 +54,2 @@ * What promise implementation should the pool use, defaults to native promises. | ||
this.idleTimeoutMillis = opts.idleTimeoutMillis || poolDefaults.idleTimeoutMillis | ||
this.fifo = (typeof opts.fifo === 'boolean') ? opts.fifo : poolDefaults.fifo | ||
@@ -72,2 +79,7 @@ this.refreshIdle = ('refreshIdle' in opts) ? opts.refreshIdle : poolDefaults.refreshIdle | ||
this.evictionRunIntervalMillis = opts.evictionRunIntervalMillis || poolDefaults.evictionRunIntervalMillis | ||
this.numTestsPerEvictionRun = opts.numTestsPerEvictionRun || poolDefaults.numTestsPerEvictionRun | ||
this.softIdleTimeoutMillis = opts.softIdleTimeoutMillis || poolDefaults.softIdleTimeoutMillis | ||
this.idleTimeoutMillis = opts.idleTimeoutMillis || poolDefaults.idleTimeoutMillis | ||
this.Promise = (typeof opts.Promise === 'object') ? opts.Promise : poolDefaults.Promise | ||
@@ -74,0 +86,0 @@ } |
@@ -15,7 +15,6 @@ 'use strict' | ||
this._list = new DoublyLinkedList() | ||
this._length = 0 | ||
} | ||
get length () { | ||
return this._length | ||
return this._list.length | ||
} | ||
@@ -33,3 +32,2 @@ | ||
this._list.insertEnd(node) | ||
this._length++ | ||
} | ||
@@ -42,3 +40,3 @@ | ||
remove () { | ||
if (this._length === 0) { | ||
if (this._list.length === 0) { | ||
return undefined | ||
@@ -50,3 +48,2 @@ } | ||
this._length-- | ||
return node.data | ||
@@ -60,3 +57,3 @@ } | ||
get head () { | ||
if (this._length === 0) { | ||
if (this._list.length === 0) { | ||
return undefined | ||
@@ -73,3 +70,3 @@ } | ||
get tail () { | ||
if (this._length === 0) { | ||
if (this._list.length === 0) { | ||
return undefined | ||
@@ -85,3 +82,2 @@ } | ||
this._list.remove(node) | ||
this._length-- | ||
} | ||
@@ -88,0 +84,0 @@ } |
{ | ||
"name": "generic-pool", | ||
"description": "Generic resource pooling for Node.JS", | ||
"version": "3.0.0-alpha.8", | ||
"version": "3.0.0-alpha.9", | ||
"author": "James Cooper <james@bitmechanic.com>", | ||
@@ -65,5 +65,5 @@ "contributors": [ | ||
"eslint-config-standard": "^6.0.0", | ||
"eslint-plugin-promise": "^2.0.1", | ||
"eslint-plugin-promise": "^3.3.0", | ||
"eslint-plugin-standard": "^2.0.0", | ||
"tap": "^7.0.0" | ||
"tap": "^8.0.0" | ||
}, | ||
@@ -76,5 +76,5 @@ "engines": { | ||
"lint-fix": "eslint --fix lib test", | ||
"test": "tap test/*-test.js --timeout 2" | ||
"test": "tap test/*-test.js " | ||
}, | ||
"license": "MIT" | ||
} |
@@ -145,3 +145,7 @@ [![build status](https://secure.travis-ci.org/coopernurse/node-pool.png)](http://travis-ci.org/coopernurse/node-pool) | ||
see example. (default 1) | ||
- `autostart`: boolean, should the pool start creating resources etc once the constructor is called, (default true) | ||
- `autostart`: boolean, should the pool start creating resources etc once the constructor is called, (default true) | ||
- `evictionRunIntervalMillis`: How often to run eviction checks. Default: 0 (does not run). | ||
- `numTestsPerRun`: Number of resources to check each eviction run. Default: 3. | ||
- `softIdleTimeoutMillis`: amount of time an object may sit idle in the pool before it is eligible for eviction by the idle object evictor (if any), with the extra condition that at least "min idle" object instances remain in the pool. Default -1 (nothing can get evicted) | ||
- `idleTimeoutMillis`: the minimum amount of time that an object may sit idle in the pool before it is eligible for eviction due to idle time. Supercedes `softIdleTimeoutMillis` Default: 30000 | ||
- `Promise`: Promise lib, a Promises/A+ implementation that the pool should use. Defaults to whatever `global.Promise` is (usually native promises). | ||
@@ -206,2 +210,6 @@ | ||
## Idle Object Eviction | ||
The pool has an evictor (off by default) which will inspect idle items in the pool and `destroy` them if they are too old | ||
## Draining | ||
@@ -208,0 +216,0 @@ |
@@ -23,12 +23,17 @@ var tap = require('tap') | ||
pool.acquire().then(function (resource) { | ||
pool.acquire() | ||
.then(function (resource) { | ||
t.fail('wooops') | ||
}).catch(function (err) { | ||
}) | ||
.catch(function (err) { | ||
t.match(err, /ResourceRequest timed out/) | ||
pool.drain() | ||
.then(function () { | ||
return pool.clear() | ||
}) | ||
.then(t.end) | ||
return pool.drain() | ||
}) | ||
.then(function () { | ||
return pool.clear() | ||
}) | ||
.then(function () { | ||
}) | ||
.then(t.end) | ||
.catch(t.error) | ||
}) | ||
@@ -55,11 +60,13 @@ | ||
pool.acquire().then(function (resource) { | ||
pool.acquire() | ||
.then(function (resource) { | ||
t.equal(resource, myResource) | ||
pool.release(resource) | ||
pool.drain() | ||
.then(function () { | ||
return pool.clear() | ||
}) | ||
.then(t.end) | ||
}).catch(t.error) | ||
return pool.drain() | ||
}) | ||
.then(function () { | ||
return pool.clear() | ||
}) | ||
.then(t.end) | ||
.catch(t.error) | ||
}) |
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
90103
31
2177
390