1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
// Test that no matter the readConcern specified on a multi-statement transaction, it has snapshot
// isolation. Also test that readConcern linearizable and available are not allowed with a
// transaction.
// The test runs commands that are not allowed with security token: endSession.
// @tags: [
// not_allowed_with_security_token,uses_transactions, requires_majority_read_concern]
(function() {
"use strict";
const dbName = "test";
const collName = "read_concern";
const testDB = db.getSiblingDB(dbName);
const testColl = testDB[collName];
testDB.runCommand({drop: collName, writeConcern: {w: "majority"}});
assert.commandWorked(testDB.createCollection(testColl.getName(), {writeConcern: {w: "majority"}}));
const sessionOptions = {
causalConsistency: false
};
const session = db.getMongo().startSession(sessionOptions);
const sessionDb = session.getDatabase(dbName);
function testReadConcernMaintainsSnapshotIsolationInTransaction(readConcern) {
jsTest.log(
"Test that the following multi-document transaction has snapshot isolation with readConcern: " +
tojson(readConcern));
assert.commandWorked(testColl.remove({}, {writeConcern: {w: "majority"}}));
// Start a new transaction with the given readConcern.
session.startTransaction();
let command = {find: collName};
if (readConcern) {
Object.extend(command, {readConcern: readConcern});
}
assert.commandWorked(sessionDb.runCommand(command));
// Insert a document outside of the transaction.
assert.commandWorked(testColl.insert({_id: 0}, {writeConcern: {w: "majority"}}));
// Test that the transaction does not see the new document (it has snapshot isolation).
let res = assert.commandWorked(sessionDb.runCommand({find: collName}));
assert.eq(res.cursor.firstBatch.length, 0, tojson(res));
// Commit the transaction.
assert.commandWorked(session.commitTransaction_forTesting());
}
testReadConcernMaintainsSnapshotIsolationInTransaction(null);
testReadConcernMaintainsSnapshotIsolationInTransaction({});
testReadConcernMaintainsSnapshotIsolationInTransaction({level: "local"});
testReadConcernMaintainsSnapshotIsolationInTransaction({level: "majority"});
testReadConcernMaintainsSnapshotIsolationInTransaction({level: "snapshot"});
function testReadConcernNotSupportedInTransaction(readConcern) {
jsTest.log(
"Test that the following readConcern is not supported in a multi-document transaction: " +
readConcern);
// Start a new transaction with the given readConcern.
session.startTransaction();
assert.commandFailedWithCode(sessionDb.runCommand({find: collName, readConcern: readConcern}),
ErrorCodes.InvalidOptions);
// No more operations are allowed in the transaction.
assert.commandFailedWithCode(sessionDb.runCommand({find: collName}),
ErrorCodes.NoSuchTransaction);
assert.commandFailedWithCode(session.abortTransaction_forTesting(),
ErrorCodes.NoSuchTransaction);
}
testReadConcernNotSupportedInTransaction({level: "available"});
testReadConcernNotSupportedInTransaction({level: "linearizable"});
jsTest.log("Test starting a transaction with an invalid readConcern");
// Start a new transaction with the given readConcern.
session.startTransaction();
assert.commandFailedWithCode(sessionDb.runCommand({find: collName, readConcern: {level: "bad"}}),
ErrorCodes.FailedToParse);
// No more operations are allowed in the transaction.
assert.commandFailedWithCode(sessionDb.runCommand({find: collName}), ErrorCodes.NoSuchTransaction);
assert.commandFailedWithCode(session.abortTransaction_forTesting(), ErrorCodes.NoSuchTransaction);
jsTest.log("Test specifying readConcern on the second statement in a transaction");
// Start a new transaction with snapshot readConcern.
session.startTransaction();
assert.commandWorked(sessionDb.runCommand({find: collName, readConcern: {level: "snapshot"}}));
// The second statement cannot specify a readConcern.
assert.commandFailedWithCode(
sessionDb.runCommand({find: collName, readConcern: {level: "snapshot"}}),
ErrorCodes.InvalidOptions);
// The transaction is still active and can be committed.
assert.commandWorked(session.commitTransaction_forTesting());
jsTest.log("Test specifying non-snapshot readConcern on the second statement in a transaction");
// Start a new transaction with majority readConcern.
session.startTransaction();
assert.commandWorked(sessionDb.runCommand({find: collName, readConcern: {level: "majority"}}));
// The second statement cannot specify a readConcern.
assert.commandFailedWithCode(
sessionDb.runCommand({find: collName, readConcern: {level: "majority"}}),
ErrorCodes.InvalidOptions);
// The transaction is still active and can be committed.
assert.commandWorked(session.commitTransaction_forTesting());
session.endSession();
}());
|