Skip to content

Commit

Permalink
feat(shell-api): add support for snapshot reads MONGOSH-1151 (#1237)
Browse files Browse the repository at this point in the history
  • Loading branch information
addaleax authored Mar 17, 2022
1 parent 48137a1 commit 95ad859
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 15 deletions.
45 changes: 31 additions & 14 deletions packages/shell-api/src/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
} from './decorators';
import {
ChangeStreamOptions,
ClientSessionOptions,
CommandOperationOptions,
Document,
generateUri,
ListDatabasesOptions,
Expand Down Expand Up @@ -451,22 +453,37 @@ export default class Mongo extends ShellApiClass {

@topologies([Topologies.ReplSet])
startSession(options: Document = {}): Session {
const driverOptions = {};
if (options === undefined) {
return new Session(this, driverOptions, this._serviceProvider.startSession(driverOptions));
const allTransactionOptions = [
'readConcern', 'writeConcern', 'readPreference', 'maxCommitTimeMS'
] as const;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function assertAllTransactionOptionsUsed(_options: (typeof allTransactionOptions)[number]) {
// These typechecks might look weird, but will tell us if we are missing
// support for a newly introduced driver option when it is being added
// to the driver API.
}
assertAllTransactionOptionsUsed('' as Exclude<keyof TransactionOptions, keyof CommandOperationOptions>);
const defaultTransactionOptions: TransactionOptions = {};
for (const key of allTransactionOptions) {
if (typeof options[key] !== 'undefined') {
defaultTransactionOptions[key] = options[key];
}
}

const allSessionOptions = [ 'causalConsistency', 'snapshot' ] as const;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function assertAllSessionOptionsUsed(_options: (typeof allSessionOptions)[number] | 'defaultTransactionOptions') {}
assertAllSessionOptionsUsed('' as keyof ClientSessionOptions);
const driverOptions: ClientSessionOptions = {};
if (Object.keys(defaultTransactionOptions).length > 0) {
driverOptions.defaultTransactionOptions = defaultTransactionOptions;
}
for (const key of allSessionOptions) {
if (typeof options[key] !== 'undefined') {
driverOptions[key] = options[key];
}
}
const defaultTransactionOptions = {} as TransactionOptions;

// Only include option if not undef
Object.assign(defaultTransactionOptions,
options.readConcern && { readConcern: options.readConcern },
options.writeConcern && { writeConcern: options.writeConcern },
options.readPreference && { readPreference: options.readPreference }
);
Object.assign(driverOptions,
Object.keys(defaultTransactionOptions).length > 0 && { defaultTransactionOptions: defaultTransactionOptions },
options.causalConsistency !== undefined && { causalConsistency: options.causalConsistency }
);
return new Session(this, driverOptions, this._serviceProvider.startSession(driverOptions));
}

Expand Down
18 changes: 17 additions & 1 deletion packages/shell-api/src/session.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
ALL_TOPOLOGIES
} from './enums';
import { CliServiceProvider } from '../../service-provider-server';
import { startTestCluster, skipIfApiStrict } from '../../../testing/integration-testing-hooks';
import { startTestCluster, skipIfServerVersion, skipIfApiStrict } from '../../../testing/integration-testing-hooks';
import { ensureMaster, ensureSessionExists } from '../../../testing/helpers';
import Database from './database';
import { CommonErrors, MongoshInvalidInputError } from '@mongosh/errors';
Expand Down Expand Up @@ -227,6 +227,22 @@ describe('Session', () => {
}
expect.fail('Error not thrown');
});
context('with 5.0+ server', () => {
skipIfApiStrict();
skipIfServerVersion(srv0, '< 5.0');
it('starts a session with snapshot reads if requested', async() => {
session = mongo.startSession({ snapshot: true });
await session.getDatabase(databaseName).getCollection('coll').findOne({});
try {
await session.getDatabase(databaseName).getCollection('coll').insertOne({});
expect.fail('missed exception');
} catch (e) {
expect(e.message).to.include('snapshot'); // Cannot do writes with snapshot: true
}
expect(session._session.snapshotEnabled).to.equal(true);
await session.endSession();
});
});
});
describe('transaction methods are called', () => {
it('cannot call start transaction twice', () => {
Expand Down

0 comments on commit 95ad859

Please sign in to comment.