summaryrefslogtreecommitdiff
path: root/jstests/replsets/rollback_after_enabling_majority_reads.js
blob: 09d3e67d58285c4d6f176406010afc3d6b2ae365 (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
/**
 * This test documents the behavior that rolling back immediately after upgrading
 * enableMajorityReadConcern to true can fassert. If this happens, the user can restart the server
 * with enableMajorityReadConcern=false to complete the rollback, then upgrade again to
 * enableMajorityReadConcern=true.
 * Rollback after restarting with enableMajorityReadConcern=true succeeds if the common point is at
 * least the stable timestamp, i.e. we do not attempt to roll back operations that were included in
 * @tags: [requires_persistence, requires_fcv_44]
 */
(function() {
"use strict";

load("jstests/replsets/libs/rollback_test.js");
load("jstests/libs/write_concern_util.js");

TestData.rollbackShutdowns = true;
const name = "rollback_after_enabling_majority_reads";
const dbName = "test";
const collName = "coll";

jsTest.log("Set up a Rollback Test with enableMajorityReadConcern=false");
const replTest = new ReplSetTest(
    {name, nodes: 3, useBridge: true, nodeOptions: {enableMajorityReadConcern: "false"}});
replTest.startSet();
let config = replTest.getReplSetConfig();
config.members[2].priority = 0;
config.settings = {
    chainingAllowed: false
};
replTest.initiateWithHighElectionTimeout(config);
let rollbackTest = new RollbackTest(name, replTest);

jsTest.log("Ensure the stable timestamp is ahead of the common point on the rollback node.");
const rollbackNode = rollbackTest.transitionToRollbackOperations();
const operationTime = assert
                          .commandWorked(rollbackNode.getDB(dbName).runCommand(
                              {insert: collName, documents: [{_id: "rollback op"}]}))
                          .operationTime;

// Do a clean shutdown to ensure the recovery timestamp is at operationTime.
jsTest.log("Restart the rollback node with enableMajorityReadConcern=true");
rollbackTest.restartNode(0, 15, {enableMajorityReadConcern: "true"});
const replSetGetStatusResponse =
    assert.commandWorked(rollbackNode.adminCommand({replSetGetStatus: 1}));
assert.eq(replSetGetStatusResponse.lastStableRecoveryTimestamp,
          operationTime,
          tojson(replSetGetStatusResponse));

// The rollback crashes because the common point is before the stable timestamp.
jsTest.log("Attempt to roll back. This will fassert.");
rollbackTest.transitionToSyncSourceOperationsBeforeRollback();
rollbackTest.transitionToSyncSourceOperationsDuringRollback();
assert.soon(() => {
    return rawMongoProgramOutput().indexOf("Fatal Assertion 51121") !== -1;
});

jsTest.log(
    "Restart the rollback node with enableMajorityReadConcern=false. Now the rollback can succeed.");
const allowedExitCode = 14;
rollbackTest.restartNode(0, 15, {enableMajorityReadConcern: "false"}, allowedExitCode);

// Fix counts for "local.startup_log", since they are corrupted by this rollback.
// transitionToSteadyStateOperations() checks collection counts.
assert.commandWorked(rollbackNode.getDB("local").runCommand({validate: "startup_log"}));
rollbackTest.transitionToSteadyStateOperations();

assert.commandWorked(rollbackTest.getPrimary().getDB(dbName)[collName].insert(
    {_id: "steady state op"}, {writeConcern: {w: "majority"}}));

assert.eq(0, rollbackNode.getDB(dbName)[collName].find({_id: "rollback op"}).itcount());
assert.eq(1, rollbackNode.getDB(dbName)[collName].find({_id: "steady state op"}).itcount());

jsTest.log("Restart the rollback node with enableMajorityReadConcern=true.");
rollbackTest.restartNode(0, 15, {enableMajorityReadConcern: "true"});

// Make sure node 0 is the primary.
let node = replTest.nodes[0];
jsTestLog("Waiting for node " + node.host + " to become primary.");
replTest.awaitNodesAgreeOnPrimary();
// Wait until the node has finished starting up before running replSetStepUp.
replTest.awaitSecondaryNodes(ReplSetTest.kDefaultTimeoutMS, [node]);
assert.commandWorked(node.adminCommand({replSetStepUp: 1}));
replTest.waitForState(node, ReplSetTest.State.PRIMARY);
assert.eq(replTest.getPrimary(), node, node.host + " was not primary after step up.");

// Restart replication on the tiebreaker node before constructing a new RollbackTest.
restartServerReplication(replTest.nodes[2]);

// Create a new RollbackTest fixture to execute the final rollback. This will guarantee that the
// final rollback occurs on the current primary, which should be node 0.
jsTestLog("Creating a new RollbackTest fixture to execute a final rollback.");
rollbackTest = new RollbackTest(name, replTest);

jsTest.log("Rollback should succeed since the common point is at least the stable timestamp.");
rollbackTest.transitionToRollbackOperations();
rollbackTest.transitionToSyncSourceOperationsBeforeRollback();
rollbackTest.transitionToSyncSourceOperationsDuringRollback();
rollbackTest.transitionToSteadyStateOperations();

rollbackTest.stop();
}());