-
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
fix: allow client connect after close #2581
fix: allow client connect after close #2581
Conversation
Note most of this work comes from PR #2355. I added comments corresponding to some unresolved comments/important points from that PR. |
test/functional/connection.test.js
Outdated
@@ -273,4 +274,31 @@ describe('Connection', function () { | |||
done(); | |||
} | |||
}); | |||
|
|||
it('should be able to connect again after close', function () { | |||
return withClient.call(this, (client, done) => { |
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.
Following up on a discussion from #2355, I'm using this instead of just withClient((client, done) ...
because I couldn't see test output (warning messages or failures) without this change.
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.
@HanaPearlman Yes! this is super confusing, there's ticket in the back log to address this I added a comment in there about how to use this as I believe it was intended. NODE-2764
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.
Whoops, took a closer look at withClient
and it seems like the right approach is to bind withClient
to this
.
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.
@reggi Thanks for that! Happy to make that change now to replace the test callback or keep the bind
syntax; not 100% clear on what is best...
this.topology = undefined; | ||
this.s.dbCache = new Map(); | ||
this.s.sessions = new Set(); | ||
|
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.
Per a discussion from #2355, we don't restore this.s.options
to what it was originally constructed with here, so we get output warnings when re-connecting:
the options [servers] is not supported
the options [caseTranslate] is not supported
the options [dbName] is not supported
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.
I think this is fine for today. There's another unit of work here in the future (after the MongoClientOptions
project) where the options will be stored immutably, in which case this problem goes away.
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.
Just Some small things 👍
@@ -346,19 +346,29 @@ export class MongoClient extends EventEmitter implements OperationParent { | |||
const force = typeof forceOrCallback === 'boolean' ? forceOrCallback : false; | |||
|
|||
return maybePromise(callback, cb => { | |||
const completeClose = (err?: AnyError) => { |
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.
nit: naming of completeClose
, perhaps something like resetState
is more apt?
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.
Had a small offline discussion. Leaning towards leaving this as-is because completeClose
both resets the state and calls the callback. Does anyone else have thoughts on this?
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.
I'm fine with completeClose
, it's also a pattern we use elsewhere
test/functional/connection.test.js
Outdated
@@ -273,4 +274,31 @@ describe('Connection', function () { | |||
done(); | |||
} | |||
}); | |||
|
|||
it('should be able to connect again after close', function () { | |||
return withClient.bind(this)((client, done) => { |
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.
withClient
is totally wonk right now, but because you're not passing in any extra options to it, it can be used like this:
it('should be able to connect again after close', withClient(function (client, done) {
Note you can't use arrow function to maintain mocha's this
scoping.
9f31a73
to
c5e0309
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.
LGTM 👍 just suggested a few additional expectations in the new test
|
||
client.close(err => { | ||
expect(err).to.not.exist; | ||
|
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.
I'd add expect(client.isConnected()).to.be.false;
here
expect(err).to.not.exist; | ||
|
||
client.connect(err => { | ||
expect(err).to.not.exist; |
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.
And another expect(client.isConnected()).to.be.true;
here, just as a sanity check.
this.topology = undefined; | ||
this.s.dbCache = new Map(); | ||
this.s.sessions = new Set(); | ||
|
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.
I think this is fine for today. There's another unit of work here in the future (after the MongoClientOptions
project) where the options will be stored immutably, in which case this problem goes away.
@@ -346,19 +346,29 @@ export class MongoClient extends EventEmitter implements OperationParent { | |||
const force = typeof forceOrCallback === 'boolean' ? forceOrCallback : false; | |||
|
|||
return maybePromise(callback, cb => { | |||
const completeClose = (err?: AnyError) => { |
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.
I'm fine with completeClose
, it's also a pattern we use elsewhere
src/operations/connect.ts
Outdated
@@ -197,6 +197,11 @@ export function connect( | |||
throw new Error('no callback function provided'); | |||
} | |||
|
|||
// Has a connection already been established? | |||
if (mongoClient.topology && mongoClient.topology.isConnected()) { | |||
throw new Error(`'connect' cannot be called when already connected`); |
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 not use plain Error
objects, instead prefer a TypeError
for simple type mismatches and MongoError
or one of its subclasses for the rest. In this case I think a MongoError
would be best. (I know the code right above this uses an Error
, but let's be the change we want to see in the world right?)
@@ -123,7 +123,7 @@ describe('Sessions', function () { | |||
// verify that the `endSessions` command was sent | |||
const lastCommand = test.commands.started[test.commands.started.length - 1]; | |||
expect(lastCommand.commandName).to.equal('endSessions'); | |||
expect(client.topology.s.sessionPool.sessions).to.have.length(0); | |||
expect(client.topology).to.not.exist; |
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.
what's going on with the changes in this file? It looks like we're losing coverage
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.
As part of closing the MongoClient
, we set client.topology
to undefined
, so we cannot check the size of its session pool, we can just check that the topology no longer exists.
I agree it's not the best but it seems like we have to set the topology to undefined
(instead of just setting the state of the topology to closed) because otherwise it's ambiguous whether the topology 1) just hasn't been opened yet or 2) was closed/all cleaned up.
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.
Hmm. I think an implicit part of this work is that Topology
can be connected, closed and reused as well. A Topology
basically is a client, the MongoClient
being a wrapper around it to provide access to higher level API. Ideally we would want to assign a Topology
in the constructor of MongoClient
, rather than during connect
(but that's not quite possible today).
Have you tried not resetting topology = undefined
yet? This might require changes to the connect
operation. Let's not go down a huge rabbit hole here, but I'd like to better understand the limitations to that approach before opting for this alternative.
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.
Yep, you're right, it is possible to not set topology = undefined
after a small change to the connect
logic. This means we don't need to do any additional clean-up on client close, since any references to a Db
/Client
can use the same topology after re-connect. I've made this change, and will write a few tests to verify that the topology still receives the correct events after client re-connect.
Update: decided against making these changes because of the refactor required to re-connect topologies may be too much work for this ticket.
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.
LGTM
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.
🚀 🚀 🚀
b214b47
to
981226c
Compare
981226c
to
09dde51
Compare
This reverts commit 1aecf96.
connect
doesn't work again afterclose
has been called once on aMongoClient
.Cleaning up some state beforehand fixes the issue.
NODE-2544