class DefaultEvictor {
evict (config, pooledResource, availableObjectsCount) {
const idleTime = Date.now() - pooledResource.lastIdleTime
if (config.softIdleTimeoutMillis < idleTime && config.min < availableObjectsCount) {
return true
}
if (config.idleTimeoutMillis < idleTime) {
return true
}
return false
}
}n/a
class Deque {
constructor () {
this._list = new DoublyLinkedList()
}
/**
* removes and returns the first element from the queue
* @return {[type]} [description]
*/
shift () {
if (this._length === 0) {
return undefined
}
const node = this._list.head
this._list.remove(node)
return node.data
}
/**
* adds one elemts to the beginning of the queue
* @param {[type]} element [description]
* @return {[type]} [description]
*/
unshift (element) {
const node = DoublyLinkedList.createNode(element)
this._list.insertBeginning(node)
}
/**
* adds one to the end of the queue
* @param {[type]} element [description]
* @return {[type]} [description]
*/
push (element) {
const node = DoublyLinkedList.createNode(element)
this._list.insertEnd(node)
}
/**
* removes and returns the last element from the queue
*/
pop () {
if (this._length === 0) {
return undefined
}
const node = this._list.tail
this._list.remove(node)
return node.data
}
[Symbol.iterator] () {
return new DequeIterator(this._list)
}
iterator () {
return new DequeIterator(this._list)
}
reverseIterator () {
return new DequeIterator(this._list, true)
}
/**
* get a reference to the item at the head of the queue
* @return {element} [description]
*/
get head () {
if (this._list.length === 0) {
return undefined
}
const node = this._list.head
return node.data
}
/**
* get a reference to the item at the tail of the queue
* @return {element} [description]
*/
get tail () {
if (this._list.length === 0) {
return undefined
}
const node = this._list.tail
return node.data
}
get length () {
return this._list.length
}
}n/a
class Pool extends EventEmitter {
/**
* Generate an Object pool with a specified `factory` and `config`.
*
* @param {Object} factory
* Factory to be used for generating and destroying the items.
* @param {Function} factory.create
* Should create the item to be acquired,
* and call it's first callback argument with the generated item as it's argument.
* @param {Function} factory.destroy
* Should gently close any resources that the item is using.
* Called before the items is destroyed.
* @param {Function} factory.validate
* Test if a resource is still valid .Should return a promise that resolves to a boolean, true if resource is still valid and
false
* If it should be removed from pool.
*/
constructor (Evictor, Deque, PriorityQueue, factory, options) {
super()
factoryValidator(factory)
this._config = new PoolOptions(options)
// TODO: fix up this ugly glue-ing
this._Promise = this._config.Promise
this._factory = factory
this._draining = false
this._started = false
/**
* Holds waiting clients
* @type {PriorityQueue}
*/
this._waitingClientsQueue = new PriorityQueue(this._config.priorityRange)
/**
* Collection of promises for resource creation calls made by the pool to factory.create
* @type {Set}
*/
this._factoryCreateOperations = new Set()
/**
* Collection of promises for resource destruction calls made by the pool to factory.destroy
* @type {Set}
*/
this._factoryDestroyOperations = new Set()
/**
* A queue/stack of pooledResources awaiting acquisition
* TODO: replace with LinkedList backed array
* @type {Array}
*/
this._availableObjects = new Deque()
/**
* Collection of references for any resource that are undergoing validation before being acquired
* @type {Set}
*/
this._testOnBorrowResources = new Set()
/**
* Collection of references for any resource that are undergoing validation before being returned
* @type {Set}
*/
this._testOnReturnResources = new Set()
/**
* Collection of promises for any validations currently in process
* @type {Set}
*/
this._validationOperations = new Set()
/**
* All objects associated with this pool in any state (except destroyed)
* @type {PooledResourceCollection}
*/
this._allObjects = new Set()
/**
* Loans keyed by the borrowed resource
* @type {Map}
*/
this._resourceLoans = new Map()
/**
* Infinitely looping iterator over available object
* @type {DLLArrayIterator}
*/
this._evictionIterator = this._availableObjects.iterator()
this._evictor = new Evictor()
/**
* handle for setTimeout for next eviction run
* @type {[type]}
*/
this._scheduledEviction = null
// create initial resources (if factory.min > 0)
if (this._config.autostart === true) {
this.start()
}
}
_destroy (pooledResource) {
// FIXME: do we need another state for "in destruction"?
pooledResource.invalidate()
this._allObjects.delete(pooledResource)
// NOTE: this maybe very bad promise usage?
const destroyPromise = this._factory.destroy(pooledResource.obj)
const wrappedDestroyPromise = this._Promise.resolve(destroyPromise)
this._trackOperation(wrappedDestroyPromise, this._factoryDestroyOperations)
.catch((reason) => {
this.emit(FACTORY_DESTROY_ERROR, reason)
})
// TODO: maybe ensuring minimum pool size should live outside here
this._ensureMinimum()
}
/**
* Attempt to move an available resource into test and then onto a waiting client
* @return {Boolean} could we move an available resource into test
*/
_testOnBorrow () {
if (this._availableObjects.length < 1) {
return false
}
const pooledResource = this._availableObjects.shift()
// Mark the resource as in test
pooledResource.test()
this._testOnBorrowResources.add(pooledResource)
const validationProm ...n/a
class PriorityQueue {
constructor (size) {
this._size = Math.max(+size | 0, 1)
this._slots = []
// initialize arrays to hold queue elements
for (let i = 0; i < this._size; i++) {
this._slots.push(new Queue())
}
}
get length () {
let _length = 0
for (let i = 0, slots = this._slots.length; i < slots; i++) {
_length += this._slots[i].length
}
return _length
}
enqueue (obj, priority) {
// Convert to integer with a default value of 0.
priority = priority && +priority | 0 || 0
if (priority) {
if (priority < 0 || priority >= this._size) {
priority = (this._size - 1)
// put obj at the end of the line
}
}
this._slots[priority].push(obj)
}
dequeue () {
for (let i = 0, sl = this._slots.length; i < sl; i += 1) {
if (this._slots[i].length) {
return this._slots[i].shift()
}
}
return
}
get head () {
for (let i = 0, sl = this._slots.length; i < sl; i += 1) {
if (this._slots[i].length > 0) {
return this._slots[i].head
}
}
return
}
get tail () {
for (let i = this._slots.length - 1; i >= 0; i--) {
if (this._slots[i].length > 0) {
return this._slots[i].tail
}
}
return
}
}n/a
createPool = function (factory, config){
return new Pool(DefaultEvictor, Deque, PriorityQueue, factory, config)
}...
}
var opts = {
max: 10, // maximum size of the pool
min: 2 // minimum size of the pool
}
var myPool = genericPool.createPool(factory, opts)
/**
* Step 2 - Use pool in your code to acquire/release resources
*/
// acquire connection - Promise is resolved
// once a resource becomes available
...class TimeoutError extends ExtendableError {
constructor (m) {
super(m)
}
}...
removeTimeout () {
clearTimeout(this._timeout)
this._timeout = null
}
_fireTimeout () {
this.reject(new errors.TimeoutError('ResourceRequest timed out'))
}
reject (reason) {
this.removeTimeout()
super.reject(reason)
}
...reflector = function (promise) {
return promise.then(noop, noop)
}n/a