pool2
v1.4.1
Published
Resource pool built with database drivers in mind
Downloads
7,743
Readme
Pool2
A generic resource pool
Usage
The values below are the defaults
var Pool = require('pool2');
var pool = new Pool({
acquire: function (cb) { cb(null, resource); },
acquireTimeout: 30*1000, // please see note below
dispose: function (res, cb) { cb(); },
disposeTimeout: 30*1000,
destroy: function (res) { },
ping: function (res, cb) { cb(); },
pingTimeout: 10*1000,
capabilities: ['tags'],
min: 0,
max: 10,
maxRequests: Infinity,
requestTimeout: Infinity,
idleTimeout: 60*1000,
syncInterval: 10*1000,
backoff: { },
bailAfter: 0
});
pool.acquire(function (err, rsrc) {
// do stuff
pool.release(rsrc);
});
pool.stats();
/* {
min: 0,
max: 10,
allocated: 0,
available: 0,
queued: 0,
maxRequests: Infinity
} */
pool.remove(rsrc);
pool.destroy(rsrc);
pool.end(function (errs) {
// errs is null or an array of errors from resources that were released
});
pool._destroyPool();
Constructor options
acquire
Required. The function that acquires a resource (e.g. opens a database connection) on behalf of the pool. Accepts a node-style callback.
acquireTimeout
An integer, in milliseconds, to specify how long to wait for a call to acquire
before failing.
NOTE: acquireTimeout
is the delay after which pool2
will stop waiting for a resource and move on with its life. This means that it's possible for in-flight resources that have timed out to remain open and exceed the pool maximum. Resources that complete allocation that have timed out this way will have the disposer called on them.
If what you want is for something, e.g. a database connection, or http request, to be destroyed after some timeout, you probably want something like this, instead:
acquire: function (cb) {
openThing({ timeout: 2000 })
.on('timeout', function () {
cb(new Error('Timed out'));
});
}
This puts the burden of timing out and cleaning up on the library for the resource you are using pool2 to manage; you then react to the timeout event and notify pool2 of the failure to acquire the resource.
dispose
Required. The function that disposes of a resource (e.g. gracefully closes a database connection) on behalf of the pool. Accepts the resource to dispose of, which is the same object returned by the acquire function, and a node-style callback.
disposeTimeout
An integer, in milliseconds, to specify how long to wait for a call to dispose
before failing. Resources that fail the dispose
call will still be removed from the pool, but undefined behavior may occur: if a dispose call fails, it may leave dangling sockets or handles that prevent graceful exit of an application.
destroy
Optional. This function is called with a resource that is destroyed, either by a timeout or failed call to dispose
, or an explicit call to pool.destroy()
. There is no node-style callback: this function is a last resort, and is fire-and-forget.
ping
Optional. A function to check whether a resource is still alive (e.g. send SELECT 1
on a database connection). Accepts the resource to test and a node-style callback.
pingTimeout
An integer, in milliseconds, to specify how long to wait for the ping
function before giving up and disposing of the resource.
capabilities
An array of strings. This is used in conjunction with clustering to specify which pools in the cluster to select from. For example, you might have a pool of connections to a database master with read-write capability, and another pool of connections to a slave with read-only capability. One would be defined as capabilities: ['read', 'write']
, and the other as capabilities: ['read']
. When acquiring from the cluster, you could use cluster.acquire('read', ...)
and be served a connection from either pool, but if you used cluster.acquire('write', ...)
, you would only receive connections from the master (read-write) pool.
min
An integer greater than zero. The minimum number of resources to maintain in the pool. If the pool contains fewer resources than this, it will attempt to acquire more until it reaches this value.
max
An integer greater than or equal to min
. The maximum number of resources the pool may contain. Requests for resources will not cause new resources to be allocated when this number of resources are currently held in the pool (whether checked out or not). If all resources are checked out, requests are queued until one becomes available.
maxRequests
An integer greater than 0 (Infinity
is also valid), to specify the maximum number of requests that the pool instance will allow. If the request queue exceeds this number, calls to acquire
will fail with the error Pool is full
.
requestTimeout
An integer, in milliseconds (Infinity
is also valid), to specify how long to wait for a successful resource request before failing.
idleTimeout
An integer, in milliseconds, to specify how long a resource must be idle before it may be disposed of. Resources are periodically checked (see syncInterval
below) for idleness, and idle resources are disposed of unless they would bring the pool below the configured min
value.
syncInterval
An integer, in milliseconds, to specify how often to dispose of idle resources and/or open new resources to fulfill the pool minimum.
backoff
An object, passed as-is to simple-backoff, which governs the retry rate for failed allocations. pool2
uses the Fibonacci strategy for backoff timing. Currently only an explicit failure or timeout on an allocation is retried in this fashion; all other errors and removals of resources that may cause the pool's resource collection to fall under the minimum are remedied on the syncInterval
or an explicit allocation request when no resources are available.
bailAfter
An integer, in milliseconds (Infinity
is also valid), to specify how long to wait for a successful allocation before failing. pool2
has a concept of whether a pool is "live", defined by whether it has succeeded in allocating any resource yet. When a pool is initialized, its live
property is set to false
, and a timestamp is taken. When an allocation succeeds, the live
property is set to true. When an allocation fails, if live
is false
, the difference between the current timestamp and the initial timestamp is taken: if this value is greater than bailAfter
, the pool is destroyed and an error is emitted.
The primary purpose of this functionality is to allow for applications to crash and notify the user when configuration is incorrect or some other problem occurs (database isn't started, network configuration has changed, etc.)
The default for this value is 0, meaning that if the very first allocation request fails, pool2 will fail. Infinity
is an acceptable value, allowing you to retry infinitely. Retries follow the backoff settings, if supplied, though an extra try or two may result from the syncInterval
setting as well.
Instance methods
pool.acquire()
Acquire a resource from the pool. Accepts a node-style callback, which is given either the resource or an error. Calls to acquire are queued and served in first in, first out order. Currently, acquire requests are queued indefinitely. Requests are subject to the maxRequests
option; if the queue is full, a call to acquire
will be rejected with the error Pool is full
.
pool.remove()
Remove a resource from the pool gracefully. This method should be preferred over destroy
(see below). This method is fire-and-forget: it does not take a callback, even though the underlying dispose
function does.
pool.destroy()
Remove a resource from the pool "ungracefully". This immediately removes the resource without attempting to clean it up. Suitable for removing resources that encounter a fatal error and cannot otherwise be nicely dealt with.
pool.stats()
Returns some information about the current state of the pool:
{
min: 0,
max: 10,
allocated: 0,
available: 0,
queued: 0,
maxRequests: Infinity
}
pool.end()
Attempt to gracefully shut everything down. Calls to acquire
after calling end
will be rejected with the error Pool is ending
(or Pool is destroyed
once shutdown has completed). Pending resources will not be disposed of until they are released by whatever has checked them out. When all resources have been released back to the pool, calls the dispose
function on each of them and collects any errors. These errors are passed along to the callback, if provided.
Example:
pool.end(function (errors) {
if (!errors.length) { return; }
console.error('Encountered some errors while shutting down:', errors);
});
pool._destroyPool()
This is an internal method used by pool2 itself primarily during testing. Rejects all pending requests and destroys all open resources; disables timers and sets the pool's status to destroyed. You shouldn't need to call this, but I'm documenting it here anyway.
Clustering
var pool1 = new Pool(opts1),
pool2 = new Pool(opts2);
var cluster = new Pool.Cluster([pool1, pool2]);
cluster.acquire(function (err, rsrc) {
// do stuff
cluster.release(rsrc);
});
cluster.acquire('read', function (err, rsrc) {
// if you specify a capability, only pools tagged with that capability
// will be used to serve the request
});
cluster.addPool(new Pool(...));
var pool = cluster.pools[0];
cluster.removePool(pool);
cluster.end(function (errs) {
// errs is an array of errors returned from ending the pools
});
Constructor options
Pool.Cluster
takes only one argument to its constructor: an array of Pool
instances.
Instance methods
cluster.addPool()
Add a pool to the cluster.
cluster.removePool()
Remove a pool from the cluster.
cluster.pools
An array of pools in the cluster.
cluster.acquire(callback)
Just like pool.acquire
, except it draws a resource from any of the pools in the cluster. Resources are drawn from the pool with the most idle resources first; otherwise, order is undefined.
cluster.acquire('capability', callback)
Like cluster.acquire
, except only pools that list 'capability'
in their capabilities
array are considered.
cluster.end()
Calls pool.end()
on all pools in this cluster, consolidates any errors, and calls back with them
Debugging
Pool2 makes use of the debug module. For a detailed look at what exactly the pool is doing, execute your program with DEBUG=pool2
set.