Skip to content

Commit 75345b1

Browse files
authored
fix: session pool should only create session if pending<=waiters (#791)
* fix: session pool should only create session if pending<=waiters The session pool would always initiate the creation of a new session if one was not available at the moment that an application requested a session, even when a large number of sessions were already pending creation. This could cause the pool to create more sessions than necessary for an application. Fixes #790. * fix: add clarifying comment
1 parent 42fdc15 commit 75345b1

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

src/session-pool.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ export class SessionPool extends EventEmitter implements SessionPoolInterface {
235235
_inventory: SessionInventory;
236236
_onClose!: Promise<void>;
237237
_pending = 0;
238+
_pendingPrepare = 0;
239+
_numWaiters = 0;
238240
_pingHandle!: NodeJS.Timer;
239241
_requests: PQueue;
240242
_traces: Map<string, trace.StackFrame[]>;
@@ -459,9 +461,13 @@ export class SessionPool extends EventEmitter implements SessionPoolInterface {
459461
// back into the pool. This will prevent the session to be marked as leaked if
460462
// the pool is closed while the session is being prepared.
461463
this._traces.delete(session.id);
464+
this._pendingPrepare++;
462465
this._prepareTransaction(session)
463466
.catch(() => (session.type = types.ReadOnly))
464-
.then(() => this._release(session));
467+
.then(() => {
468+
this._pendingPrepare--;
469+
this._release(session);
470+
});
465471
}
466472

467473
/**
@@ -772,7 +778,12 @@ export class SessionPool extends EventEmitter implements SessionPoolInterface {
772778
);
773779
}
774780

775-
if (!this.isFull) {
781+
// Only create a new session if there are more waiters than sessions already
782+
// being created. The current requester will be waiter number _numWaiters+1.
783+
if (
784+
!this.isFull &&
785+
this._pending + this._pendingPrepare <= this._numWaiters
786+
) {
776787
promises.push(
777788
new Promise((_, reject) => {
778789
this._createSession(type).catch(reject);
@@ -781,10 +792,13 @@ export class SessionPool extends EventEmitter implements SessionPoolInterface {
781792
}
782793

783794
try {
795+
this._numWaiters++;
784796
await Promise.race(promises);
785797
} catch (e) {
786798
removeListener!();
787799
throw e;
800+
} finally {
801+
this._numWaiters--;
788802
}
789803

790804
return this._borrowNextAvailableSession(type);

test/session-pool.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,28 @@ describe('SessionPool', () => {
11861186
assert.strictEqual(stub.callCount, 1);
11871187
});
11881188

1189+
it('should wait for a pending session to become available', async () => {
1190+
const fakeSession = createSession();
1191+
1192+
sessionPool.options.max = 2;
1193+
sessionPool._pending = 1;
1194+
const stub = sandbox
1195+
.stub(sessionPool, '_createSession')
1196+
.withArgs(types.ReadOnly)
1197+
.callsFake(() => {
1198+
return Promise.reject(new Error('should not be called'));
1199+
});
1200+
sandbox
1201+
.stub(sessionPool, '_borrowNextAvailableSession')
1202+
.withArgs(types.ReadOnly)
1203+
.returns(fakeSession);
1204+
setTimeout(() => sessionPool.emit('available'), 100);
1205+
1206+
const session = await sessionPool._getSession(types.ReadOnly, startTime);
1207+
assert.strictEqual(session, fakeSession);
1208+
assert.strictEqual(stub.callCount, 0);
1209+
});
1210+
11891211
it('should return any create errors', async () => {
11901212
const error = new Error('err');
11911213

0 commit comments

Comments
 (0)