Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a maximum age of object in pool #303

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export interface IEvictorConfig {
softIdleTimeoutMillis: number;
idleTimeoutMillis: number;
min: number;
maxAgeMillis: number;
}

export interface IEvictor<T> {
Expand Down Expand Up @@ -155,6 +156,7 @@ export interface Options {
numTestsPerEvictionRun?: number;
softIdleTimeoutMillis?: number;
idleTimeoutMillis?: number;
maxAgeMillis?: number;
}

export class Pool<T> extends EventEmitter {
Expand Down
5 changes: 5 additions & 0 deletions lib/DefaultEvictor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand All @@ -16,6 +17,10 @@ class DefaultEvictor {
return true;
}

if (config.maxAgeMillis > 0 && config.maxAgeMillis < age) {
return true;
}

return false;
}
}
Expand Down
3 changes: 2 additions & 1 deletion lib/Pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions lib/PoolDefaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class PoolDefaults {
this.numTestsPerEvictionRun = 3;
this.softIdleTimeoutMillis = -1;
this.idleTimeoutMillis = 30000;
this.maxAgeMillis = -1;

// FIXME: no defaults!
this.acquireTimeoutMillis = null;
Expand Down
4 changes: 4 additions & 0 deletions lib/PoolOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
Expand Down
24 changes: 24 additions & 0 deletions test/generic-pool-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down