summaryrefslogtreecommitdiff
path: root/jstests/core/txns/speculative_snapshot_includes_all_writes.js
blob: efeefdfa8898f7953920fe4ff1fa56e1b5b74e98 (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
/**
 * A speculative snapshot must not include any writes ordered after any uncommitted writes.
 *
 * @tags: [uses_transactions]
 */
(function() {
"use strict";

load("jstests/libs/check_log.js");

const dbName = "test";
const collName = "speculative_snapshot_includes_all_writes_1";
const collName2 = "speculative_snapshot_includes_all_writes_2";
const testDB = db.getSiblingDB(dbName);
const testColl = testDB[collName];
const testColl2 = testDB[collName2];

testDB.runCommand({drop: collName, writeConcern: {w: "majority"}});
testDB.runCommand({drop: collName2, writeConcern: {w: "majority"}});

assert.commandWorked(testDB.createCollection(collName, {writeConcern: {w: "majority"}}));
assert.commandWorked(testDB.createCollection(collName2, {writeConcern: {w: "majority"}}));

const sessionOptions = {
    causalConsistency: false
};

function startSessionAndTransaction(readConcernLevel) {
    let session = db.getMongo().startSession(sessionOptions);
    jsTestLog("Start a transaction with readConcern " + readConcernLevel.level + ".");
    session.startTransaction({readConcern: readConcernLevel});
    return session;
}

let checkReads = (session, collExpected, coll2Expected) => {
    let sessionDb = session.getDatabase(dbName);
    let coll = sessionDb.getCollection(collName);
    let coll2 = sessionDb.getCollection(collName2);
    assert.sameMembers(collExpected, coll.find().toArray());
    assert.sameMembers(coll2Expected, coll2.find().toArray());
};

// Clear ramlog so checkLog can't find log messages from previous times this fail point was
// enabled.
assert.commandWorked(testDB.adminCommand({clearLog: 'global'}));

jsTest.log("Prepopulate the collections.");
assert.commandWorked(testColl.insert([{_id: 0}], {writeConcern: {w: "majority"}}));
assert.commandWorked(testColl2.insert([{_id: "a"}], {writeConcern: {w: "majority"}}));

jsTest.log("Create the uncommitted write.");

assert.commandWorked(db.adminCommand({
    configureFailPoint: "hangAfterCollectionInserts",
    mode: "alwaysOn",
    data: {collectionNS: testColl2.getFullName()}
}));

const joinHungWrite = startParallelShell(() => {
    assert.commandWorked(db.getSiblingDB("test").speculative_snapshot_includes_all_writes_2.insert(
        {_id: "b"}, {writeConcern: {w: "majority"}}));
});

checkLog.contains(db.getMongo(),
                  "hangAfterCollectionInserts fail point enabled for " + testColl2.getFullName());

jsTest.log("Create a write following the uncommitted write.");
// Note this write must use local write concern; it cannot be majority committed until
// the prior uncommitted write is committed.
assert.commandWorked(testColl.insert([{_id: 1}]));

const snapshotSession = startSessionAndTransaction({level: "snapshot"});
checkReads(snapshotSession, [{_id: 0}], [{_id: "a"}]);

const majoritySession = startSessionAndTransaction({level: "majority"});
checkReads(majoritySession, [{_id: 0}, {_id: 1}], [{_id: "a"}]);

const localSession = startSessionAndTransaction({level: "local"});
checkReads(localSession, [{_id: 0}, {_id: 1}], [{_id: "a"}]);

const defaultSession = startSessionAndTransaction({});
checkReads(defaultSession, [{_id: 0}, {_id: 1}], [{_id: "a"}]);

jsTestLog("Allow the uncommitted write to finish.");
assert.commandWorked(db.adminCommand({
    configureFailPoint: "hangAfterCollectionInserts",
    mode: "off",
}));

joinHungWrite();

jsTestLog("Double-checking that writes not committed at start of snapshot cannot appear.");
checkReads(snapshotSession, [{_id: 0}], [{_id: "a"}]);

jsTestLog(
    "Double-checking that writes performed before the start of a transaction of 'majority' or lower must appear.");
checkReads(majoritySession, [{_id: 0}, {_id: 1}], [{_id: "a"}]);
checkReads(localSession, [{_id: 0}, {_id: 1}], [{_id: "a"}]);
checkReads(defaultSession, [{_id: 0}, {_id: 1}], [{_id: "a"}]);

jsTestLog("Committing transactions.");
assert.commandWorked(snapshotSession.commitTransaction_forTesting());
assert.commandWorked(majoritySession.commitTransaction_forTesting());
assert.commandWorked(localSession.commitTransaction_forTesting());
assert.commandWorked(defaultSession.commitTransaction_forTesting());

jsTestLog("A new local read must see all committed writes.");
checkReads(defaultSession, [{_id: 0}, {_id: 1}], [{_id: "a"}, {_id: "b"}]);

snapshotSession.endSession();
majoritySession.endSession();
localSession.endSession();
defaultSession.endSession();
}());