-
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
refactor(NODE-5915): refactor topology close logic to be synchronous #4021
Conversation
… github.com:mongodb/node-mongodb-native into NODE-5915/refactor-topology-close-to-be-synchronous
connection.onError(new PoolClearedOnNetworkError(this)); | ||
this.checkIn(connection); |
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.
@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 comment
The 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
checkIn
callsdestroyConnection()
, which callsconnection.destroy()
in a process.nextTick()- then `connection.onError() runs and shuts down the connection
- then
connection.destroy()
is a no-op because the connection has already been destroyed
You made destroyConnection
synchronous, so we need to run onError
first to ensure the connection is closed with the correct error. Then we run checkIn, which resets the pool state and calls connection.destroy()
, which is a no-op.
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.
left a few small comments, otherwise LGTM, nice work :)
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
and ConnectionPool.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 with DestroyOptions
.
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
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
The eachAsync
utils function is now also used nowhere in the codebase, do we want to remove it? It uses callbacks, and IIUC we no longer want to add any new callback code to the driver.
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 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Go for it
c533616
to
b9ee91c
Compare
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.
code changes LGTM! I can't seem to find the follow-up ticket, can you link the follow-up link on this ticket.
Follow-up ticket: NODE-6007 |
topology.close(); | ||
|
||
const { encrypter } = this[kOptions]; | ||
if (encrypter) { | ||
await encrypter.close(this, force); | ||
} |
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.
can close()
throw? If so, both the new and old implementations potentially would leave the encrypter
open if topology.close()
throws
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.
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 close()
in the future?
this.pool.close(); | ||
stateTransition(this, STATE_CLOSED); | ||
this.emit('closed'); |
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.
same question as with client.close()
- can pool.close()
throw?
@@ -469,7 +469,8 @@ export class Topology extends TypedEventEmitter<TopologyEvents> { | |||
selectServerOptions, | |||
(err, server) => { | |||
if (err) { | |||
return this.close({ force: false }, () => exitWithError(err)); | |||
this.close(); |
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.
same question about errors
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.
method | relevant direct dependencies | throws? |
---|---|---|
Connection.destroy |
- | No |
ConnectionPool.close |
Connection.destroy |
No |
Server.destroy |
stateTransition *, ConnectionPool.close |
No; stateTransition * can technically throw, but Server.destroy is written such that it will never try to make an illegal state transition. |
destroyServer |
Server.destroy |
No |
Topology.close |
destroyServer , stateTransition ** |
No; stateTransition ** can technically throw, but like in Server.destroy , Topology.close is written such that it never tries to make an illegal state transition |
Note:
stateTransition
* refers to the local function using the static state machine defined for the Server
class created by the makeStateMachine
helper.
stateTransition
** refers to the local function using the static state machine defined for the Topology
class created by the makeStateMachine
helper.
connection.onError(new PoolClearedOnNetworkError(this)); | ||
this.checkIn(connection); |
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.
Yeah, this is right. Good catch.
I think the reason this worked in the past was because
checkIn
callsdestroyConnection()
, which callsconnection.destroy()
in a process.nextTick()- then `connection.onError() runs and shuts down the connection
- then
connection.destroy()
is a no-op because the connection has already been destroyed
You made destroyConnection
synchronous, so we need to run onError
first to ensure the connection is closed with the correct error. Then we run checkIn, which resets the pool state and calls connection.destroy()
, which is a no-op.
Description
What is changing?
Connection.destroy
ConnectionPool.close
Server.destroy
Topology.close
DestroyOptions
as theforce
flag is unused in all the places it would have been passed inConnection.onError
inConnectionPool.interruptInUseConnections
due to failure on this testIs there new documentation needed for these changes?
No
What is the motivation for this change?
NODE-5915
Release Highlight
Double check the following
npm run check:lint
scripttype(NODE-xxxx)[!]: description
feat(NODE-1234)!: rewriting everything in coffeescript