summaryrefslogtreecommitdiff
path: root/jstests/core/txns/read_concern.js
blob: 460f705c7675df805d039cd537a225e313b764f4 (plain)
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();
}());