-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(NODE-5915): refactor topology close logic to be synchronous #4021
Changes from 16 commits
c105a58
15d1b7a
7d97dfa
fb7d617
f69ab81
d1f3817
b5edf02
16b4bd6
4122b0a
48fa90a
e0a68ae
6aa97e0
3b40d9f
9f1d474
64da97f
b923d25
b9ee91c
aebb2da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,7 +28,6 @@ import { CancellationToken, TypedEventEmitter } from '../mongo_types'; | |
import type { Server } from '../sdam/server'; | ||
import { | ||
type Callback, | ||
eachAsync, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I agree that removing it is likely the way we want to go here. @nbbeeken @baileympearson thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Go for it |
||
List, | ||
makeCounter, | ||
promiseWithResolvers, | ||
|
@@ -493,25 +492,16 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> { | |
private interruptInUseConnections(minGeneration: number) { | ||
for (const connection of this[kCheckedOut]) { | ||
if (connection.generation <= minGeneration) { | ||
this.checkIn(connection); | ||
connection.onError(new PoolClearedOnNetworkError(this)); | ||
this.checkIn(connection); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @baileympearson this was done to fix a test failure where we were getting an uncaught error when running this spec test There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, this is right. Good catch. I think the reason this worked in the past was because
You made |
||
} | ||
} | ||
} | ||
|
||
/** Close the pool */ | ||
close(callback: Callback<void>): void; | ||
close(options: CloseOptions, callback: Callback<void>): void; | ||
close(_options?: CloseOptions | Callback<void>, _cb?: Callback<void>): void { | ||
let options = _options as CloseOptions; | ||
const callback = (_cb ?? _options) as Callback<void>; | ||
if (typeof options === 'function') { | ||
options = {}; | ||
} | ||
|
||
options = Object.assign({ force: false }, options); | ||
close(): void { | ||
if (this.closed) { | ||
return callback(); | ||
return; | ||
} | ||
|
||
// immediately cancel any in-flight connections | ||
|
@@ -526,21 +516,15 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> { | |
this.clearMinPoolSizeTimer(); | ||
this.processWaitQueue(); | ||
|
||
eachAsync<Connection>( | ||
this[kConnections].toArray(), | ||
(conn, cb) => { | ||
this.emitAndLog( | ||
ConnectionPool.CONNECTION_CLOSED, | ||
new ConnectionClosedEvent(this, conn, 'poolClosed') | ||
); | ||
conn.destroy({ force: !!options.force }, cb); | ||
}, | ||
err => { | ||
this[kConnections].clear(); | ||
this.emitAndLog(ConnectionPool.CONNECTION_POOL_CLOSED, new ConnectionPoolClosedEvent(this)); | ||
callback(err); | ||
} | ||
); | ||
for (const conn of this[kConnections]) { | ||
this.emitAndLog( | ||
ConnectionPool.CONNECTION_CLOSED, | ||
new ConnectionClosedEvent(this, conn, 'poolClosed') | ||
); | ||
conn.destroy(); | ||
} | ||
this[kConnections].clear(); | ||
this.emitAndLog(ConnectionPool.CONNECTION_POOL_CLOSED, new ConnectionPoolClosedEvent(this)); | ||
} | ||
|
||
/** | ||
|
@@ -592,7 +576,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> { | |
new ConnectionClosedEvent(this, connection, reason) | ||
); | ||
// destroy the connection | ||
process.nextTick(() => connection.destroy({ force: false })); | ||
connection.destroy(); | ||
} | ||
|
||
private connectionIsStale(connection: Connection) { | ||
|
@@ -648,7 +632,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> { | |
// The pool might have closed since we started trying to create a connection | ||
if (this[kPoolState] !== PoolState.ready) { | ||
this[kPending]--; | ||
connection.destroy({ force: true }); | ||
connection.destroy(); | ||
callback(this.closed ? new PoolClosedError(this) : new PoolClearedError(this)); | ||
return; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -552,7 +552,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> { | |
try { | ||
await promisify(callback => this.topology?.connect(options, callback))(); | ||
} catch (error) { | ||
this.topology?.close({ force: true }); | ||
this.topology?.close(); | ||
throw error; | ||
} | ||
}; | ||
|
@@ -614,19 +614,12 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> { | |
const topology = this.topology; | ||
this.topology = undefined; | ||
|
||
await new Promise<void>((resolve, reject) => { | ||
topology.close({ force }, error => { | ||
if (error) return reject(error); | ||
const { encrypter } = this[kOptions]; | ||
if (encrypter) { | ||
return encrypter.closeCallback(this, force, error => { | ||
if (error) return reject(error); | ||
resolve(); | ||
}); | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
topology.close(); | ||
|
||
const { encrypter } = this[kOptions]; | ||
if (encrypter) { | ||
await encrypter.close(this, force); | ||
} | ||
Comment on lines
+617
to
+622
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the investigation into whether these can throw or not. doesn't have to be in this PR but do you think it might be worth handling errors anyways, since we could introduce errors into |
||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import type { Document } from '../bson'; | ||
import { type AutoEncrypter } from '../client-side-encryption/auto_encrypter'; | ||
import { type CommandOptions, Connection, type DestroyOptions } from '../cmap/connection'; | ||
import { type CommandOptions, Connection } from '../cmap/connection'; | ||
import { | ||
ConnectionPool, | ||
type ConnectionPoolEvents, | ||
|
@@ -41,7 +41,6 @@ import type { GetMoreOptions } from '../operations/get_more'; | |
import type { ClientSession } from '../sessions'; | ||
import { isTransactionCommand } from '../transactions'; | ||
import { | ||
type Callback, | ||
type EventEmitterWithState, | ||
makeStateMachine, | ||
maxWireVersion, | ||
|
@@ -236,18 +235,8 @@ export class Server extends TypedEventEmitter<ServerEvents> { | |
} | ||
|
||
/** Destroy the server connection */ | ||
destroy(options?: DestroyOptions, callback?: Callback): void { | ||
if (typeof options === 'function') { | ||
callback = options; | ||
options = { force: false }; | ||
} | ||
options = Object.assign({}, { force: false }, options); | ||
|
||
destroy(): void { | ||
if (this.s.state === STATE_CLOSED) { | ||
if (typeof callback === 'function') { | ||
callback(); | ||
} | ||
|
||
return; | ||
} | ||
|
||
|
@@ -257,13 +246,9 @@ export class Server extends TypedEventEmitter<ServerEvents> { | |
this.monitor?.close(); | ||
} | ||
|
||
this.pool.close(options, err => { | ||
stateTransition(this, STATE_CLOSED); | ||
this.emit('closed'); | ||
if (typeof callback === 'function') { | ||
callback(err); | ||
} | ||
}); | ||
this.pool.close(); | ||
stateTransition(this, STATE_CLOSED); | ||
this.emit('closed'); | ||
Comment on lines
+249
to
+251
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same question as with |
||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's file a follow-up ticket in the V7 epic to remove
closeOptions
since its no longer used anywhere in the code base after these changes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good callout here. I can file the follow-up and the first subtask of that follow-up can be to mark it as deprecated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking a little closer, it seems the only places that interface was used are in
Topology.close
andConnectionPool.close
which are both internal classes, so users shouldn't be interacting with this interface at all. Seems like it wouldn't be a breaking change to just remove it in this PR along withDestroyOptions
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, even though it's only used in internal classes / types, it's public and exported so technically it's a part of our API. In situations like this in the past, we've considered it breaking to remove and done it in a major release