summaryrefslogtreecommitdiff
path: root/jstests/replsets/read_committed_on_secondary.js
blob: 23a566f4073faae4edef9d7b5513b07c34a3848b (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
/**
 * Test basic read committed functionality on a secondary:
 *  - Updates should not be visible until they are in the blessed snapshot.
 *  - Updates should be visible once they are in the blessed snapshot.
 */

load("jstests/replsets/rslib.js");  // For startSetIfSupportsReadMajority.

(function() {
    "use strict";

    function printStatus() {
        var primaryStatus;
        replTest.nodes.forEach((n) => {
            var status = n.getDB("admin").runCommand("replSetGetStatus");
            var self = status.members.filter((m) => m.self)[0];
            var msg = self.name + "\n";
            msg += tojson(status.optimes) + "\n";
            if (self.state == 1) {  // Primary status.
                // List other members status from the primaries perspective
                msg += tojson(status.members.filter((m) => !m.self)) + "\n";
                msg += tojson(status.slaveInfo) + "\n";
            }
            jsTest.log(msg);
        });
    }

    function log(arg) {
        jsTest.log(tojson(arg));
    }
    // Set up a set and grab things for later.
    var name = "read_committed_on_secondary";
    var replTest =
        new ReplSetTest({name: name, nodes: 3, nodeOptions: {enableMajorityReadConcern: ''}});

    if (!startSetIfSupportsReadMajority(replTest)) {
        log("skipping test since storage engine doesn't support committed reads");
        return;
    }

    var nodes = replTest.nodeList();
    var config = {
        "_id": name,
        "members": [
            {"_id": 0, "host": nodes[0]},
            {"_id": 1, "host": nodes[1], priority: 0},
            {"_id": 2, "host": nodes[2], arbiterOnly: true}
        ]
    };
    updateConfigIfNotDurable(config);
    replTest.initiate(config);

    // Get connections and collection.
    var primary = replTest.getPrimary();
    var secondary = replTest.liveNodes.slaves[0];
    var secondaryId = replTest.getNodeId(secondary);

    var dbPrimary = primary.getDB(name);
    var collPrimary = dbPrimary[name];

    var dbSecondary = secondary.getDB(name);
    var collSecondary = dbSecondary[name];

    function saveDoc(state) {
        log("saving doc.");
        var res = dbPrimary.runCommandWithMetadata(
            'update',
            {
              update: name,
              writeConcern: {w: 2, wtimeout: 60 * 1000},
              updates: [{q: {_id: 1}, u: {_id: 1, state: state}, upsert: true}],
            },
            {"$replData": 1});
        assert.commandWorked(res.commandReply);
        assert.eq(res.commandReply.writeErrors, undefined);
        log("done saving doc: optime " + tojson(res.metadata.$replData.lastOpVisible));
        return res.metadata.$replData.lastOpVisible;
    }

    function doDirtyRead(lastOp) {
        log("doing dirty read for lastOp:" + tojson(lastOp));
        var res = collSecondary.runCommand(
            'find', {"readConcern": {"level": "local", "afterOpTime": lastOp}, "maxTimeMS": 3000});
        assert.commandWorked(res);
        log("done doing dirty read.");
        return new DBCommandCursor(secondary, res).toArray()[0].state;
    }

    function doCommittedRead(lastOp) {
        log("doing committed read for optime: " + tojson(lastOp));
        var res = collSecondary.runCommand(
            'find',
            {"readConcern": {"level": "majority", "afterOpTime": lastOp}, "maxTimeMS": 3000});
        assert.commandWorked(res);
        log("done doing committed read.");
        return new DBCommandCursor(secondary, res).toArray()[0].state;
    }

    // Do a write, wait for it to replicate, and ensure it is visible.
    var op0 = saveDoc(0);
    assert.eq(doDirtyRead(op0), 0);

    printStatus();
    assert.eq(doCommittedRead(op0), 0);

    // Disable snapshotting on the secondary.
    secondary.adminCommand({configureFailPoint: 'disableSnapshotting', mode: 'alwaysOn'});

    // Do a write and ensure it is only visible to dirty reads
    var op1 = saveDoc(1);
    assert.eq(doDirtyRead(op1), 1);
    assert.eq(doCommittedRead(op0), 0);

    // Try the committed read again after sleeping to ensure it doesn't only work for queries
    // immediately after the write.
    log("sleeping");
    sleep(1000);
    assert.eq(doCommittedRead(op0), 0);

    // Reenable snapshotting on the secondary and ensure that committed reads are able to see the
    // new
    // state.
    log("turning off failpoint");
    secondary.adminCommand({configureFailPoint: 'disableSnapshotting', mode: 'off'});
    assert.eq(doDirtyRead(op1), 1);
    log(replTest.status());
    replTest.awaitReplication();
    log(replTest.status());
    assert.eq(doCommittedRead(op1), 1);
    log("test success!");
    replTest.stopSet();
}());