Skip to content

Commit 36fa752

Browse files
authored
refactor(NODE-5915): topology close logic to be synchronous (#4021)
1 parent 937c9c8 commit 36fa752

18 files changed

+108
-249
lines changed

src/cmap/connect.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export async function connect(options: ConnectionOptions): Promise<Connection> {
4343
await performInitialHandshake(connection, options);
4444
return connection;
4545
} catch (error) {
46-
connection?.destroy({ force: false });
46+
connection?.destroy();
4747
throw error;
4848
}
4949
}

src/cmap/connection.ts

+1-11
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,6 @@ export interface ConnectionOptions
126126
mongoLogger?: MongoLogger | undefined;
127127
}
128128

129-
/** @internal */
130-
export interface DestroyOptions {
131-
/** Force the destruction. */
132-
force: boolean;
133-
}
134-
135129
/** @public */
136130
export type ConnectionEvents = {
137131
commandStarted(event: CommandStartedEvent): void;
@@ -301,14 +295,10 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
301295
}, 1).unref(); // No need for this timer to hold the event loop open
302296
}
303297

304-
public destroy(options: DestroyOptions, callback?: Callback): void {
298+
public destroy(): void {
305299
if (this.closed) {
306-
if (typeof callback === 'function') process.nextTick(callback);
307300
return;
308301
}
309-
if (typeof callback === 'function') {
310-
this.once('close', () => process.nextTick(() => callback()));
311-
}
312302

313303
// load balanced mode requires that these listeners remain on the connection
314304
// after cleanup on timeouts, errors or close so we remove them before calling

src/cmap/connection_pool.ts

+14-30
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import { CancellationToken, TypedEventEmitter } from '../mongo_types';
2828
import type { Server } from '../sdam/server';
2929
import {
3030
type Callback,
31-
eachAsync,
3231
List,
3332
makeCounter,
3433
promiseWithResolvers,
@@ -493,25 +492,16 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
493492
private interruptInUseConnections(minGeneration: number) {
494493
for (const connection of this[kCheckedOut]) {
495494
if (connection.generation <= minGeneration) {
496-
this.checkIn(connection);
497495
connection.onError(new PoolClearedOnNetworkError(this));
496+
this.checkIn(connection);
498497
}
499498
}
500499
}
501500

502501
/** Close the pool */
503-
close(callback: Callback<void>): void;
504-
close(options: CloseOptions, callback: Callback<void>): void;
505-
close(_options?: CloseOptions | Callback<void>, _cb?: Callback<void>): void {
506-
let options = _options as CloseOptions;
507-
const callback = (_cb ?? _options) as Callback<void>;
508-
if (typeof options === 'function') {
509-
options = {};
510-
}
511-
512-
options = Object.assign({ force: false }, options);
502+
close(): void {
513503
if (this.closed) {
514-
return callback();
504+
return;
515505
}
516506

517507
// immediately cancel any in-flight connections
@@ -526,21 +516,15 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
526516
this.clearMinPoolSizeTimer();
527517
this.processWaitQueue();
528518

529-
eachAsync<Connection>(
530-
this[kConnections].toArray(),
531-
(conn, cb) => {
532-
this.emitAndLog(
533-
ConnectionPool.CONNECTION_CLOSED,
534-
new ConnectionClosedEvent(this, conn, 'poolClosed')
535-
);
536-
conn.destroy({ force: !!options.force }, cb);
537-
},
538-
err => {
539-
this[kConnections].clear();
540-
this.emitAndLog(ConnectionPool.CONNECTION_POOL_CLOSED, new ConnectionPoolClosedEvent(this));
541-
callback(err);
542-
}
543-
);
519+
for (const conn of this[kConnections]) {
520+
this.emitAndLog(
521+
ConnectionPool.CONNECTION_CLOSED,
522+
new ConnectionClosedEvent(this, conn, 'poolClosed')
523+
);
524+
conn.destroy();
525+
}
526+
this[kConnections].clear();
527+
this.emitAndLog(ConnectionPool.CONNECTION_POOL_CLOSED, new ConnectionPoolClosedEvent(this));
544528
}
545529

546530
/**
@@ -592,7 +576,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
592576
new ConnectionClosedEvent(this, connection, reason)
593577
);
594578
// destroy the connection
595-
process.nextTick(() => connection.destroy({ force: false }));
579+
connection.destroy();
596580
}
597581

598582
private connectionIsStale(connection: Connection) {
@@ -648,7 +632,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
648632
// The pool might have closed since we started trying to create a connection
649633
if (this[kPoolState] !== PoolState.ready) {
650634
this[kPending]--;
651-
connection.destroy({ force: true });
635+
connection.destroy();
652636
callback(this.closed ? new PoolClosedError(this) : new PoolClearedError(this));
653637
return;
654638
}

src/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ export type {
275275
Connection,
276276
ConnectionEvents,
277277
ConnectionOptions,
278-
DestroyOptions,
279278
ProxyOptions
280279
} from './cmap/connection';
281280
export type {

src/mongo_client.ts

+7-14
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
552552
try {
553553
await promisify(callback => this.topology?.connect(options, callback))();
554554
} catch (error) {
555-
this.topology?.close({ force: true });
555+
this.topology?.close();
556556
throw error;
557557
}
558558
};
@@ -614,19 +614,12 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
614614
const topology = this.topology;
615615
this.topology = undefined;
616616

617-
await new Promise<void>((resolve, reject) => {
618-
topology.close({ force }, error => {
619-
if (error) return reject(error);
620-
const { encrypter } = this[kOptions];
621-
if (encrypter) {
622-
return encrypter.closeCallback(this, force, error => {
623-
if (error) return reject(error);
624-
resolve();
625-
});
626-
}
627-
resolve();
628-
});
629-
});
617+
topology.close();
618+
619+
const { encrypter } = this[kOptions];
620+
if (encrypter) {
621+
await encrypter.close(this, force);
622+
}
630623
}
631624

632625
/**

src/sdam/monitor.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ function resetMonitorState(monitor: Monitor) {
214214

215215
monitor[kCancellationToken].emit('cancel');
216216

217-
monitor.connection?.destroy({ force: true });
217+
monitor.connection?.destroy();
218218
monitor.connection = null;
219219
}
220220

@@ -247,7 +247,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
247247
);
248248

249249
function onHeartbeatFailed(err: Error) {
250-
monitor.connection?.destroy({ force: true });
250+
monitor.connection?.destroy();
251251
monitor.connection = null;
252252

253253
monitor.emitAndLogHeartbeat(
@@ -366,13 +366,13 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
366366
await performInitialHandshake(connection, monitor.connectOptions);
367367
return connection;
368368
} catch (error) {
369-
connection.destroy({ force: false });
369+
connection.destroy();
370370
throw error;
371371
}
372372
})().then(
373373
connection => {
374374
if (isInCloseState(monitor)) {
375-
connection.destroy({ force: true });
375+
connection.destroy();
376376
return;
377377
}
378378

@@ -479,7 +479,7 @@ export class RTTPinger {
479479
this.closed = true;
480480
clearTimeout(this[kMonitorId]);
481481

482-
this.connection?.destroy({ force: true });
482+
this.connection?.destroy();
483483
this.connection = undefined;
484484
}
485485
}
@@ -495,7 +495,7 @@ function measureRoundTripTime(rttPinger: RTTPinger, options: RTTPingerOptions) {
495495

496496
function measureAndReschedule(conn?: Connection) {
497497
if (rttPinger.closed) {
498-
conn?.destroy({ force: true });
498+
conn?.destroy();
499499
return;
500500
}
501501

@@ -529,7 +529,7 @@ function measureRoundTripTime(rttPinger: RTTPinger, options: RTTPingerOptions) {
529529
connection.command(ns('admin.$cmd'), { [commandName]: 1 }, undefined).then(
530530
() => measureAndReschedule(),
531531
() => {
532-
rttPinger.connection?.destroy({ force: true });
532+
rttPinger.connection?.destroy();
533533
rttPinger.connection = undefined;
534534
rttPinger[kRoundTripTime] = 0;
535535
return;

src/sdam/server.ts

+5-20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Document } from '../bson';
22
import { type AutoEncrypter } from '../client-side-encryption/auto_encrypter';
3-
import { type CommandOptions, Connection, type DestroyOptions } from '../cmap/connection';
3+
import { type CommandOptions, Connection } from '../cmap/connection';
44
import {
55
ConnectionPool,
66
type ConnectionPoolEvents,
@@ -41,7 +41,6 @@ import type { GetMoreOptions } from '../operations/get_more';
4141
import type { ClientSession } from '../sessions';
4242
import { isTransactionCommand } from '../transactions';
4343
import {
44-
type Callback,
4544
type EventEmitterWithState,
4645
makeStateMachine,
4746
maxWireVersion,
@@ -236,18 +235,8 @@ export class Server extends TypedEventEmitter<ServerEvents> {
236235
}
237236

238237
/** Destroy the server connection */
239-
destroy(options?: DestroyOptions, callback?: Callback): void {
240-
if (typeof options === 'function') {
241-
callback = options;
242-
options = { force: false };
243-
}
244-
options = Object.assign({}, { force: false }, options);
245-
238+
destroy(): void {
246239
if (this.s.state === STATE_CLOSED) {
247-
if (typeof callback === 'function') {
248-
callback();
249-
}
250-
251240
return;
252241
}
253242

@@ -257,13 +246,9 @@ export class Server extends TypedEventEmitter<ServerEvents> {
257246
this.monitor?.close();
258247
}
259248

260-
this.pool.close(options, err => {
261-
stateTransition(this, STATE_CLOSED);
262-
this.emit('closed');
263-
if (typeof callback === 'function') {
264-
callback(err);
265-
}
266-
});
249+
this.pool.close();
250+
stateTransition(this, STATE_CLOSED);
251+
this.emit('closed');
267252
}
268253

269254
/**

0 commit comments

Comments
 (0)