Skip to content

Commit 0dd57b4

Browse files
committed
Fix release of broken connections to the pool
An attempt to resolve a pending acquisition request can be made when connection is returned back to the pool. It will only be made when there exists a pending acquisition request and returned connection is valid. It was previously possible for the release operation to deque a pending acquisition request and forget about it, even when the returned connection was invalid. This made the acquisition request either fail with acquisition timeout or with "Cannot read property 'filter' of undefined" error. Later happened because subsequent release operation for the same key removed the array of pending acquisition requests. This commit fixes the problem by making release operation only deque an acquisition request when new request can be acquired.
1 parent 27f92ca commit 0dd57b4

File tree

2 files changed

+62
-3
lines changed

2 files changed

+62
-3
lines changed

src/v1/internal/pool.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,16 @@ class Pool {
158158
// check if there are any pending requests
159159
const requests = this._acquireRequests[key];
160160
if (requests) {
161-
const pending = requests.shift();
161+
const pending = requests[0];
162162

163163
if (pending) {
164164
const resource = this._acquire(key);
165165
if (resource) {
166-
pending.resolve(resource);
166+
// managed to acquire a valid resource from the pool to satisfy the pending acquire request
167+
requests.shift(); // forget the pending request
168+
pending.resolve(resource); // resolve the pending request with the acquired resource
167169

170+
// return early to not decrement the number of released resources
168171
return;
169172
}
170173
} else {

test/internal/pool.test.js

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,16 +522,72 @@ describe('Pool', () => {
522522
});
523523
});
524524

525+
it('should work fine when invalid resources released and acquisition attempt pending', done => {
526+
const key = 'bolt://localhost:7687';
527+
const acquisitionTimeout = 1000;
528+
let counter = 0;
529+
530+
const pool = new Pool(
531+
(url, release) => new Resource(url, counter++, release),
532+
resource => {
533+
},
534+
resourceValidOnlyOnceValidationFunction,
535+
new PoolConfig(2, acquisitionTimeout)
536+
);
537+
538+
pool.acquire(key).then(resource1 => {
539+
expect(resource1.id).toEqual(0);
540+
expect(pool.activeResourceCount(key)).toEqual(1);
541+
542+
pool.acquire(key).then(resource2 => {
543+
expect(resource2.id).toEqual(1);
544+
expect(pool.activeResourceCount(key)).toEqual(2);
545+
546+
// release both resources before the acquisition timeout, they should be treated as invalid
547+
setTimeout(() => {
548+
expectNumberOfAcquisitionRequests(pool, key, 1);
549+
resource1.close();
550+
resource2.close();
551+
}, acquisitionTimeout / 2);
552+
553+
pool.acquire(key).then(resource3 => {
554+
expect(resource3.id).toEqual(2);
555+
expectNoPendingAcquisitionRequests(pool);
556+
expect(pool.activeResourceCount(key)).toEqual(1);
557+
done();
558+
}).catch(error => {
559+
done.fail(error);
560+
});
561+
});
562+
});
563+
});
564+
525565
});
526566

527567
function expectNoPendingAcquisitionRequests(pool) {
528-
expect(pool._acquireRequests).toEqual({});
568+
const acquireRequests = pool._acquireRequests;
569+
Object.values(acquireRequests).forEach(requests => {
570+
if (Array.isArray(requests) && requests.length === 0) {
571+
requests = undefined;
572+
}
573+
expect(requests).not.toBeDefined();
574+
});
529575
}
530576

531577
function expectNumberOfAcquisitionRequests(pool, key, expectedNumber) {
532578
expect(pool._acquireRequests[key].length).toEqual(expectedNumber);
533579
}
534580

581+
function resourceValidOnlyOnceValidationFunction(resource) {
582+
// all resources are valid only once
583+
if (resource.validatedOnce) {
584+
return false;
585+
} else {
586+
resource.validatedOnce = true;
587+
return true;
588+
}
589+
}
590+
535591
class Resource {
536592

537593
constructor(key, id, release) {

0 commit comments

Comments
 (0)