blob: f1674f5ea3a6537c272abab1a6bcc4c137133e50 (
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
|
/**
* Tests that multi-documment transactions no longer race with stepdown over
* "setAlwaysInterruptAtStepDownOrUp".
*/
(function() {
"use strict";
load("jstests/libs/fail_point_util.js");
const rst = new ReplSetTest({nodes: 1});
rst.startSet();
rst.initiate();
const dbName = "testdb";
const collName = "testcoll";
const primary = rst.getPrimary();
const primaryDB = primary.getDB(dbName);
const primaryColl = primaryDB.getCollection(collName);
// Insert a document that we will later modify in a transaction.
assert.commandWorked(primaryColl.insert({a: 1}));
// In the first part of the race, we set the 'setAlwaysInterruptAtStepDownOrUp' too late,
// after stepDown is already done interrupting operations.
const txnFpBefore = configureFailPoint(primary, "hangBeforeSettingTxnInterruptFlag");
// The second critical part of the race is when the transaction thread has already passed
// the regular "not primary" checks by the time stepDown has completed and updated
// writability. (This is the reason we check writability again in the accompanying patch.)
const txnFpAfter =
configureFailPoint(primary, "hangAfterCheckingWritabilityForMultiDocumentTransactions");
jsTestLog("Start the transaction in a parallel shell");
const txn = startParallelShell(() => {
const session = db.getMongo().startSession();
const sessionDB = session.getDatabase("testdb");
const sessionColl = sessionDB.getCollection("testcoll");
session.startTransaction();
assert.commandFailedWithCode(sessionColl.insert({b: 2}), ErrorCodes.NotWritablePrimary);
}, primary.port);
jsTestLog("Wait on the first transaction fail point");
txnFpBefore.wait();
const stepdownFP = configureFailPoint(primary, "stepdownHangAfterGrabbingRSTL");
jsTestLog("Issue a stepdown in a parallel shell");
const stepdown = startParallelShell(() => {
assert.commandWorked(db.adminCommand({replSetStepDown: 10 * 60, force: true}));
}, primary.port);
jsTestLog("Wait on the stepdown fail point");
stepdownFP.wait();
// The txn will be forced to wait for stepdown to finish.
jsTestLog("Release the first transaction fail point and wait in the second");
txnFpBefore.off();
txnFpAfter.wait();
jsTestLog("Let stepdown finish");
stepdownFP.off();
stepdown();
jsTestLog("Wait on the second transaction fail point");
txnFpAfter.wait();
jsTestLog("Let the transaction attempt finish");
txnFpAfter.off();
txn();
jsTestLog("Checking that the transaction never succeeded");
assert.eq(1, primaryColl.find().toArray().length);
rst.stopSet();
})();
|