mirror of
https://github.com/mongodb/mongo.git
synced 2024-11-24 16:46:00 +01:00
2fd5f78d5a
GitOrigin-RevId: df168ee363c3f0e86526270437d3688ac4bb326d
181 lines
6.5 KiB
JavaScript
181 lines
6.5 KiB
JavaScript
/**
|
|
* Basic test of a succesful replica set rollback for DDL operations.
|
|
*
|
|
* This tests sets up a 3 node set, data-bearing nodes A and B and an arbiter.
|
|
*
|
|
* 1. A is elected PRIMARY and receives several writes, which are propagated to B.
|
|
* 2. A is isolated from the rest of the set and B is elected PRIMARY.
|
|
* 3. B receives several operations, which will later be undone during rollback.
|
|
* 4. B is then isolated and A regains its connection to the arbiter.
|
|
* 5. A receives many new operations, which B will replicate after rollback.
|
|
* 6. B rejoins the set and goes through the rollback process.
|
|
* 7. The contents of A and B are compare to ensure the rollback results in consistent nodes.
|
|
*/
|
|
import {ReplSetTest} from "jstests/libs/replsettest.js";
|
|
import {awaitOpTime} from "jstests/replsets/rslib.js";
|
|
|
|
// helper function for verifying contents at the end of the test
|
|
var checkFinalResults = function(db) {
|
|
assert.eq(2, db.b.getIndexes().length);
|
|
assert.eq(2, db.oldname.getIndexes().length);
|
|
assert.eq(2, db.oldname.find().itcount());
|
|
assert.eq(1, db.kap.find().itcount());
|
|
assert(db.kap.isCapped());
|
|
assert.eq(0, db.bar.count({q: 70}));
|
|
assert.eq(33, db.bar.findOne({q: 0})["y"]);
|
|
assert.eq(0, db.bar.count({q: 70}));
|
|
assert.eq(1, db.bar.count({txt: "foo"}));
|
|
assert.eq(200, db.bar.count({i: {$gt: -1}}));
|
|
assert.eq(6, db.bar.count({q: {$gt: -1}}));
|
|
assert.eq(0, db.getSiblingDB("abc").foo.find().itcount());
|
|
assert.eq(0, db.getSiblingDB("abc").bar.find().itcount());
|
|
};
|
|
|
|
var name = "rollback_ddl_op_sequences";
|
|
var replTest = new ReplSetTest({
|
|
name: name,
|
|
nodes: 3,
|
|
useBridge: true,
|
|
});
|
|
var nodes = replTest.nodeList();
|
|
|
|
var conns = replTest.startSet();
|
|
replTest.initiate({
|
|
"_id": name,
|
|
"members": [
|
|
{"_id": 0, "host": nodes[0], priority: 3},
|
|
{"_id": 1, "host": nodes[1]},
|
|
{"_id": 2, "host": nodes[2], arbiterOnly: true}
|
|
]
|
|
},
|
|
null,
|
|
{initiateWithDefaultElectionTimeout: true});
|
|
|
|
// Make sure we have a primary and that that primary is node A
|
|
replTest.waitForState(replTest.nodes[0], ReplSetTest.State.PRIMARY);
|
|
var primary = replTest.getPrimary();
|
|
|
|
// The default WC is majority and this test can't satisfy majority writes.
|
|
assert.commandWorked(primary.adminCommand(
|
|
{setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}, writeConcern: {w: "majority"}}));
|
|
replTest.awaitReplication();
|
|
|
|
var a_conn = conns[0];
|
|
a_conn.setSecondaryOk();
|
|
var A = a_conn.getDB("admin");
|
|
var b_conn = conns[1];
|
|
b_conn.setSecondaryOk();
|
|
var B = b_conn.getDB("admin");
|
|
assert.eq(primary, conns[0], "conns[0] assumed to be primary");
|
|
assert.eq(a_conn, primary);
|
|
|
|
// Wait for initial replication
|
|
var a = a_conn.getDB("foo");
|
|
var b = b_conn.getDB("foo");
|
|
|
|
// This test create indexes with fail point enabled on secondary which prevents secondary from
|
|
// voting. So, disabling index build commit quorum.
|
|
// initial data for both nodes
|
|
assert.commandWorked(a.b.insert({x: 1}));
|
|
assert.commandWorked(a.b.createIndex({x: 1}, {}, 0));
|
|
assert.commandWorked(a.oldname.insert({y: 1}));
|
|
assert.commandWorked(a.oldname.insert({y: 2}));
|
|
assert.commandWorked(a.oldname.createIndex({y: 1}, {unique: true}, 0));
|
|
assert.commandWorked(a.bar.insert({q: 0}));
|
|
assert.commandWorked(a.bar.insert({q: 1, a: "foo"}));
|
|
assert.commandWorked(a.bar.insert({q: 2, a: "foo", x: 1}));
|
|
assert.commandWorked(a.bar.insert({q: 3, bb: 9, a: "foo"}));
|
|
assert.commandWorked(a.bar.insert({q: 40333333, a: 1}));
|
|
for (var i = 0; i < 200; i++) {
|
|
assert.commandWorked(a.bar.insert({i: i}));
|
|
}
|
|
assert.commandWorked(a.bar.insert({q: 40, a: 2}));
|
|
assert.commandWorked(a.bar.insert({q: 70, txt: 'willremove'}));
|
|
a.createCollection("kap", {capped: true, size: 5000});
|
|
assert.commandWorked(a.kap.insert({foo: 1}));
|
|
replTest.awaitReplication();
|
|
|
|
// isolate A and wait for B to become primary
|
|
conns[0].disconnect(conns[1]);
|
|
conns[0].disconnect(conns[2]);
|
|
assert.soon(function() {
|
|
try {
|
|
return B.hello().isWritablePrimary;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
});
|
|
|
|
// do operations on B and B alone, these will be rolled back
|
|
assert.commandWorked(b.bar.insert({q: 4}));
|
|
assert.commandWorked(b.bar.update({q: 3}, {q: 3, rb: true}));
|
|
assert.commandWorked(b.bar.remove({q: 40})); // multi remove test
|
|
assert.commandWorked(b.bar.update({q: 2}, {q: 39, rb: true}));
|
|
// rolling back a delete will involve reinserting the item(s)
|
|
assert.commandWorked(b.bar.remove({q: 1}));
|
|
assert.commandWorked(b.bar.update({q: 0}, {$inc: {y: 1}}));
|
|
assert.commandWorked(b.kap.insert({foo: 2}));
|
|
assert.commandWorked(b.kap2.insert({foo: 2}));
|
|
// create a collection (need to roll back the whole thing)
|
|
assert.commandWorked(b.newcoll.insert({a: true}));
|
|
// create a new empty collection (need to roll back the whole thing)
|
|
b.createCollection("abc");
|
|
// drop a collection - we'll need all its data back!
|
|
b.bar.drop();
|
|
// drop an index - verify it comes back
|
|
b.b.dropIndexes();
|
|
// two to see if we transitively rollback?
|
|
b.oldname.renameCollection("newname");
|
|
b.newname.renameCollection("fooname");
|
|
assert(b.fooname.find().itcount() > 0, "count rename");
|
|
// create an index - verify that it is removed
|
|
assert.commandWorked(b.fooname.createIndex({q: 1}, {}, 0));
|
|
// test roll back (drop) a whole database
|
|
var abc = b.getSiblingDB("abc");
|
|
assert.commandWorked(abc.foo.insert({x: 1}));
|
|
assert.commandWorked(abc.bar.insert({y: 999}));
|
|
|
|
// isolate B, bring A back into contact with the arbiter, then wait for A to become primary
|
|
// insert new data into A so that B will need to rollback when it reconnects to A
|
|
conns[1].disconnect(conns[2]);
|
|
assert.soon(function() {
|
|
try {
|
|
return !B.hello().isWritablePrimary;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
});
|
|
|
|
conns[0].reconnect(conns[2]);
|
|
assert.soon(function() {
|
|
try {
|
|
return A.hello().isWritablePrimary;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
});
|
|
assert(a.bar.find().itcount() >= 1, "count check");
|
|
assert.commandWorked(a.bar.insert({txt: 'foo'}));
|
|
assert.commandWorked(a.bar.remove({q: 70}));
|
|
assert.commandWorked(a.bar.update({q: 0}, {$inc: {y: 33}}));
|
|
|
|
// A is 1 2 3 7 8
|
|
// B is 1 2 3 4 5 6
|
|
// put B back in contact with A and arbiter, as A is primary, B will rollback and then catch up
|
|
conns[1].reconnect(conns[2]);
|
|
conns[0].reconnect(conns[1]);
|
|
|
|
awaitOpTime(b_conn, a_conn);
|
|
|
|
// await steady state and ensure the two nodes have the same contents
|
|
replTest.awaitSecondaryNodes();
|
|
replTest.awaitReplication();
|
|
checkFinalResults(a);
|
|
checkFinalResults(b);
|
|
|
|
// Verify data consistency between nodes.
|
|
replTest.checkReplicatedDataHashes();
|
|
replTest.checkOplogs();
|
|
|
|
replTest.stopSet(15);
|