diff --git a/README.md b/README.md index 9a3850d..42676fc 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ An optional object/dictionary with the any of the following properties: - `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). +- `maxAgeMillis`: the maximum amount of time passed since an object has been created before it is eligible for eviction. Note it must first become idle to be evicted. Default: -1 (no max age) ### pool.acquire @@ -265,10 +266,13 @@ and returns a `Promise` that either `resolve`s with the value from the user supp ## 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. +The pool has an evictor (off by default) which will inspect idle items in the pool and `destroy` them if they have been idle too long. By default the evictor does not run, to enable it you must set the `evictionRunIntervalMillis` option to a non-zero value. Once enable the evictor will check at most `numTestsPerEvictionRun` each time, this is to stop it blocking your application if you have lots of resources in the pool. +## Max Age Object Eviction + +By setting `maxAgeMillis` the evictor will inspect idle items in the pool and `destroy` any objects that have been created more than `maxAgeMillis` ago. ## Priority Queueing diff --git a/index.d.ts b/index.d.ts index 2c02bad..68340f6 100644 --- a/index.d.ts +++ b/index.d.ts @@ -123,6 +123,7 @@ export interface IEvictorConfig { softIdleTimeoutMillis: number; idleTimeoutMillis: number; min: number; + maxAgeMillis: number; } export interface IEvictor { @@ -155,6 +156,7 @@ export interface Options { numTestsPerEvictionRun?: number; softIdleTimeoutMillis?: number; idleTimeoutMillis?: number; + maxAgeMillis?: number; } export class Pool extends EventEmitter { diff --git a/lib/DefaultEvictor.js b/lib/DefaultEvictor.js index 658d345..87d9665 100644 --- a/lib/DefaultEvictor.js +++ b/lib/DefaultEvictor.js @@ -3,6 +3,7 @@ class DefaultEvictor { evict(config, pooledResource, availableObjectsCount) { const idleTime = Date.now() - pooledResource.lastIdleTime; + const age = Date.now() - pooledResource.creationTime; if ( config.softIdleTimeoutMillis > 0 && @@ -16,6 +17,10 @@ class DefaultEvictor { return true; } + if (config.maxAgeMillis > 0 && config.maxAgeMillis < age) { + return true; + } + return false; } } diff --git a/lib/Pool.js b/lib/Pool.js index d014052..2472994 100644 --- a/lib/Pool.js +++ b/lib/Pool.js @@ -360,7 +360,8 @@ class Pool extends EventEmitter { const evictionConfig = { softIdleTimeoutMillis: this._config.softIdleTimeoutMillis, idleTimeoutMillis: this._config.idleTimeoutMillis, - min: this._config.min + min: this._config.min, + maxAgeMillis: this._config.maxAgeMillis }; for (let testsHaveRun = 0; testsHaveRun < testsToRun; ) { const iterationResult = this._evictionIterator.next(); diff --git a/lib/PoolDefaults.js b/lib/PoolDefaults.js index 56d004c..872bd04 100644 --- a/lib/PoolDefaults.js +++ b/lib/PoolDefaults.js @@ -18,6 +18,7 @@ class PoolDefaults { this.numTestsPerEvictionRun = 3; this.softIdleTimeoutMillis = -1; this.idleTimeoutMillis = 30000; + this.maxAgeMillis = -1; // FIXME: no defaults! this.acquireTimeoutMillis = null; diff --git a/lib/PoolOptions.js b/lib/PoolOptions.js index f355948..bf09b4a 100644 --- a/lib/PoolOptions.js +++ b/lib/PoolOptions.js @@ -47,6 +47,9 @@ class PoolOptions { * due to idle time. Supercedes "softIdleTimeoutMillis" Default: 30000 * @param {typeof Promise} [opts.Promise=Promise] * What promise implementation should the pool use, defaults to native promises. + * @param {Number} [opts.maxAgeMillis=-1] + * the maximum amount of time passed since an object has been created before it is eligible for eviction. Note it must + * first become idle to be evicted. Default: -1 (no max age) */ constructor(opts) { const poolDefaults = new PoolDefaults(); @@ -101,6 +104,7 @@ class PoolOptions { opts.softIdleTimeoutMillis || poolDefaults.softIdleTimeoutMillis; this.idleTimeoutMillis = opts.idleTimeoutMillis || poolDefaults.idleTimeoutMillis; + this.maxAgeMillis = opts.maxAgeMillis || poolDefaults.maxAgeMillis; this.Promise = opts.Promise != null ? opts.Promise : poolDefaults.Promise; } diff --git a/test/generic-pool-test.js b/test/generic-pool-test.js index baaddfb..ce5f16c 100644 --- a/test/generic-pool-test.js +++ b/test/generic-pool-test.js @@ -208,6 +208,30 @@ tap.test("evictor removes instances on idletimeout", function(t) { }, 120); }); +tap.test("evictor removes instances on maxAgeMillis", function(t) { + const resourceFactory = new ResourceFactory(); + const config = { + min: 2, + max: 2, + maxAgeMillis: 50, + evictionRunIntervalMillis: 10 + }; + const pool = createPool(resourceFactory, config); + + setTimeout(function() { + pool + .acquire() + .then(res => { + t.ok(res.id > 1); + return pool.release(res); + }) + .then(() => { + utils.stopPool(pool); + t.end(); + }); + }, 120); +}); + tap.test("tests drain", function(t) { const count = 5; let acquired = 0;