-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(NODE-4650): handle handshake errors with SDAM #3426
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
Merged
Merged
Changes from 5 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
c6bcaa5
fix: handle handshake errors with SDAM
dariakp f7d224c
test: unskip SDAM auth tests
dariakp 1700c79
style: remove stray comment
dariakp 08a7a12
feat: add stale error check logic for sdam
dariakp d8a5ee3
test: add unit tests
dariakp aac6da4
lint
dariakp e548514
refactor: use variable name instead of comment
dariakp File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| import { expect } from 'chai'; | ||
| import { once } from 'events'; | ||
| import * as sinon from 'sinon'; | ||
|
|
||
| import { | ||
| Connection, | ||
| MongoError, | ||
| MongoErrorLabel, | ||
| MongoNetworkError, | ||
| MongoNetworkTimeoutError, | ||
| ObjectId, | ||
| ServerType, | ||
| TopologyType | ||
| } from '../../../src'; | ||
| import { Server } from '../../../src/sdam/server'; | ||
| import { ServerDescription } from '../../../src/sdam/server_description'; | ||
| import { Topology } from '../../../src/sdam/topology'; | ||
| import { sleep } from '../../tools/utils'; | ||
|
|
||
| const handledErrors = [ | ||
| { | ||
| description: 'any non-timeout network error', | ||
| errorClass: MongoNetworkError, | ||
| errorArgs: ['TestError'] | ||
| }, | ||
| { | ||
| description: 'a network timeout error before handshake', | ||
| errorClass: MongoNetworkTimeoutError, | ||
| errorArgs: ['TestError', { beforeHandshake: true }] | ||
| }, | ||
| { | ||
| description: 'an auth handshake error', | ||
| errorClass: MongoError, | ||
| errorArgs: ['TestError'], | ||
| errorLabel: MongoErrorLabel.HandshakeError | ||
| } | ||
| ]; | ||
|
|
||
| const unhandledErrors = [ | ||
| { | ||
| description: 'a non-MongoError', | ||
| errorClass: Error, | ||
| errorArgs: ['TestError'] | ||
| }, | ||
| { | ||
| description: 'a network timeout error after handshake', | ||
| errorClass: MongoNetworkTimeoutError, | ||
| errorArgs: ['TestError', { beforeHandshake: false }] | ||
| }, | ||
| { | ||
| description: 'a non-network non-handshake MongoError', | ||
| errorClass: MongoError, | ||
| errorArgs: ['TestError'] | ||
| } | ||
| ]; | ||
|
|
||
| describe('Server', () => { | ||
| describe('#handleError', () => { | ||
| let server: Server, connection: Connection | undefined; | ||
| beforeEach(() => { | ||
| server = new Server(new Topology([], {} as any), new ServerDescription('a:1'), {} as any); | ||
| }); | ||
| for (const loadBalanced of [true, false]) { | ||
| const mode = loadBalanced ? 'loadBalanced' : 'non-loadBalanced'; | ||
| const contextSuffix = loadBalanced ? ' with connection provided' : ''; | ||
| context(`in ${mode} mode${contextSuffix}`, () => { | ||
| beforeEach(() => { | ||
| if (loadBalanced) { | ||
| server.s.topology.description.type = TopologyType.LoadBalanced; | ||
| connection = { serviceId: new ObjectId() } as Connection; | ||
| server.s.pool.clear = sinon.stub(); | ||
| } else { | ||
| connection = undefined; | ||
| } | ||
| }); | ||
| for (const { description, errorClass, errorArgs, errorLabel } of handledErrors) { | ||
| const handledDescription = loadBalanced | ||
| ? `should reset the pool but not attach a ResetPool label to the error or mark the server unknown on ${description}` | ||
| : `should attach a ResetPool label to the error and mark the server unknown on ${description}`; | ||
| it(`${handledDescription}`, async () => { | ||
| // @ts-expect-error because of varied number of args | ||
| const error = new errorClass(...errorArgs); | ||
| if (errorLabel) { | ||
| error.addErrorLabel(errorLabel); | ||
| } | ||
| const newDescriptionEvent = Promise.race([ | ||
| once(server, Server.DESCRIPTION_RECEIVED), | ||
| sleep(1000) | ||
| ]); | ||
| server.handleError(error, connection); | ||
| if (!loadBalanced) { | ||
| expect( | ||
| error.hasErrorLabel(MongoErrorLabel.ResetPool), | ||
| 'expected error to have a ResetPool label' | ||
| ).to.be.true; | ||
| } else { | ||
| expect( | ||
| error.hasErrorLabel(MongoErrorLabel.ResetPool), | ||
| 'expected error NOT to have a ResetPool label' | ||
| ).to.be.false; | ||
| } | ||
| const newDescription = await newDescriptionEvent; | ||
| if (!loadBalanced) { | ||
| expect(newDescription).to.have.nested.property('[0].type', ServerType.Unknown); | ||
| } else { | ||
| expect(newDescription).to.be.undefined; | ||
| expect(server.s.pool.clear).to.have.been.calledOnceWith(connection!.serviceId); | ||
| } | ||
| }); | ||
|
|
||
| it(`should not attach a ResetPool label to the error or mark the server unknown on ${description} if it is stale`, async () => { | ||
dariakp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // @ts-expect-error because of varied number of args | ||
| const error = new errorClass(...errorArgs); | ||
| if (errorLabel) { | ||
| error.addErrorLabel(errorLabel); | ||
| } | ||
|
|
||
| error.connectionGeneration = -1; | ||
| expect( | ||
| server.s.pool.generation, | ||
| 'expected test server to have a pool of generation 0' | ||
| ).to.equal(0); // sanity check | ||
|
|
||
| const newDescriptionEvent = Promise.race([ | ||
| once(server, Server.DESCRIPTION_RECEIVED), | ||
| sleep(1000) | ||
| ]); | ||
| server.handleError(error, connection); | ||
| expect( | ||
| error.hasErrorLabel(MongoErrorLabel.ResetPool), | ||
| 'expected error NOT to have a ResetPool label' | ||
| ).to.be.false; | ||
| const newDescription = await newDescriptionEvent; | ||
| expect(newDescription).to.be.undefined; | ||
| }); | ||
| } | ||
|
|
||
| for (const { description, errorClass, errorArgs } of unhandledErrors) { | ||
| it(`should not attach a ResetPool label to the error or mark the server unknown on ${description}`, async () => { | ||
| // @ts-expect-error because of varied number of args | ||
| const error = new errorClass(...errorArgs); | ||
|
|
||
| const newDescriptionEvent = Promise.race([ | ||
| once(server, Server.DESCRIPTION_RECEIVED), | ||
| sleep(1000) | ||
| ]); | ||
| server.handleError(error, connection); | ||
| if (error instanceof MongoError) { | ||
| expect( | ||
| error.hasErrorLabel(MongoErrorLabel.ResetPool), | ||
| 'expected error NOT to have a ResetPool label' | ||
| ).to.be.false; | ||
| } | ||
| const newDescription = await newDescriptionEvent; | ||
| expect(newDescription).to.be.undefined; | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| }); | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.