Skip to content

Commit 17e4c88

Browse files
committed
feat: support creating collections and indexes in transactions
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
1 parent a5acfe0 commit 17e4c88

File tree

2 files changed

+104
-9
lines changed

2 files changed

+104
-9
lines changed

test/functional/spec-runner/index.js

+33-6
Original file line numberDiff line numberDiff line change
@@ -487,30 +487,57 @@ function resolveOperationArgs(operationName, operationArgs, context) {
487487
const CURSOR_COMMANDS = new Set(['find', 'aggregate', 'listIndexes', 'listCollections']);
488488
const ADMIN_COMMANDS = new Set(['listDatabases']);
489489

490+
function maybeSession(operation, context) {
491+
return (
492+
operation &&
493+
operation.arguments &&
494+
operation.arguments.session &&
495+
context[operation.arguments.session]
496+
);
497+
}
498+
490499
const kOperations = new Map([
491500
[
492501
'createIndex',
493-
(operation, collection /*, context, options */) => {
502+
(operation, collection, context /*, options */) => {
494503
const fieldOrSpec = operation.arguments.keys;
495-
return collection.createIndex(fieldOrSpec);
504+
const options = { session: maybeSession(operation, context) };
505+
if (operation.arguments.name) options.name = operation.arguments.name;
506+
return collection.createIndex(fieldOrSpec, options);
507+
}
508+
],
509+
[
510+
'createCollection',
511+
(operation, db, context /*, options */) => {
512+
const collectionName = operation.arguments.collection;
513+
const session = maybeSession(operation, context);
514+
return db.createCollection(collectionName, { session });
515+
}
516+
],
517+
[
518+
'dropCollection',
519+
(operation, db, context /*, options */) => {
520+
const collectionName = operation.arguments.collection;
521+
const session = maybeSession(operation, context);
522+
return db.dropCollection(collectionName, { session });
496523
}
497524
],
498525
[
499526
'dropIndex',
500527
(operation, collection /*, context, options */) => {
501528
const indexName = operation.arguments.name;
502-
return collection.dropIndex(indexName);
529+
const session = maybeSession(operation, context);
530+
return collection.dropIndex(indexName, { session });
503531
}
504532
],
505533
[
506534
'mapReduce',
507-
(operation, collection /*, context, options */) => {
535+
(operation, collection, context /*, options */) => {
508536
const args = operation.arguments;
509537
const map = args.map;
510538
const reduce = args.reduce;
511-
const options = {};
539+
const options = { session: maybeSession(operation, context) };
512540
if (args.out) options.out = args.out;
513-
514541
return collection.mapReduce(map, reduce, options);
515542
}
516543
]

test/functional/transactions.test.js

+71-3
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,66 @@ const loadSpecTests = require('../spec').loadSpecTests;
99
const generateTopologyTests = require('./spec-runner').generateTopologyTests;
1010
const MongoNetworkError = require('../../lib/core').MongoNetworkError;
1111

12+
function ignoreNsNotFoundForListIndexes(err) {
13+
if (err.code !== 26) {
14+
throw err;
15+
}
16+
17+
return [];
18+
}
19+
20+
class TransactionsRunnerContext extends TestRunnerContext {
21+
assertCollectionExists(options) {
22+
const client = this.sharedClient;
23+
const db = client.db(options.database);
24+
const collectionName = options.collection;
25+
26+
return db
27+
.listCollections()
28+
.toArray()
29+
.then(collections => expect(collections.some(coll => coll.name === collectionName)).to.be.ok);
30+
}
31+
32+
assertCollectionNotExists(options) {
33+
const client = this.sharedClient;
34+
const db = client.db(options.database);
35+
const collectionName = options.collection;
36+
37+
return db
38+
.listCollections()
39+
.toArray()
40+
.then(
41+
collections => expect(collections.every(coll => coll.name !== collectionName)).to.be.ok
42+
);
43+
}
44+
45+
assertIndexExists(options) {
46+
const client = this.sharedClient;
47+
const collection = client.db(options.database).collection(options.collection);
48+
const indexName = options.index;
49+
50+
return collection
51+
.listIndexes()
52+
.toArray()
53+
.catch(ignoreNsNotFoundForListIndexes)
54+
.then(indexes => expect(indexes.some(idx => idx.name === indexName)).to.be.ok);
55+
}
56+
57+
assertIndexNotExists(options) {
58+
const client = this.sharedClient;
59+
const collection = client.db(options.database).collection(options.collection);
60+
const indexName = options.index;
61+
62+
return collection
63+
.listIndexes()
64+
.toArray()
65+
.catch(ignoreNsNotFoundForListIndexes)
66+
.then(indexes => expect(indexes.every(idx => idx.name !== indexName)).to.be.ok);
67+
}
68+
}
69+
1270
describe('Transactions', function() {
13-
const testContext = new TestRunnerContext();
71+
const testContext = new TransactionsRunnerContext();
1472

1573
[
1674
{ name: 'spec tests', specPath: 'transactions' },
@@ -36,7 +94,17 @@ describe('Transactions', function() {
3694
// This test needs there to be multiple mongoses
3795
'increment txnNumber',
3896
// Skipping this until SPEC-1320 is resolved
39-
'remain pinned after non-transient error on commit'
97+
'remain pinned after non-transient error on commit',
98+
99+
// Will be implemented as part of NODE-2034
100+
'Client side error in command starting transaction',
101+
'Client side error when transaction is in progress',
102+
103+
// Will be implemented as part of NODE-2538
104+
'abortTransaction only retries once with RetryableWriteError from server',
105+
'abortTransaction does not retry without RetryableWriteError label',
106+
'commitTransaction does not retry error without RetryableWriteError label',
107+
'commitTransaction retries once with RetryableWriteError from server'
40108
];
41109

42110
return SKIP_TESTS.indexOf(spec.description) === -1;
@@ -151,7 +219,7 @@ describe('Transactions', function() {
151219

152220
const session = client.startSession();
153221
const db = client.db(configuration.db);
154-
db.createCollection('transaction_error_test', (err, coll) => {
222+
db.createCollection('transaction_error_test_2', (err, coll) => {
155223
expect(err).to.not.exist;
156224

157225
session.startTransaction();

0 commit comments

Comments
 (0)