Skip to content

Commit 95ad859

Browse files
authored
feat(shell-api): add support for snapshot reads MONGOSH-1151 (#1237)
1 parent 48137a1 commit 95ad859

File tree

2 files changed

+48
-15
lines changed

2 files changed

+48
-15
lines changed

packages/shell-api/src/mongo.ts

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import {
2121
} from './decorators';
2222
import {
2323
ChangeStreamOptions,
24+
ClientSessionOptions,
25+
CommandOperationOptions,
2426
Document,
2527
generateUri,
2628
ListDatabasesOptions,
@@ -451,22 +453,37 @@ export default class Mongo extends ShellApiClass {
451453

452454
@topologies([Topologies.ReplSet])
453455
startSession(options: Document = {}): Session {
454-
const driverOptions = {};
455-
if (options === undefined) {
456-
return new Session(this, driverOptions, this._serviceProvider.startSession(driverOptions));
456+
const allTransactionOptions = [
457+
'readConcern', 'writeConcern', 'readPreference', 'maxCommitTimeMS'
458+
] as const;
459+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
460+
function assertAllTransactionOptionsUsed(_options: (typeof allTransactionOptions)[number]) {
461+
// These typechecks might look weird, but will tell us if we are missing
462+
// support for a newly introduced driver option when it is being added
463+
// to the driver API.
464+
}
465+
assertAllTransactionOptionsUsed('' as Exclude<keyof TransactionOptions, keyof CommandOperationOptions>);
466+
const defaultTransactionOptions: TransactionOptions = {};
467+
for (const key of allTransactionOptions) {
468+
if (typeof options[key] !== 'undefined') {
469+
defaultTransactionOptions[key] = options[key];
470+
}
471+
}
472+
473+
const allSessionOptions = [ 'causalConsistency', 'snapshot' ] as const;
474+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
475+
function assertAllSessionOptionsUsed(_options: (typeof allSessionOptions)[number] | 'defaultTransactionOptions') {}
476+
assertAllSessionOptionsUsed('' as keyof ClientSessionOptions);
477+
const driverOptions: ClientSessionOptions = {};
478+
if (Object.keys(defaultTransactionOptions).length > 0) {
479+
driverOptions.defaultTransactionOptions = defaultTransactionOptions;
480+
}
481+
for (const key of allSessionOptions) {
482+
if (typeof options[key] !== 'undefined') {
483+
driverOptions[key] = options[key];
484+
}
457485
}
458-
const defaultTransactionOptions = {} as TransactionOptions;
459486

460-
// Only include option if not undef
461-
Object.assign(defaultTransactionOptions,
462-
options.readConcern && { readConcern: options.readConcern },
463-
options.writeConcern && { writeConcern: options.writeConcern },
464-
options.readPreference && { readPreference: options.readPreference }
465-
);
466-
Object.assign(driverOptions,
467-
Object.keys(defaultTransactionOptions).length > 0 && { defaultTransactionOptions: defaultTransactionOptions },
468-
options.causalConsistency !== undefined && { causalConsistency: options.causalConsistency }
469-
);
470487
return new Session(this, driverOptions, this._serviceProvider.startSession(driverOptions));
471488
}
472489

packages/shell-api/src/session.spec.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
ALL_TOPOLOGIES
1414
} from './enums';
1515
import { CliServiceProvider } from '../../service-provider-server';
16-
import { startTestCluster, skipIfApiStrict } from '../../../testing/integration-testing-hooks';
16+
import { startTestCluster, skipIfServerVersion, skipIfApiStrict } from '../../../testing/integration-testing-hooks';
1717
import { ensureMaster, ensureSessionExists } from '../../../testing/helpers';
1818
import Database from './database';
1919
import { CommonErrors, MongoshInvalidInputError } from '@mongosh/errors';
@@ -227,6 +227,22 @@ describe('Session', () => {
227227
}
228228
expect.fail('Error not thrown');
229229
});
230+
context('with 5.0+ server', () => {
231+
skipIfApiStrict();
232+
skipIfServerVersion(srv0, '< 5.0');
233+
it('starts a session with snapshot reads if requested', async() => {
234+
session = mongo.startSession({ snapshot: true });
235+
await session.getDatabase(databaseName).getCollection('coll').findOne({});
236+
try {
237+
await session.getDatabase(databaseName).getCollection('coll').insertOne({});
238+
expect.fail('missed exception');
239+
} catch (e) {
240+
expect(e.message).to.include('snapshot'); // Cannot do writes with snapshot: true
241+
}
242+
expect(session._session.snapshotEnabled).to.equal(true);
243+
await session.endSession();
244+
});
245+
});
230246
});
231247
describe('transaction methods are called', () => {
232248
it('cannot call start transaction twice', () => {

0 commit comments

Comments
 (0)