Skip to content

Commit c0a1be5

Browse files
authored
fix: close micro race window between connect() resolve and abort listener removal (#700)
2 parents 4409e12 + 0b66bab commit c0a1be5

1 file changed

Lines changed: 25 additions & 4 deletions

File tree

src/documentdb/ClustersClient.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,21 @@ export class ClustersClient {
295295
throw new UserCancelledError('abortConnection');
296296
}
297297

298+
// Track whether connect() has resolved so the abort handler can avoid
299+
// closing an already-connected client during the micro window between
300+
// connect() resolving and removeEventListener firing. This window exists
301+
// because abort events are dispatched synchronously when abort() is
302+
// called: if abort() fires during the synchronous continuation after
303+
// connect() resolves but before the listener is removed, the handler
304+
// would close the now-connected client without this guard.
305+
let connected = false;
306+
298307
// Wire up abort: closing the client causes the pending connect() to reject
299308
const onAbort = (): void => {
309+
if (connected) {
310+
// connect() already resolved — do not close the connected client.
311+
return;
312+
}
300313
ext.outputChannel.debug('AbortSignal fired — closing MongoClient to interrupt connection handshake.');
301314
void this._mongoClient.close().catch(() => {
302315
// Ignore close errors during abort cleanup
@@ -306,10 +319,13 @@ export class ClustersClient {
306319

307320
try {
308321
await this._mongoClient.connect();
322+
connected = true;
309323

310-
// Remove the abort listener immediately after connect() resolves so that
311-
// a late cancellation during synchronous API init below cannot close an
312-
// already-connected client while the method continues as "successful".
324+
// Remove the abort listener immediately after connect() resolves so
325+
// that a late cancellation during synchronous API init below cannot
326+
// close an already-connected client while the method continues as
327+
// "successful". The connected flag above serves as a belt-and-suspenders
328+
// guard in case abort fires between the assignment and this removal.
313329
abortSignal?.removeEventListener('abort', onAbort);
314330

315331
this._llmEnhancedFeatureApis = new llmEnhancedFeatureApis(this._mongoClient);
@@ -336,7 +352,12 @@ export class ClustersClient {
336352
}
337353
throw error;
338354
} finally {
339-
abortSignal?.removeEventListener('abort', onAbort);
355+
// Clean up the abort listener if we didn't already remove it in the
356+
// success path (i.e., connect() failed). In the success path the
357+
// listener was already removed above.
358+
if (abortSignal && !connected) {
359+
abortSignal.removeEventListener('abort', onAbort);
360+
}
340361
}
341362
}
342363

0 commit comments

Comments
 (0)