Socket
Socket
Sign inDemoInstall

tarn

Package Overview
Dependencies
0
Maintainers
4
Versions
17
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.1.5 to 2.0.0

26

lib/Pool.d.ts

@@ -1,4 +0,4 @@

/// <reference types="node" />
import { PendingOperation } from './PendingOperation';
import { Resource } from './Resource';
import { EventEmitter } from 'events';
export interface PoolOptions<T> {

@@ -26,2 +26,3 @@ create: CallbackOrPromise<T>;

protected pendingAcquires: PendingOperation<T>[];
protected pendingDestroys: PendingOperation<T>[];
protected interval: NodeJS.Timer | null;

@@ -40,2 +41,4 @@ protected destroyed: boolean;

protected validate: (resource: T) => boolean;
protected eventId: number;
protected emitter: EventEmitter;
constructor(opt: PoolOptions<T>);

@@ -51,2 +54,18 @@ numUsed(): number;

destroy(): Promise<import("./PromiseInspection").PromiseInspection<{}> | import("./PromiseInspection").PromiseInspection<void>>;
on(eventName: 'acquireRequest', handler: (eventId: number) => void): void;
on(eventName: 'acquireSuccess', handler: (eventId: number, resource: T) => void): void;
on(eventName: 'acquireFail', handler: (eventId: number, err: Error) => void): void;
on(eventName: 'release', handler: (resource: T) => void): void;
on(eventName: 'createRequest', handler: (eventId: number) => void): void;
on(eventName: 'createSuccess', handler: (eventId: number, resource: T) => void): void;
on(eventName: 'createFail', handler: (eventId: number, err: Error) => void): void;
on(eventName: 'destroyRequest', handler: (eventId: number, resource: T) => void): void;
on(eventName: 'destroySuccess', handler: (eventId: number, resource: T) => void): void;
on(eventName: 'destroyFail', handler: (eventId: number, resource: T, err: Error) => void): void;
on(eventName: 'startReaping', handler: () => void): void;
on(eventName: 'stopReaping', handler: () => void): void;
on(eventName: 'poolDestroyRequest', handler: (eventId: number) => void): void;
on(eventName: 'poolDestroySuccess', handler: (eventId: number) => void): void;
removeListener(event: string | symbol, listener: (...args: any[]) => void): void;
removeAllListeners(event?: string | symbol | undefined): void;
_tryAcquireOrCreate(): void;

@@ -60,8 +79,9 @@ _hasFreeResources(): boolean;

_create(): PendingOperation<T>;
_destroy(resource: T): Promise<any>;
_logError(err: Error): void;
_destroy(resource: T): Promise<void | T>;
_logDestroyerError(eventId: number, resource: T, err: Error): void;
_startReaping(): void;
_stopReaping(): void;
_executeEventHandlers(eventName: string, ...args: any): void;
}
export declare type Callback<T> = (err: Error | null, resource: T) => any;
export declare type CallbackOrPromise<T> = (cb: Callback<T>) => any | (() => Promise<T>);

132

lib/Pool.js

@@ -6,5 +6,7 @@ "use strict";

