summaryrefslogtreecommitdiff
path: root/jstests/libs/csrs_upgrade_util.js
blob: 7dccccc3e7c160dc1245dca348826344ef2d59c3 (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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/**
* This file defines a class, CSRSUpgradeCoordinator, which contains logic for spinning up a
* sharded cluster using SCCC config servers and for upgrading that cluster to CSRS.
* Include this file and use the CSRSUpgradeCoordinator class in any targetted jstests of csrs
* upgrade behavior.
*/

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

var CSRSUpgradeCoordinator = function() {
    "use strict";

    var testDBName = jsTestName();
    var dataCollectionName = testDBName + ".data";
    var csrsName = jsTestName() + "-csrs";
    var numCsrsMembers;
    var st;
    var shardConfigs;
    var csrsConfig;
    var csrs;
    var csrs0Opts;

    this.getTestDBName = function() {
        return testDBName;
    };

    this.getDataCollectionName = function() {
        return dataCollectionName;
    };

    /**
     * Returns an array of connections to the CSRS nodes.
     */
    this.getCSRSNodes = function() {
        return csrs;
    };

    /**
     * Returns the replica set name of the config server replica set.
     */
    this.getCSRSName = function() {
        return csrsName;
    };

    /**
     * Returns a copy of the options used for starting a mongos in the coordinator's cluster.
     */
    this.getMongosConfig = function() {
        var sconfig = Object.extend({}, st.s0.fullOptions, /* deep */ true);
        delete sconfig.port;
        return sconfig;
    };

    this.getMongos = function(n) {
        return st._mongos[n];
    };

    this.getShardName = function(n) {
        return shardConfigs[n]._id;
    };

    /**
     * Returns the ShardingTest fixture backing this CSRSUpgradeCoordinator.
     */
    this.getShardingTestFixture = function() {
        return st;
    };

    /**
     * Private helper method for waiting for a given node to return ismaster:true in its ismaster
     * command response.
     */
    var _waitUntilMaster = function(dnode) {
        var isMasterReply;
        assert.soon(
            function() {
                isMasterReply = dnode.adminCommand({ismaster: 1});
                return isMasterReply.ismaster;
            },
            function() {
                return "Expected " + dnode.name + " to respond ismaster:true, but got " +
                    tojson(isMasterReply);
            });
    };

    /**
    * Sets up the underlying sharded cluster in SCCC mode, and shards the test collection on _id.
    */
    this.setupSCCCCluster = function() {
        if (TestData.storageEngine == "wiredTiger" || TestData.storageEngine == "") {
            // TODO(schwerin): SERVER-19739 Support testing CSRS with storage engines other than
            // wired
            // tiger, when such other storage engines support majority read concern.
            numCsrsMembers = 3;
        } else {
            numCsrsMembers = 4;
        }

        jsTest.log("Setting up SCCC sharded cluster");

        st = new ShardingTest({
            name: "csrsUpgrade",
            mongos: 2,
            rs: {nodes: 3},
            shards: 2,
            nopreallocj: true,
            other: {sync: true, enableBalancer: false, useHostname: true}
        });

        shardConfigs = st.s0.getCollection("config.shards").find().toArray();
        assert.eq(2, shardConfigs.length);

        jsTest.log("Enabling sharding on " + testDBName + " and making " + this.getShardName(0) +
                   " the primary shard");
        assert.commandWorked(st.s0.adminCommand({enablesharding: testDBName}));
        st.ensurePrimaryShard(testDBName, this.getShardName(0));

        jsTest.log("Creating a sharded collection " + dataCollectionName);
        assert.commandWorked(
            st.s0.adminCommand({shardcollection: dataCollectionName, key: {_id: 1}}));
    };

    /**
     * Restarts the first config server as a single node replica set, while still leaving the
     * cluster
     * operating in SCCC mode.
     */
    this.restartFirstConfigAsReplSet = function() {
        jsTest.log("Restarting " + st.c0.name + " as a standalone replica set");
        csrsConfig = {
            _id: csrsName,
            version: 1,
            configsvr: true,
            members: [{_id: 0, host: st.c0.name}]
        };
        assert.commandWorked(st.c0.adminCommand({replSetInitiate: csrsConfig}));
        csrs = [];
        csrs0Opts = Object.extend({}, st.c0.fullOptions, /* deep */ true);
        csrs0Opts.restart = true;  // Don't clean the data files from the old c0.
        csrs0Opts.replSet = csrsName;
        csrs0Opts.configsvrMode = "sccc";
        MongoRunner.stopMongod(st.c0);
        csrs.push(MongoRunner.runMongod(csrs0Opts));
        _waitUntilMaster(csrs[0]);
    };

    /**
     * Starts up the new members of the config server replica set as non-voting, priority zero
     * nodes.
     */
    this.startNewCSRSNodes = function() {
        jsTest.log("Starting new CSRS nodes");
        for (var i = 1; i < numCsrsMembers; ++i) {
            csrs.push(MongoRunner.runMongod(
                {replSet: csrsName, configsvr: "", storageEngine: "wiredTiger"}));
            csrsConfig.members.push({_id: i, host: csrs[i].name, votes: 0, priority: 0});
        }
        csrsConfig.version = 2;
        jsTest.log("Adding non-voting members to csrs set: " + tojson(csrsConfig));
        assert.commandWorked(csrs[0].adminCommand({replSetReconfig: csrsConfig}));
    };

    this.waitUntilConfigsCaughtUp = function() {
        waitUntilAllNodesCaughtUp(csrs, 60000);
    };

    /**
     * Stops one of the SCCC config servers, thus disabling changes to cluster metadata and
     * preventing
     * any further writes to the config servers until the upgrade to CSRS is completed.
     */
    this.shutdownOneSCCCNode = function() {
        // Only shut down one of the SCCC config servers to avoid any period without any config
        // servers
        // online.
        jsTest.log("Shutting down third SCCC config server node");
        MongoRunner.stopMongod(st.c2);
    };

    /**
     * Allows all CSRS members to vote, in preparation for switching fully to CSRS mode.
     */
    this.allowAllCSRSNodesToVote = function() {
        csrsConfig.members.forEach(function(member) {
            member.votes = 1;
            member.priority = 1;
        });
        csrsConfig.version = 3;
        jsTest.log("Allowing all csrs members to vote: " + tojson(csrsConfig));
        assert.commandWorked(csrs[0].adminCommand({replSetReconfig: csrsConfig}));
    };

    /**
     * Restarts the first member of the config server replica set without the --configsvrMode flag,
     * marking the official switchover from SCCC to CSRS mode.  If the first config server doesn't
     * support readCommitted, waits for it to automatically go into the REMOVED state.  Finally,
     * it shuts down the one remaining SCCC config server node now that it is no longer needed.
     */
    this.switchToCSRSMode = function() {
        jsTest.log("Restarting " + csrs[0].name + " in csrs mode");
        delete csrs0Opts.configsvrMode;
        try {
            csrs[0].adminCommand({replSetStepDown: 60});
        } catch (e) {
        }  // Expected
        MongoRunner.stopMongod(csrs[0]);
        csrs[0] = MongoRunner.runMongod(csrs0Opts);
        var csrsStatus;
        assert.soon(
            function() {
                csrsStatus = csrs[0].adminCommand({replSetGetStatus: 1});
                if (csrsStatus.members[0].stateStr == "STARTUP" ||
                    csrsStatus.members[0].stateStr == "STARTUP2" ||
                    csrsStatus.members[0].stateStr == "RECOVERING") {
                    // Make sure first node is fully online or else mongoses still in SCCC mode
                    // might not
                    // find any node online to talk to.
                    return false;
                }

                var i;
                for (i = 0; i < csrsStatus.members.length; ++i) {
                    if (csrsStatus.members[i].name == csrs[0].name) {
                        var supportsCommitted = csrs[0]
                                                    .getDB("admin")
                                                    .serverStatus()
                                                    .storageEngine.supportsCommittedReads;
                        var stateIsRemoved = csrsStatus.members[i].stateStr == "REMOVED";
                        // If the storage engine supports committed reads, it shouldn't go into
                        // REMOVED
                        // state, but if it does not then it should.
                        if (supportsCommitted) {
                            assert(!stateIsRemoved);
                        } else if (!stateIsRemoved) {
                            return false;
                        }
                    }
                    if (csrsStatus.members[i].stateStr == "PRIMARY") {
                        return csrs[i].adminCommand({ismaster: 1}).ismaster;
                    }
                }
                return false;
            },
            function() {
                return "No primary or non-WT engine not removed in " + tojson(csrsStatus);
            });

        jsTest.log("Shutting down final SCCC config server now that upgrade is complete");
        MongoRunner.stopMongod(st.c1);
    };

};