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