Skip to content

Commit

Permalink
feat: support creating collections and indexes in transactions
Browse files Browse the repository at this point in the history
MongoDB 4.4 now supports creation of collections and indexes within
transactions. This patch includes that support as well as a spec
tests to validate the behavior.

NODE-2295
  • Loading branch information
mbroadst committed Apr 6, 2020
1 parent 98e879d commit 917f2b0
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 9 deletions.
39 changes: 33 additions & 6 deletions test/functional/spec-runner/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -473,30 +473,57 @@ function resolveOperationArgs(operationName, operationArgs, context) {
const CURSOR_COMMANDS = new Set(['find', 'aggregate', 'listIndexes', 'listCollections']);
const ADMIN_COMMANDS = new Set(['listDatabases']);

function maybeSession(operation, context) {
return (
operation &&
operation.arguments &&
operation.arguments.session &&
context[operation.arguments.session]
);
}

const kOperations = new Map([
[
'createIndex',
(operation, collection /*, context, options */) => {
(operation, collection, context /*, options */) => {
const fieldOrSpec = operation.arguments.keys;
return collection.createIndex(fieldOrSpec);
const options = { session: maybeSession(operation, context) };
if (operation.arguments.name) options.name = operation.arguments.name;
return collection.createIndex(fieldOrSpec, options);
}
],
[
'createCollection',
(operation, db, context /*, options */) => {
const collectionName = operation.arguments.collection;
const session = maybeSession(operation, context);
return db.createCollection(collectionName, { session });
}
],
[
'dropCollection',
(operation, db, context /*, options */) => {
const collectionName = operation.arguments.collection;
const session = maybeSession(operation, context);
return db.dropCollection(collectionName, { session });
}
],
[
'dropIndex',
(operation, collection /*, context, options */) => {
const indexName = operation.arguments.name;
return collection.dropIndex(indexName);
const session = maybeSession(operation, context);
return collection.dropIndex(indexName, { session });
}
],
[
'mapReduce',
(operation, collection /*, context, options */) => {
(operation, collection, context /*, options */) => {
const args = operation.arguments;
const map = args.map;
const reduce = args.reduce;
const options = {};
const options = { session: maybeSession(operation, context) };
if (args.out) options.out = args.out;

return collection.mapReduce(map, reduce, options);
}
]
Expand Down
74 changes: 71 additions & 3 deletions test/functional/transactions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,66 @@ const { TestRunnerContext, generateTopologyTests } = require('./spec-runner');
const { loadSpecTests } = require('../spec');
const { MongoNetworkError } = require('../../lib/error');

function ignoreNsNotFoundForListIndexes(err) {
if (err.code !== 26) {
throw err;
}

return [];
}

class TransactionsRunnerContext extends TestRunnerContext {
assertCollectionExists(options) {
const client = this.sharedClient;
const db = client.db(options.database);
const collectionName = options.collection;

return db
.listCollections()
.toArray()
.then(collections => expect(collections.some(coll => coll.name === collectionName)).to.be.ok);
}

assertCollectionNotExists(options) {
const client = this.sharedClient;
const db = client.db(options.database);
const collectionName = options.collection;

return db
.listCollections()
.toArray()
.then(
collections => expect(collections.every(coll => coll.name !== collectionName)).to.be.ok
);
}

assertIndexExists(options) {
const client = this.sharedClient;
const collection = client.db(options.database).collection(options.collection);
const indexName = options.index;

return collection
.listIndexes()
.toArray()
.catch(ignoreNsNotFoundForListIndexes)
.then(indexes => expect(indexes.some(idx => idx.name === indexName)).to.be.ok);
}

assertIndexNotExists(options) {
const client = this.sharedClient;
const collection = client.db(options.database).collection(options.collection);
const indexName = options.index;

return collection
.listIndexes()
.toArray()
.catch(ignoreNsNotFoundForListIndexes)
.then(indexes => expect(indexes.every(idx => idx.name !== indexName)).to.be.ok);
}
}

describe('Transactions', function() {
const testContext = new TestRunnerContext();
const testContext = new TransactionsRunnerContext();

[
{ name: 'spec tests', specPath: 'transactions' },
Expand All @@ -34,7 +92,17 @@ describe('Transactions', function() {
// This test needs there to be multiple mongoses
'increment txnNumber',
// Skipping this until SPEC-1320 is resolved
'remain pinned after non-transient error on commit'
'remain pinned after non-transient error on commit',

// Will be implemented as part of NODE-2034
'Client side error in command starting transaction',
'Client side error when transaction is in progress',

// Will be implemented as part of NODE-2538
'abortTransaction only retries once with RetryableWriteError from server',
'abortTransaction does not retry without RetryableWriteError label',
'commitTransaction does not retry error without RetryableWriteError label',
'commitTransaction retries once with RetryableWriteError from server'
];

return SKIP_TESTS.indexOf(spec.description) === -1;
Expand Down Expand Up @@ -132,7 +200,7 @@ describe('Transactions', function() {

const session = client.startSession();
const db = client.db(configuration.db);
db.createCollection('transaction_error_test', (err, coll) => {
db.createCollection('transaction_error_test_2', (err, coll) => {
expect(err).to.not.exist;

session.startTransaction();
Expand Down

0 comments on commit 917f2b0

Please sign in to comment.