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();
}());
|