summaryrefslogtreecommitdiff
path: root/jstests/replsets/stepdown_race_with_transaction.js
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();
})();