-
Notifications
You must be signed in to change notification settings - Fork 1.8k
fix(NODE-3648): run get more ops through server selection #3030
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
Changes from 10 commits
0d3e37d
05db07a
7d57268
6dd5d5f
f21b49a
49f91c0
7076ae1
5abdf35
7f3c7d3
24c4750
2f2690e
fc88b8e
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 |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import type { Document, Long } from '../bson'; | ||
| import { MongoRuntimeError } from '../error'; | ||
| import type { Callback, MongoDBNamespace } from '../utils'; | ||
| import type { Server } from '../sdam/server'; | ||
| import { Aspect, AbstractOperation, OperationOptions, defineAspects } from './operation'; | ||
| import type { ClientSession } from '../sessions'; | ||
|
|
||
| /** | ||
| * @public | ||
| */ | ||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| export interface GetMoreOptions extends OperationOptions { | ||
| /** Set the batchSize for the getMoreCommand when iterating over the query results. */ | ||
| batchSize?: number; | ||
| /** You can put a $comment field on a query to make looking in the profiler logs simpler. */ | ||
| comment?: string | Document; | ||
| /** Number of milliseconds to wait before aborting the query. */ | ||
| maxTimeMS?: number; | ||
| } | ||
|
|
||
| /** @internal */ | ||
| export class GetMoreOperation extends AbstractOperation { | ||
| cursorId: Long; | ||
| options: GetMoreOptions; | ||
| server: Server; | ||
|
|
||
| constructor(ns: MongoDBNamespace, cursorId: Long, server: Server, options: GetMoreOptions = {}) { | ||
| super(options); | ||
| this.options = options; | ||
| this.ns = ns; | ||
| this.cursorId = cursorId; | ||
| this.server = server; | ||
| } | ||
|
|
||
| /** | ||
| * Although there is a server already associated with the get more operation, the signature | ||
| * for execute passes a server so we will just use that one. | ||
|
nbbeeken marked this conversation as resolved.
|
||
| */ | ||
| execute(server: Server, session: ClientSession, callback: Callback<Document>): void { | ||
| if (server !== this.server) { | ||
| return callback( | ||
| new MongoRuntimeError('Getmore must run on the same server operation began on') | ||
| ); | ||
| } | ||
| server.getMore(this.ns, this.cursorId, this.options, callback); | ||
| } | ||
| } | ||
|
|
||
| defineAspects(GetMoreOperation, [Aspect.READ_OPERATION, Aspect.CURSOR_ITERATING]); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| 'use strict'; | ||
|
|
||
| const sinon = require('sinon'); | ||
| const { expect } = require('chai'); | ||
| const { Long } = require('../../../src/bson'); | ||
| const { GetMoreOperation } = require('../../../src/operations/get_more'); | ||
| const { Server } = require('../../../src/sdam/server'); | ||
| const { ClientSession } = require('../../../src/sessions'); | ||
| const { ReadPreference } = require('../../../src/read_preference'); | ||
| const { Aspect } = require('../../../src/operations/operation'); | ||
| const { MongoRuntimeError } = require('../../../src/error'); | ||
|
|
||
| describe('GetMoreOperation', function () { | ||
|
dariakp marked this conversation as resolved.
|
||
| const ns = 'db.coll'; | ||
| const cursorId = Long.fromNumber(1); | ||
| const options = { | ||
|
Contributor
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 we freeze these so that we can make sure the options aren't mutated? (otherwise that deep equal check below doesn't guarantee as much)
Contributor
Author
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. Now all params are frozen. |
||
| batchSize: 100, | ||
| comment: 'test', | ||
| maxTimeMS: 500, | ||
| readPreference: ReadPreference.primary | ||
| }; | ||
|
|
||
| describe('#constructor', function () { | ||
| const server = sinon.createStubInstance(Server, {}); | ||
| const operation = new GetMoreOperation(ns, cursorId, server, options); | ||
|
|
||
| it('sets the namespace', function () { | ||
| expect(operation.ns).to.equal(ns); | ||
| }); | ||
|
|
||
| it('sets the cursorId', function () { | ||
| expect(operation.cursorId).to.equal(cursorId); | ||
| }); | ||
|
|
||
| it('sets the server', function () { | ||
| expect(operation.server).to.equal(server); | ||
| }); | ||
|
|
||
| it('sets the options', function () { | ||
| expect(operation.options).to.deep.equal(options); | ||
| }); | ||
| }); | ||
|
|
||
| describe('#execute', function () { | ||
| context('when the server is the same as the instance', function () { | ||
| const getMoreStub = sinon.stub().yields(undefined); | ||
| const server = sinon.createStubInstance(Server, { | ||
| getMore: getMoreStub | ||
| }); | ||
| const session = sinon.createStubInstance(ClientSession); | ||
| const opts = { ...options, session }; | ||
| const operation = new GetMoreOperation(ns, cursorId, server, opts); | ||
|
|
||
| it('executes a getmore on the provided server', function (done) { | ||
| const callback = () => { | ||
| const call = getMoreStub.getCall(0); | ||
| expect(getMoreStub.calledOnce).to.be.true; | ||
| expect(call.args[0]).to.equal(ns); | ||
| expect(call.args[1]).to.equal(cursorId); | ||
| expect(call.args[2]).to.deep.equal(opts); | ||
| done(); | ||
| }; | ||
| operation.execute(server, session, callback); | ||
| }); | ||
| }); | ||
|
|
||
| context('when the server is not the same as the instance', function () { | ||
| const getMoreStub = sinon.stub().yields(undefined); | ||
| const server = sinon.createStubInstance(Server, { | ||
| getMore: getMoreStub | ||
| }); | ||
| const newServer = sinon.createStubInstance(Server, { | ||
| getMore: getMoreStub | ||
| }); | ||
| const session = sinon.createStubInstance(ClientSession); | ||
| const opts = { ...options, session }; | ||
| const operation = new GetMoreOperation(ns, cursorId, server, opts); | ||
|
|
||
| it('errors in the callback', function (done) { | ||
| const callback = error => { | ||
| expect(error).to.be.instanceOf(MongoRuntimeError); | ||
| expect(error.message).to.equal('Getmore must run on the same server operation began on'); | ||
| done(); | ||
| }; | ||
| operation.execute(newServer, session, callback); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| describe('#hasAspect', function () { | ||
| const server = sinon.createStubInstance(Server, {}); | ||
| const operation = new GetMoreOperation(ns, cursorId, server, options); | ||
|
|
||
| context('when the aspect is cursor iterating', function () { | ||
| it('returns true', function () { | ||
| expect(operation.hasAspect(Aspect.CURSOR_ITERATING)).to.be.true; | ||
| }); | ||
| }); | ||
|
|
||
| context('when the aspect is read', function () { | ||
| it('returns true', function () { | ||
| expect(operation.hasAspect(Aspect.READ_OPERATION)).to.be.true; | ||
| }); | ||
| }); | ||
|
|
||
| context('when the aspect is write', function () { | ||
| it('returns false', function () { | ||
| expect(operation.hasAspect(Aspect.WRITE_OPERATION)).to.be.false; | ||
| }); | ||
| }); | ||
|
|
||
| context('when the aspect is retryable', function () { | ||
| it('returns false', function () { | ||
| expect(operation.hasAspect(Aspect.RETRYABLE)).to.be.false; | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.