From c775be9f2c05abb085f5346d22936b30b2626f4e Mon Sep 17 00:00:00 2001 From: Iain Sproat <68657+iainsproat@users.noreply.github.com> Date: Wed, 26 Jun 2024 20:55:27 +0100 Subject: [PATCH 1/3] Adds additional metrics for remaining capacity, min, and max --- README.md | 9 +++++ src/Pool.ts | 12 +++++++ tests.js | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 92d3e71..ff6e8d4 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,12 @@ try { } } +// returns the maximum number of resources allowed by the pool. This can be configured when the pool is created. +pool.numMax(); + +// returns the minimum number of resources allowed by the pool. This can be configured when the pool is created. +pool.numMin(); + // releases the resource. pool.release(resource); @@ -115,6 +121,9 @@ pool.numPendingAcquires(); // how many asynchronous create calls are running pool.numPendingCreates(); +// the number of additional resources which can be created before the maximum is reached. This already deducts the number of resources that are currently being created. +pool.numRemainingCapacity(); + // waits for all resources to be returned to the pool and destroys them. // pool cannot be used after this. await pool.destroy(); diff --git a/src/Pool.ts b/src/Pool.ts index 9bcc90c..33ce995 100644 --- a/src/Pool.ts +++ b/src/Pool.ts @@ -164,6 +164,14 @@ export class Pool { this.eventId = 1; } + numMax() { + return this.max; + } + + numMin() { + return this.min; + } + numUsed() { return this.used.length; } @@ -184,6 +192,10 @@ export class Pool { return this.pendingCreates.length; } + numRemainingCapacity() { + return this.numMax() - (this.numUsed() + this.numPendingCreates()); + } + acquire() { const eventId = this.eventId++; this._executeEventHandlers('acquireRequest', eventId); diff --git a/tests.js b/tests.js index 409ee06..f3c9e3c 100644 --- a/tests.js +++ b/tests.js @@ -398,10 +398,13 @@ describe('Tarn', () => { pool.acquire().promise ]); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(0); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(4); expect(pool.numPendingCreates()).to.equal(4); + expect(pool.numRemainingCapacity()).to.equal(0); return acquirePromise.then(res => { expect(sortBy(res, 'a')).to.eql([{ a: 0 }, { a: 1 }, { a: 2 }, { a: 3 }]); @@ -409,10 +412,13 @@ describe('Tarn', () => { expect(createCalled).to.equal(4); expect(destroyCalled).to.equal(0); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(4); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(0); }); }); @@ -438,10 +444,13 @@ describe('Tarn', () => { pool.acquire().promise ]); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(0); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(4); expect(pool.numPendingCreates()).to.equal(4); + expect(pool.numRemainingCapacity()).to.equal(0); return acquirePromise.then(res => { expect(sortBy(res, 'a')).to.eql([{ a: 0 }, { a: 1 }, { a: 2 }, { a: 3 }]); @@ -449,10 +458,13 @@ describe('Tarn', () => { expect(createCalled).to.equal(4); expect(destroyCalled).to.equal(0); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(4); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(0); }); }); @@ -482,10 +494,13 @@ describe('Tarn', () => { expect(createCalled).to.equal(4); expect(destroyCalled).to.equal(0); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(4); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(0); }); }); @@ -517,10 +532,13 @@ describe('Tarn', () => { expect(createCalled).to.equal(2); expect(destroyCalled).to.equal(0); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(1); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(3); }); }); @@ -554,11 +572,13 @@ describe('Tarn', () => { expect(res).to.eql({ a: 2 }); expect(createCalled).to.equal(2); - + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(1); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(3); // Wait until the first resource has resolved return new Promise(resolve => setTimeout(resolve, 200)); @@ -592,10 +612,13 @@ describe('Tarn', () => { expect(sortBy(res, 'a')).to.eql([{ a: 0 }, { a: 1 }, { a: 2 }, { a: 3 }, { a: 4 }]); expect(createCalled).to.equal(5); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(5); expect(pool.numUsed()).to.equal(5); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(0); let newAcquires = [pool.acquire().promise, pool.acquire().promise, pool.acquire().promise]; @@ -616,10 +639,13 @@ describe('Tarn', () => { expect(newRes[2] === res[4]).to.equal(true); expect(createCalled).to.equal(5); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(5); expect(pool.numUsed()).to.equal(5); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(0); }); }); }); @@ -659,18 +685,24 @@ describe('Tarn', () => { expect(createCalled).to.equal(1); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(0); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(1); + expect(pool.numRemainingCapacity()).to.equal(3); return Promise.delay(100).then(() => { expect(createCalled).to.equal(1); expect(destroyCalled).to.equal(0); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(0); expect(pool.numFree()).to.equal(1); expect(pool.numPendingAcquires()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(4); done(); }); @@ -707,10 +739,13 @@ describe('Tarn', () => { expect(createCalled).to.equal(2); expect(destroyCalled).to.equal(0); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(2); expect(pool.numUsed()).to.equal(2); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(0); pool.release(res[0]); pool.release(res[1]); @@ -726,10 +761,13 @@ describe('Tarn', () => { expect(createCalled).to.equal(3); expect(destroyCalled).to.equal(1); expect(destroyed).to.eql({ a: 0, n: 1 }); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(2); expect(pool.numUsed()).to.equal(2); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(0); }); function acquire() { @@ -770,8 +808,11 @@ describe('Tarn', () => { .then(() => { // a resource should have been created and it should have been added to the free pool expect(createCalled).to.be(1); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(2); expect(pool.numUsed()).to.be(0); expect(pool.numFree()).to.be(1); + expect(pool.numRemainingCapacity()).to.equal(2); }); }); @@ -1088,19 +1129,25 @@ describe('Tarn', () => { expect(sortBy(res, 'a')).to.eql([{ a: 0 }, { a: 1 }, { a: 2 }]); expect(createCalled).to.equal(3); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(3); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(1); pool.release(res[2]); pool.release(res[1]); expect(createCalled).to.equal(3); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(1); expect(pool.numFree()).to.equal(2); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(3); return Promise.all([ pool.acquire().promise, @@ -1112,10 +1159,13 @@ describe('Tarn', () => { expect(res).to.eql([{ a: 1 }, { a: 2 }, { a: 3 }]); expect(createCalled).to.equal(4); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(4); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(0); }); }); @@ -1141,10 +1191,13 @@ describe('Tarn', () => { let pendingAcquire = pool.acquire(); expect(createCalled).to.equal(2); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(2); expect(pool.numUsed()).to.equal(2); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(1); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(0); setTimeout(() => { releaseCalled = true; @@ -1188,10 +1241,13 @@ describe('Tarn', () => { expect(createCalled).to.equal(2); expect(destroyCalled).to.equal(0); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(2); expect(pool.numUsed()).to.equal(2); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(0); }); }); }); @@ -1248,10 +1304,13 @@ describe('Tarn', () => { expect(createCalled).to.equal(3); expect(destroyCalled).to.equal(3); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(10); expect(pool.numUsed()).to.equal(0); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(10); }); }); @@ -1307,10 +1366,13 @@ describe('Tarn', () => { expect(createCalled).to.equal(3); expect(destroyCalled).to.equal(3); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(10); expect(pool.numUsed()).to.equal(0); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(10); }); }); @@ -1498,10 +1560,13 @@ describe('Tarn', () => { expect(duration - acquireTimeoutMillis).to.be.lessThan(50); expect(createCalled).to.equal(5); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(5); expect(pool.numUsed()).to.equal(5); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(0); done(); }) @@ -1544,10 +1609,13 @@ describe('Tarn', () => { expect(duration - acquireTimeoutMillis).to.be.lessThan(50); expect(createCalled).to.equal(1); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(0); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(4); done(); }) @@ -1586,10 +1654,13 @@ describe('Tarn', () => { expect(duration - acquireTimeoutMillis).to.be.lessThan(50); expect(createCalled).to.equal(1); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(0); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(4); done(); }) @@ -1642,10 +1713,13 @@ describe('Tarn', () => { expect(duration).to.be.lessThan(acquireTimeoutMillis - 500); expect(createCalled).to.equal(2); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(5); expect(pool.numUsed()).to.equal(1); expect(pool.numFree()).to.equal(0); expect(pool.numPendingAcquires()).to.equal(0); expect(pool.numPendingCreates()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(4); done(); }) @@ -1681,22 +1755,31 @@ describe('Tarn', () => { .then(res => { expect(sortBy(res, 'a')).to.eql([{ a: 0 }, { a: 1 }, { a: 2 }]); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(3); expect(pool.numFree()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(1); pool.release(res[0]); pool.release(res[1]); expect(destroyCalled).to.equal(0); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(1); expect(pool.numFree()).to.equal(2); + expect(pool.numRemainingCapacity()).to.equal(3); return Promise.delay(50); }) .then(() => { expect(destroyCalled).to.equal(0); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(1); expect(pool.numFree()).to.equal(2); + expect(pool.numRemainingCapacity()).to.equal(3); return Promise.delay(65); }) @@ -1704,8 +1787,11 @@ describe('Tarn', () => { expect(destroyed).to.eql([{ a: 0 }, { a: 1 }]); expect(destroyCalled).to.equal(2); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(1); expect(pool.numFree()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(3); done(); }) @@ -1796,23 +1882,32 @@ describe('Tarn', () => { .then(res => { expect(sortBy(res, 'a')).to.eql([{ a: 0 }, { a: 1 }, { a: 2 }, { a: 3 }]); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(4); expect(pool.numFree()).to.equal(0); + expect(pool.numRemainingCapacity()).to.equal(0); pool.release(findBy(res, 'a', 0)); pool.release(findBy(res, 'a', 1)); pool.release(findBy(res, 'a', 2)); expect(destroyCalled).to.equal(0); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(1); expect(pool.numFree()).to.equal(3); + expect(pool.numRemainingCapacity()).to.equal(3); return Promise.delay(50); }) .then(() => { expect(destroyCalled).to.equal(0); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(1); expect(pool.numFree()).to.equal(3); + expect(pool.numRemainingCapacity()).to.equal(3); return Promise.delay(60); }) @@ -1820,8 +1915,11 @@ describe('Tarn', () => { expect(sortBy(destroyed, 'a')).to.eql([{ a: 0 }, { a: 1 }]); expect(destroyCalled).to.equal(2); + expect(pool.numMin()).to.equal(2); + expect(pool.numMax()).to.equal(4); expect(pool.numUsed()).to.equal(1); expect(pool.numFree()).to.equal(1); + expect(pool.numRemainingCapacity()).to.equal(3); done(); }) @@ -1896,6 +1994,8 @@ describe('Tarn', () => { expect(createCalled).to.equal(4); expect(destroyCalled).to.equal(2); + expect(pool.numMin()).to.equal(0); + expect(pool.numMax()).to.equal(4); expect(pool.numFree() + pool.numUsed()).to.equal(2); done(); }) From dc27788b8ee26f0ac03c21683742497e197a504e Mon Sep 17 00:00:00 2001 From: Iain Sproat <68657+iainsproat@users.noreply.github.com> Date: Wed, 26 Jun 2024 21:30:22 +0100 Subject: [PATCH 2/3] Include pending acquisitions in the demand calculation --- src/Pool.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Pool.ts b/src/Pool.ts index 33ce995..30a1b5d 100644 --- a/src/Pool.ts +++ b/src/Pool.ts @@ -193,7 +193,13 @@ export class Pool { } numRemainingCapacity() { - return this.numMax() - (this.numUsed() + this.numPendingCreates()); + const demand = ( + this.numUsed() + + this.numPendingCreates() + + this.numPendingValidations() + + this.numPendingAcquires() + ); + return Math.max(0, this.numMax() - demand); } acquire() { From a2979630ff46f70ac4d4f89645d072c9ea852bdf Mon Sep 17 00:00:00 2001 From: Iain Sproat <68657+iainsproat@users.noreply.github.com> Date: Wed, 26 Jun 2024 22:35:28 +0100 Subject: [PATCH 3/3] Fix linting issue --- src/Pool.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Pool.ts b/src/Pool.ts index 30a1b5d..78259cd 100644 --- a/src/Pool.ts +++ b/src/Pool.ts @@ -193,12 +193,11 @@ export class Pool { } numRemainingCapacity() { - const demand = ( + const demand = this.numUsed() + this.numPendingCreates() + this.numPendingValidations() + - this.numPendingAcquires() - ); + this.numPendingAcquires(); return Math.max(0, this.numMax() - demand); }