const utils_1 = require("./utils");
const events_1 = require("events");
class Pool {
constructor(opt) {
this.destroyed = false;
this.emitter = new events_1.EventEmitter();
opt = opt || {};

@@ -45,2 +47,22 @@ if (!opt.create) {

}
const allowedKeys = {
create: true,
validate: true,
destroy: true,
log: true,
min: true,
max: true,
acquireTimeoutMillis: true,
createTimeoutMillis: true,
destroyTimeoutMillis: true,
idleTimeoutMillis: true,
reapIntervalMillis: true,
createRetryIntervalMillis: true,
propagateCreateError: true
};
for (let key of Object.keys(opt)) {
if (!allowedKeys[key]) {
throw new Error(`Tarn: unsupported option opt.${key}`);
}
}
this.creator = opt.create;

@@ -63,4 +85,6 @@ this.destroyer = opt.destroy;

this.pendingAcquires = [];
this.pendingDestroys = [];
this.destroyed = false;
this.interval = null;
this.eventId = 1;
}

@@ -80,2 +104,4 @@ numUsed() {

acquire() {
const eventId = this.eventId++;
this._executeEventHandlers('acquireRequest', eventId);
const pendingAcquire = new PendingOperation_1.PendingOperation(this.acquireTimeoutMillis);

@@ -85,3 +111,9 @@ this.pendingAcquires.push(pendingAcquire);

// remove it from the pending queue.
pendingAcquire.promise = pendingAcquire.promise.catch(err => {
pendingAcquire.promise = pendingAcquire.promise
.then(resource => {
this._executeEventHandlers('acquireSuccess', eventId, resource);
return resource;
})
.catch(err => {
this._executeEventHandlers('acquireFail', eventId, err);
remove(this.pendingAcquires, pendingAcquire);

@@ -94,2 +126,3 @@ return Promise.reject(err);

release(resource) {
this._executeEventHandlers('release', resource);
for (let i = 0, l = this.used.length; i < l; ++i) {

@@ -116,3 +149,3 @@ const used = this.used[i];

this.free.forEach(free => {
if (utils_1.duration(timestamp, free.timestamp) > this.idleTimeoutMillis &&
if (utils_1.duration(timestamp, free.timestamp) >= this.idleTimeoutMillis &&
numDestroyed < maxDestroy) {

@@ -134,2 +167,4 @@ numDestroyed++;

destroy() {
const eventId = this.eventId++;
this._executeEventHandlers('poolDestroyRequest', eventId);
this._stopReaping();

@@ -155,6 +190,23 @@ this.destroyed = true;

.then(() => {
// Also wait rest of the pending destroys to finish
return Promise.all(this.pendingDestroys.map(pd => pd.promise));
})
.then(() => {
this.free = [];
this.pendingAcquires = [];
}));
})).then(res => {
this._executeEventHandlers('poolDestroySuccess', eventId);
this.emitter.removeAllListeners();
return res;
});
}
on(event, listener) {
this.emitter.on(event, listener);
}
removeListener(event, listener) {
this.emitter.removeListener(event, listener);
}
removeAllListeners(event) {
this.emitter.removeAllListeners(event);
}
_tryAcquireOrCreate() {

@@ -243,2 +295,4 @@ if (this.destroyed) {

_create() {
const eventId = this.eventId++;
this._executeEventHandlers('createRequest', eventId);
const pendingCreate = new PendingOperation_1.PendingOperation(this.createTimeoutMillis);

@@ -252,2 +306,3 @@ this.pendingCreates.push(pendingCreate);

pendingCreate.resolve(resource);
this._executeEventHandlers('createSuccess', eventId, resource);
return null;

@@ -259,2 +314,3 @@ })

pendingCreate.reject(err);
this._executeEventHandlers('createFail', eventId, err);
return null;

@@ -265,29 +321,31 @@ });

_destroy(resource) {
try {
// this.destroyer can be both synchronous and asynchronous.
// When it's synchronous, errors are handled by the try/catch
// When it's asynchronous, errors are handled by .catch()
const retVal = this.destroyer(resource);
if (retVal && retVal.then && retVal.catch) {
const pendingDestroy = new PendingOperation_1.PendingOperation(this.destroyTimeoutMillis);
retVal
.then(() => {
pendingDestroy.resolve(resource);
})
.catch((err) => {
pendingDestroy.reject(err);
});
// In case of an error there's nothing we can do here but log it.
return pendingDestroy.promise.catch(err => this._logError(err));
}
return Promise.resolve(retVal);
}
catch (err) {
// There's nothing we can do here but log the error. This would otherwise
// leak out as an unhandled exception.
this._logError(err);
return Promise.resolve();
}
const eventId = this.eventId++;
this._executeEventHandlers('destroyRequest', eventId, resource);
// this.destroyer can be both synchronous and asynchronous.
// so we wrap it to promise to get all exceptions through same pipeline
const pendingDestroy = new PendingOperation_1.PendingOperation(this.destroyTimeoutMillis);
const retVal = Promise.resolve().then(() => this.destroyer(resource));
retVal
.then(() => {
pendingDestroy.resolve(resource);
})
.catch((err) => {
pendingDestroy.reject(err);
});
this.pendingDestroys.push(pendingDestroy);
// In case of an error there's nothing we can do here but log it.
return pendingDestroy.promise
.then(res => {
this._executeEventHandlers('destroySuccess', eventId, resource);
return res;
})
.catch(err => this._logDestroyerError(eventId, resource, err))
.then(res => {
const index = this.pendingDestroys.findIndex(pd => pd === pendingDestroy);
this.pendingDestroys.splice(index, 1);
return res;
});
}
_logError(err) {
_logDestroyerError(eventId, resource, err) {
this._executeEventHandlers('destroyFail', eventId, resource, err);
this.log('Tarn: resource destroyer threw an exception ' + err.stack, 'warn');

@@ -297,2 +355,3 @@ }

if (!this.interval) {
this._executeEventHandlers('startReaping');
this.interval = setInterval(() => this.check(), this.reapIntervalMillis);

@@ -303,2 +362,3 @@ }

if (this.interval !== null) {
this._executeEventHandlers('stopReaping');
clearInterval(this.interval);

@@ -308,2 +368,16 @@ }

}
_executeEventHandlers(eventName, ...args) {
const listeners = this.emitter.listeners(eventName);
// just calling .emit() would stop running rest of the listeners if one them fails
listeners.forEach(listener => {
try {
listener(...args);
}
catch (err) {
// There's nothing we can do here but log the error. This would otherwise
// leak out as an unhandled exception.
this.log(`Tarn: event handler "${eventName}" threw an exception ${err.stack}`, 'warn');
}
});
}
}

@@ -310,0 +384,0 @@ exports.Pool = Pool;

{
"name": "tarn",
"version": "1.1.5",
"version": "2.0.0",
"description": "Simple and robust resource pool for node.js",

@@ -26,3 +26,3 @@ "main": "lib/tarn.js",

"engines": {
"node": ">=4.0.0"
"node": ">=8.0.0"
},

@@ -29,0 +29,0 @@ "keywords": [

@@ -23,7 +23,6 @@ [![Build Status](https://travis-ci.org/Vincit/tarn.js.svg?branch=master)](https://travis-ci.org/Vincit/tarn.js)

const pool = new Pool({
// function that creates a resource. You can either pass the resource
// to the callback or return a promise that resolves the resource
// to the callback(error, resource) or return a promise that resolves the resource
// (but not both).
create: (cb) => {
create: cb => {
cb(null, new SomeResource());

@@ -35,12 +34,16 @@ },

// another one is acquired.
validate: (resource) => {
validate: resource => {
return true;
},
// function that destroys a resource. This is always synchronous
// as nothing waits for the return value.
destroy: (someResource) => {
// function that destroys a resource, should return promise if
// destroying is asynchronous operation
// (destroy does not support callback syntax like create)
destroy: someResource => {
someResource.cleanup();
},
// logger function, noop by default
log: (message, logLevel) => console.log(`${logLevel}: ${message}`)
// minimum size

@@ -84,3 +87,5 @@ min: 2,

// acquire can be aborted using the abort method
// acquire can be aborted using the abort method.
// If acquire had triggered creating new resource to the pool
// creation will continue and it is not aborted.
acquire.abort();

@@ -103,12 +108,12 @@

// returns the number of non-free resources
pool.numUsed()
pool.numUsed();
// returns the number of free resources
pool.numFree()
pool.numFree();
// how many acquires are waiting for a resource to be released
pool.numPendingAcquires()
pool.numPendingAcquires();
// how many asynchronous create calls are running
pool.numPendingCreates()
pool.numPendingCreates();

@@ -118,2 +123,39 @@ // waits for all resources to be returned to the pool and destroys them.

await pool.destroy();
// The following examples add synchronous event handlers for example to
// allow externally collect diagnostic data of pool behaviour.
// If any of these hooks fail, all errors are catched and warnings are logged.
// resource is acquired from pool
pool.on('acquireRequest', eventId => {});
pool.on('acquireSuccess', (eventId, resource) => {});
pool.on('acquireFail', (eventId, err) => {});
// resource returned to pool
pool.on('release', resource => {});
// resource was created and added to the pool
pool.on('createRequest', eventId => {});
pool.on('createSuccess', (eventId, resource) => {});
pool.on('createFail', (eventId, err) => {});
// resource is destroyed and evicted from pool
// resource may or may not be invalid when destroySuccess / destroyFail is called
pool.on('destroyRequest', (eventId, resource) => {});
pool.on('destroySuccess', (eventId, resource) => {});
pool.on('destroyFail', (eventId, resource, err) => {});
// when internal reaping event clock is activated / deactivated
pool.on('startReaping', () => {});
pool.on('stopReaping', () => {});
// pool is destroyed (after poolDestroySuccess all event handlers are also cleared)
pool.on('poolDestroyRequest', eventId => {});
pool.on('poolDestroySuccess', eventId => {});
// remove single event listener
pool.removeListener(eventName, listener);
// remove all listeners from an event
pool.removeAllListeners(eventName);
```

@@ -123,4 +165,17 @@

### 1.1.5 2019-04-06
### Master
### 2.0.0 2019-06-02
- Accidentally published breaking changes in 1.2.0. Unpublished it and published again with correct version number 2.0.0 #33
### 1.2.0 2019-06-02 (UNPUBLISHED)
- Passing unknown options throws an error #19 #32
- Diagnostic event handlers to allow monitoring pool behaviour #14 #23
- Dropped node 6 support #25 #28
- pool.destroy() now always waits for all pending destroys to finish before resolving #29
### 1.1.5 2019-04-06
- Added changelog #22

@@ -127,0 +182,0 @@ - Handle opt.destroy() being a promise with destroyTimeout #16

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc