Skip to content

Commit 917f2b0

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 98e879d commit 917f2b0

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
@@ -473,30 +473,57 @@ function resolveOperationArgs(operationName, operationArgs, context) {
473473
const CURSOR_COMMANDS = new Set(['find', 'aggregate', 'listIndexes', 'listCollections']);
474474
const ADMIN_COMMANDS = new Set(['listDatabases']);
475475

476+
function maybeSession(operation, context) {
477+
return (
478+
operation &&
479+
operation.arguments &&
480+
operation.arguments.session &&
481+
context[operation.arguments.session]
482+
);
483+
}
484+
476485
const kOperations = new Map([
477486
[
478487
'createIndex',
479-
(operation, collection /*, context, options */) => {
488+
(operation, collection, context /*, options */) => {
480489
const fieldOrSpec = operation.arguments.keys;
481-
return collection.createIndex(fieldOrSpec);
490+
const options = { session: maybeSession(operation, context) };
491+
if (operation.arguments.name) options.name = operation.arguments.name;
492+
return collection.createIndex(fieldOrSpec, options);
493+
}
494+
],
495+
[
496+
'createCollection',
497+
(operation, db, context /*, options */) => {
498+
const collectionName = operation.arguments.collection;
499+
const session = maybeSession(operation, context);
500+
return db.createCollection(collectionName, { session });
501+
}
502+
],
503+
[
504+
'dropCollection',
505+
(operation, db, context /*, options */) => {
506+
const collectionName = operation.arguments.collection;
507+
const session = maybeSession(operation, context);
508+
return db.dropCollection(collectionName, { session });
482509
}
483510
],
484511
[
485512
'dropIndex',
486513
(operation, collection /*, context, options */) => {
487514
const indexName = operation.arguments.name;
488-
return collection.dropIndex(indexName);
515+
const session = maybeSession(operation, context);
516+
return collection.dropIndex(indexName, { session });
489517
}
490518
],
491519
[
492520
'mapReduce',
493-
(operation, collection /*, context, options */) => {
521+
(operation, collection, context /*, options */) => {
494522
const args = operation.arguments;
495523
const map = args.map;
496524
const reduce = args.reduce;
497-
const options = {};
525+
const options = { session: maybeSession(operation, context) };
498526
if (args.out) options.out = args.out;
499-
500527
return collection.mapReduce(map, reduce, options);
501528
}
502529
]

test/functional/transactions.test.js

+71-3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,66 @@ const { TestRunnerContext, generateTopologyTests } = require('./spec-runner');
77
const { loadSpecTests } = require('../spec');
88
const { MongoNetworkError } = require('../../lib/error');
99

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

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

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

133201
const session = client.startSession();
134202
const db = client.db(configuration.db);
135-
db.createCollection('transaction_error_test', (err, coll) => {
203+
db.createCollection('transaction_error_test_2', (err, coll) => {
136204
expect(err).to.not.exist;
137205

138206
session.startTransaction();

0 commit comments

Comments
 (0)