summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/standalone_replication_recovery.js
blob: afe4e755b7aa9bccaba233a0d78a02b71de2619c (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
/**
 * Tests that a standalone succeeds when passed the 'recoverFromOplogAsStandalone' parameter.
 *
 * This test only makes sense for storage engines that support recover to stable timestamp.
 * @tags: [
 *   requires_majority_read_concern,
 *   requires_persistence,
 *   requires_replication,
 * ]
 */

(function() {
"use strict";
load("jstests/replsets/rslib.js");
load("jstests/libs/write_concern_util.js");

const name = 'standalone_replication_recovery';
const dbName = name;
const collName = 'srr_coll';
const logLevel = tojson({storage: {recovery: 2}});

const rst = new ReplSetTest({
    nodes: 2,
});

function getColl(conn) {
    return conn.getDB(dbName)[collName];
}

function assertDocsInColl(node, nums) {
    let results = getColl(node).find().sort({_id: 1}).toArray();
    let expected = nums.map((i) => ({_id: i}));
    if (!friendlyEqual(results, expected)) {
        rst.dumpOplog(node, {}, 100);
    }
    assert.eq(results, expected, "actual (left) != expected (right)");
}

jsTestLog("Test that an empty standalone fails trying to recover.");
assert.throws(
    () => rst.start(0, {noReplSet: true, setParameter: {recoverFromOplogAsStandalone: true}}));

jsTestLog("Initiating as a replica set.");
// Restart as a replica set node without the flag so we can add operations to the oplog.
let nodes = rst.startSet({setParameter: {logComponentVerbosity: logLevel}});
let node = nodes[0];
let secondary = nodes[1];
rst.initiate(
    {_id: name, members: [{_id: 0, host: node.host}, {_id: 2, host: secondary.host, priority: 0}]});

// The default WC is majority and stopServerReplication will prevent satisfying any majority writes.
assert.commandWorked(rst.getPrimary().adminCommand(
    {setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}, writeConcern: {w: "majority"}}));

// Create the collection with w:majority and then perform a clean restart to ensure that
// the collection is in a stable checkpoint.
assert.commandWorked(node.getDB(dbName).runCommand(
    {create: collName, writeConcern: {w: "majority", wtimeout: ReplSetTest.kDefaultTimeoutMS}}));
assertDocsInColl(node, []);
node = rst.restart(node, {"noReplSet": false});
reconnect(node);
assert.eq(rst.getPrimary(), node);

// Keep node 0 the primary, but prevent it from committing any writes.
stopServerReplication(secondary);

assert.commandWorked(getColl(node).insert({_id: 3}, {writeConcern: {w: 1, j: 1}}));
assert.commandWorked(getColl(node).insert({_id: 4}, {writeConcern: {w: 1, j: 1}}));
assert.commandWorked(getColl(node).insert({_id: 5}, {writeConcern: {w: 1, j: 1}}));
assertDocsInColl(node, [3, 4, 5]);

jsTestLog("Test that if we kill the node, recovery still plays.");
rst.stop(node, 9, {allowedExitCode: MongoRunner.EXIT_SIGKILL});
node = rst.restart(node, {"noReplSet": false});
reconnect(node);
assert.eq(rst.getPrimary(), node);
assertDocsInColl(node, [3, 4, 5]);

jsTestLog("Test that a replica set node cannot start up with the parameter set.");
assert.throws(
    () => rst.restart(
        0, {setParameter: {recoverFromOplogAsStandalone: true, logComponentVerbosity: logLevel}}));

jsTestLog("Test that on restart as a standalone we only see committed writes by default.");
node = rst.start(node, {noReplSet: true, setParameter: {logComponentVerbosity: logLevel}}, true);
reconnect(node);
assertDocsInColl(node, []);

// Test that we can run the validate command on a standalone.
assert.commandWorked(node.getDB(dbName).runCommand({"validate": collName}));

jsTestLog("Test that on restart with the flag set we play recovery.");
node = rst.restart(node, {
    noReplSet: true,
    setParameter: {recoverFromOplogAsStandalone: true, logComponentVerbosity: logLevel}
});
reconnect(node);
assertDocsInColl(node, [3, 4, 5]);

// Test that we can run the validate command on a standalone that recovered.
assert.commandWorked(node.getDB(dbName).runCommand({"validate": collName}));

jsTestLog("Test that we go into read-only mode.");
assert.commandFailedWithCode(getColl(node).insert({_id: 1}), ErrorCodes.IllegalOperation);

jsTestLog("Test that we cannot set the parameter during standalone runtime.");
assert.commandFailed(node.adminCommand({setParameter: 1, recoverFromOplogAsStandalone: true}));
assert.commandFailed(node.adminCommand({setParameter: 1, recoverFromOplogAsStandalone: false}));

jsTestLog("Test that on restart after standalone recovery we do not see replicated writes.");
node = rst.restart(node, {
    noReplSet: true,
    setParameter: {recoverFromOplogAsStandalone: false, logComponentVerbosity: logLevel}
});
reconnect(node);
assertDocsInColl(node, []);
assert.commandWorked(getColl(node).insert({_id: 6}));
assertDocsInColl(node, [6]);
node = rst.restart(node, {
    noReplSet: true,
    setParameter: {recoverFromOplogAsStandalone: true, logComponentVerbosity: logLevel}
});
reconnect(node);
assertDocsInColl(node, [3, 4, 5, 6]);

jsTestLog("Test that we can restart again as a replica set node.");
node = rst.restart(node, {
    noReplSet: false,
    setParameter: {recoverFromOplogAsStandalone: false, logComponentVerbosity: logLevel}
});
reconnect(node);
assert.eq(rst.getPrimary(), node);
assertDocsInColl(node, [3, 4, 5, 6]);

jsTestLog("Test that we cannot set the parameter during replica set runtime.");
assert.commandFailed(node.adminCommand({setParameter: 1, recoverFromOplogAsStandalone: true}));
assert.commandFailed(node.adminCommand({setParameter: 1, recoverFromOplogAsStandalone: false}));

jsTestLog("Test that we can still recover as a standalone.");
assert.commandWorked(getColl(node).insert({_id: 7}));
assertDocsInColl(node, [3, 4, 5, 6, 7]);
node = rst.restart(node, {
    noReplSet: true,
    setParameter: {recoverFromOplogAsStandalone: false, logComponentVerbosity: logLevel}
});
reconnect(node);
assertDocsInColl(node, [6]);
node = rst.restart(node, {
    noReplSet: true,
    setParameter: {recoverFromOplogAsStandalone: true, logComponentVerbosity: logLevel}
});
reconnect(node);
assertDocsInColl(node, [3, 4, 5, 6, 7]);

jsTestLog("Restart as a replica set node so that the test can complete successfully.");
node = rst.restart(node, {
    noReplSet: false,
    setParameter: {recoverFromOplogAsStandalone: false, logComponentVerbosity: logLevel}
});
reconnect(node);
assert.eq(rst.getPrimary(), node);
assertDocsInColl(node, [3, 4, 5, 6, 7]);

restartServerReplication(secondary);

// Skip checking db hashes since we do a write as a standalone.
TestData.skipCheckDBHashes = true;
rst.stopSet();
})();