summaryrefslogtreecommitdiff
path: root/jstests/replsets/write_concern_after_stepdown_and_stepup.js
blob: ea1c6312eae4029a3605a9fd668a7bdb7fd8806e (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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
 * Tests that heartbeats containing writes from a different branch of history can't cause a stale
 * primary to incorrectly acknowledge a w:majority write that's about to be rolled back, even if the
 * stale primary is re-elected primary before waiting for the write concern acknowledgement.
 */
(function() {
    'use strict';

    var name = "writeConcernStepDownAndBackUp";
    var dbName = "wMajorityCheck";
    var collName = "stepdownAndBackUp";

    var rst = new ReplSetTest({
        name: name,
        nodes: [
            {},
            {},
            {rsConfig: {priority: 0}},
        ],
        useBridge: true
    });
    var nodes = rst.startSet();
    rst.initiate();

    function waitForState(node, state) {
        assert.soonNoExcept(function() {
            assert.commandWorked(node.adminCommand(
                {replSetTest: 1, waitForMemberState: state, timeoutMillis: rst.kDefaultTimeoutMS}));
            return true;
        });
    }

    function waitForPrimary(node) {
        assert.soon(function() {
            return node.adminCommand('ismaster').ismaster;
        });
    }

    function stepUp(node) {
        var primary = rst.getPrimary();
        if (primary != node) {
            assert.throws(function() {
                primary.adminCommand({replSetStepDown: 60 * 5});
            });
        }
        waitForPrimary(node);
    }

    jsTestLog("Make sure node 0 is primary.");
    stepUp(nodes[0]);
    var primary = rst.getPrimary();
    var secondaries = rst.getSecondaries();
    assert.eq(nodes[0], primary);
    // Wait for all data bearing nodes to get up to date.
    assert.writeOK(nodes[0].getDB(dbName).getCollection(collName).insert(
        {a: 1}, {writeConcern: {w: 3, wtimeout: rst.kDefaultTimeoutMS}}));

    // Stop the secondaries from replicating.
    secondaries.forEach(function(node) {
        assert.commandWorked(
            node.adminCommand({configureFailPoint: 'rsSyncApplyStop', mode: 'alwaysOn'}));
    });
    // Stop the primary from calling into awaitReplication()
    assert.commandWorked(nodes[0].adminCommand(
        {configureFailPoint: 'hangBeforeWaitingForWriteConcern', mode: 'alwaysOn'}));

    jsTestLog("Do w:majority write that won't enter awaitReplication() until after the primary " +
              "has stepped down and back up");
    var doMajorityWrite = function() {
        // Run ismaster command with 'hangUpOnStepDown' set to false to mark this connection as
        // one that shouldn't be closed when the node steps down.  This simulates the scenario where
        // the write was coming from a mongos.
        assert.commandWorked(db.adminCommand({ismaster: 1, hangUpOnStepDown: false}));

        var res = db.getSiblingDB('wMajorityCheck').stepdownAndBackUp.insert({a: 2}, {
            writeConcern: {w: 'majority'}
        });
        assert.writeErrorWithCode(res, ErrorCodes.PrimarySteppedDown);
    };

    var joinMajorityWriter = startParallelShell(doMajorityWrite, nodes[0].port);

    jsTest.log("Disconnect primary from all secondaries");
    nodes[0].disconnect(nodes[1]);
    nodes[0].disconnect(nodes[2]);

    jsTest.log("Wait for a new primary to be elected");
    // Allow the secondaries to replicate again.
    secondaries.forEach(function(node) {
        assert.commandWorked(
            node.adminCommand({configureFailPoint: 'rsSyncApplyStop', mode: 'off'}));
    });

    waitForPrimary(nodes[1]);

    jsTest.log("Do a write to the new primary");
    assert.writeOK(nodes[1].getDB(dbName).getCollection(collName).insert(
        {a: 3}, {writeConcern: {w: 2, wtimeout: rst.kDefaultTimeoutMS}}));

    jsTest.log("Reconnect the old primary to the rest of the nodes");
    nodes[0].reconnect(nodes[1]);
    nodes[0].reconnect(nodes[2]);

    jsTest.log("Wait for the old primary to step down, roll back its write, and apply the " +
               "new writes from the new primary");
    waitForState(nodes[0], ReplSetTest.State.SECONDARY);
    rst.awaitReplication();

    // At this point all 3 nodes should have the same data
    assert.soonNoExcept(function() {
        nodes.forEach(function(node) {
            assert.eq(null,
                      node.getDB(dbName).getCollection(collName).findOne({a: 2}),
                      "Node " + node.host + " contained op that should have been rolled back");
            assert.neq(null,
                       node.getDB(dbName).getCollection(collName).findOne({a: 3}),
                       "Node " + node.host +
                           " was missing op from branch of history that should have persisted");
        });
        return true;
    });

    jsTest.log("Make the original primary become primary once again");
    stepUp(nodes[0]);

    jsTest.log("Unblock the thread waiting for replication of the now rolled-back write, ensure " +
               "that the write concern failed");
    assert.commandWorked(nodes[0].adminCommand(
        {configureFailPoint: 'hangBeforeWaitingForWriteConcern', mode: 'off'}));

    joinMajorityWriter();

    rst.stopSet();
}());