Skip to content

Commit 02ab9aa

Browse files
Added option.maxTries
Setting this option stops trying to create resource after specified consequent failures. Prevents pool going into infinite loop if resource can not be created.
1 parent 7850748 commit 02ab9aa

File tree

4 files changed

+61
-7
lines changed

4 files changed

+61
-7
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ An optional object/dictionary with the any of the following properties:
137137
- `numTestsPerRun`: Number of resources to check each eviction run. Default: 3.
138138
- `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)
139139
- `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
140+
- `maxTries`: maximum number of consequent failures before pool stops trying to create resource and rejects resource request. Default: 0 (tries forever until resource is created).
140141
- `Promise`: Promise lib, a Promises/A+ implementation that the pool should use. Defaults to whatever `global.Promise` is (usually native promises).
141142

142143
### pool.acquire

lib/Pool.js

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const EventEmitter = require("events").EventEmitter;
44

55
const factoryValidator = require("./factoryValidator");
6+
const errors = require('./errors')
67
const PoolOptions = require("./PoolOptions");
78
const ResourceRequest = require("./ResourceRequest");
89
const ResourceLoan = require("./ResourceLoan");
@@ -123,6 +124,12 @@ class Pool extends EventEmitter {
123124
*/
124125
this._scheduledEviction = null;
125126

127+
/**
128+
* counter of consequent failures to create resource
129+
* @type {Number}
130+
*/
131+
this._failedTries = 0
132+
126133
// create initial resources (if factory.min > 0)
127134
if (this._config.autostart === true) {
128135
this.start();
@@ -215,6 +222,15 @@ class Pool extends EventEmitter {
215222
return;
216223
}
217224

225+
// Try counter exceeded - reject resource request to next waiting client
226+
if (
227+
this._config.maxTries > 0 &&
228+
this._failedTries >= this._config.maxTries
229+
) {
230+
this._dispatchPooledResourceToNextWaitingClient()
231+
return
232+
}
233+
218234
const resourceShortfall =
219235
numWaitingClients - this._potentiallyAllocableResourceCount;
220236

@@ -267,15 +283,27 @@ class Pool extends EventEmitter {
267283
) {
268284
// While we were away either all the waiting clients timed out
269285
// or were somehow fulfilled. put our pooledResource back.
270-
this._addPooledResourceToAvailableObjects(pooledResource);
286+
if (pooledResource) {
287+
// While we were away either all the waiting clients timed out
288+
// or were somehow fulfilled. put our pooledResource back.
289+
this._addPooledResourceToAvailableObjects(pooledResource)
290+
}
271291
// TODO: do need to trigger anything before we leave?
272292
return false;
273293
}
274-
const loan = new ResourceLoan(pooledResource, this._Promise);
275-
this._resourceLoans.set(pooledResource.obj, loan);
276-
pooledResource.allocate();
277-
clientResourceRequest.resolve(pooledResource.obj);
278-
return true;
294+
if (pooledResource) {
295+
const loan = new ResourceLoan(pooledResource, this._Promise)
296+
this._resourceLoans.set(pooledResource.obj, loan)
297+
pooledResource.allocate()
298+
clientResourceRequest.resolve(pooledResource.obj)
299+
} else {
300+
let errorMessage = 'Failed to create resource'
301+
if (this._config.maxTries > 0 && this._failedTries >= this._config.maxTries) {
302+
errorMessage = 'Failed to create resource ' + this._failedTries + ' times in a row'
303+
}
304+
clientResourceRequest.reject(new errors.ResourceCreationError(errorMessage))
305+
}
306+
return true
279307
}
280308

281309
/**
@@ -304,12 +332,21 @@ class Pool extends EventEmitter {
304332
* @private
305333
*/
306334
_createResource() {
335+
// Do not attempt to create resource if reached maximum number of consequent failures
336+
if (
337+
this._config.maxTries > 0 &&
338+
this._failedTries >= this._config.maxTries
339+
) {
340+
return
341+
}
307342
// An attempt to create a resource
308343
const factoryPromise = this._factory.create();
309344
const wrappedFactoryPromise = this._Promise.resolve(factoryPromise);
310345

311346
this._trackOperation(wrappedFactoryPromise, this._factoryCreateOperations)
312347
.then(resource => {
348+
// Resource created successfully - reset fail counter
349+
this._failedTries = 0
313350
this._handleNewResource(resource);
314351
return null;
315352
})
@@ -435,6 +472,10 @@ class Pool extends EventEmitter {
435472
);
436473
}
437474

475+
// Reset fail counter - maybe resource will be created this time
476+
// (for example network is restored and connection can be made)
477+
this._failedTries = 0
478+
438479
// TODO: should we defer this check till after this event loop incase "the situation" changes in the meantime
439480
if (
440481
this._config.maxWaitingClients !== undefined &&

lib/PoolOptions.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ class PoolOptions {
4242
* @param {Number} [opts.idleTimeoutMillis=30000]
4343
* the minimum amount of time that an object may sit idle in the pool before it is eligible for eviction
4444
* due to idle time. Supercedes "softIdleTimeoutMillis" Default: 30000
45+
* @param {Number} opts.maxTries
46+
* maximum number of consequent failures before pool stops trying to create resource
47+
* and rejects resource request. Default: 0 (tries forever until resource is created).
4548
* @param {typeof Promise} [opts.Promise=Promise]
4649
* What promise implementation should the pool use, defaults to native promises.
4750
*/
@@ -81,9 +84,11 @@ class PoolOptions {
8184
this.max = parseInt(opts.max, 10);
8285
// @ts-ignore
8386
this.min = parseInt(opts.min, 10);
87+
this.maxTries = parseInt(opts.maxTries, 10)
8488

8589
this.max = Math.max(isNaN(this.max) ? 1 : this.max, 1);
8690
this.min = Math.min(isNaN(this.min) ? 0 : this.min, this.max);
91+
this.maxTries = (isNaN(this.maxTries) ? 0 : this.maxTries)
8792

8893
this.evictionRunIntervalMillis =
8994
opts.evictionRunIntervalMillis || poolDefaults.evictionRunIntervalMillis;

lib/errors.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,15 @@ class TimeoutError extends ExtendableError {
2020
super(m);
2121
}
2222
}
23+
24+
class ResourceCreationError extends ExtendableError {
25+
constructor (m) {
26+
super(m)
27+
}
28+
}
2329
/* eslint-enable no-useless-constructor */
2430

2531
module.exports = {
26-
TimeoutError: TimeoutError
32+
TimeoutError: TimeoutError,
33+
ResourceCreationError: ResourceCreationError
2734
};

0 commit comments

Comments
 (0)