0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-30 00:56:44 +01:00
mongodb/jstests/replsets/rollback_views.js

138 lines
5.8 KiB
JavaScript

/*
* Basic test of successful replica set rollback for system.views creation.
*
* This test sets up a 3 node set, data-bearing nodes A and B and an arbiter.
*
* 1. A is elected PRIMARY and inserts into "test1.coll", which is propagated to B.
* 2. A is isolated from the rest of the set and B is elected PRIMARY.
* 3. B creates views "test1.x" and "test2.y" (creating test[12].system.views)
* and collection "test3.z", which will later be undone during rollback.
* 4. B is then isolated and A regains its connection to the arbiter.
* 5. A inserts a document into collection "test1.x", and creates views "test2.y" and "test3.z"
* which B will replicate after rollback.
* 6. B rejoins the set and goes through the rollback/recovery process.
* 7. The contents of A and B are compared to ensure the rollback results in consistent nodes,
* and have the expected collections and views..
*/
load("jstests/replsets/rslib.js");
(function() {
"use strict";
// Run a command, return the result if it worked, or assert with a message otherwise.
let checkedRunCommand = (db, cmd) =>
((res, msg) => (assert.commandWorked(res, msg), res))(db.runCommand(cmd), tojson(cmd));
// Like db.getCollectionNames, but allows a filter.
let getCollectionNames = (db, filter) => checkedRunCommand(db, {listCollections: 1, filter})
.cursor.firstBatch.map((entry) => entry.name)
.sort();
// Function that checks that all array elements are equal, and returns the unique element.
let checkEqual = (array, what) =>
array.reduce((x, y) => assert.eq(x, y, "nodes don't have matching " + what) || x);
// Helper function for verifying database contents at the end of the test.
let checkFinalResults = (dbs, expectedColls, expectedViews) => ({
dbname: checkEqual(dbs, "names"),
colls: checkEqual(
dbs.map((db) => getCollectionNames(db, {type: "collection"})).concat([expectedColls]),
"colls"),
views: checkEqual(
dbs.map((db) => getCollectionNames(db, {type: "view"})).concat([expectedViews]), "views"),
md5: checkEqual(dbs.map((db) => checkedRunCommand(db, {dbHash: 1}).md5), "hashes")
});
let name = "rollback_views.js";
let replTest = new ReplSetTest({name: name, nodes: 3, useBridge: true});
let nodes = replTest.nodeList();
let 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}
]
});
// Make sure we have a primary and that that primary is node A.
replTest.waitForState(replTest.nodes[0], ReplSetTest.State.PRIMARY);
let nodeA = conns[0];
let nodeB = conns[1];
let arbiter = conns[2];
let a1 = nodeA.getDB("test1");
let b1 = nodeB.getDB("test1");
// Initial data for both nodes.
assert.commandWorked(a1.coll.insert([{_id: 1, x: 1}, {_id: 2, x: 2}]));
// Wait for initial replication.
replTest.awaitReplication();
// Isolate A and wait for B to become primary.
nodeA.disconnect(nodeB);
nodeA.disconnect(arbiter);
assert.soon(() => replTest.getPrimary() == nodeB, "node B did not become primary as expected");
// Do operations on B and B alone, these will be rolled back.
// For the collection creation, first create a view with the same name, stressing rollback.
assert.commandWorked(b1.coll.remove({x: 2}));
assert.commandWorked(b1.createView("x", "coll", [{$match: {x: 1}}]));
let b2 = b1.getSiblingDB("test2");
assert.commandWorked(b2.coll.insert([{_id: 1, y: 1}, {_id: 2, y: 2}]));
assert.commandWorked(b2.createView("y", "coll", [{$match: {y: 2}}]));
let b3 = b1.getSiblingDB("test3");
assert.commandWorked(b3.createView("z", "coll", []));
assert.commandWorked(b3.system.views.remove({}));
assert.commandWorked(b3.z.insert([{z: 1}, {z: 2}, {z: 3}]));
assert.commandWorked(b3.z.remove({z: 1}));
// 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.
nodeB.disconnect(arbiter);
replTest.awaitNoPrimary();
nodeA.reconnect(arbiter);
assert.soon(() => replTest.getPrimary() == nodeA, "nodeA did not become primary as expected");
// A is now primary and will perform writes that must be copied by B after rollback.
assert.eq(a1.coll.find().itcount(), 2, "expected two documents in test1.coll");
assert.commandWorked(a1.x.insert({_id: 3, x: "string in test1.x"}));
let a2 = a1.getSiblingDB("test2");
assert.commandWorked(a2.createView("y", "coll", [{$match: {y: 2}}]));
assert.commandWorked(a2.coll.insert([{_id: 1, y: 1}, {_id: 2, y: 2}]));
let a3 = a1.getSiblingDB("test3");
assert.commandWorked(a3.coll.insert([{z: 1}, {z: 2}, {z: 3}]));
assert.commandWorked(a3.createView("z", "coll", [{$match: {z: 3}}]));
// A is collections: test1.{coll,x}, test2.{coll,system.views}, test3.{coll,system.views}
// views: test2.y, test3.z
// B is collections: test1.{coll,system.views}, test2.{coll,systems}, test3.{z,system.views}
// views: test1.x, test2.y
//
// Put B back in contact with A and arbiter. A is primary, so B will rollback and catch up.
nodeB.reconnect(arbiter);
nodeA.reconnect(nodeB);
awaitOpTime(nodeB, nodeA);
// Await steady state and ensure the two nodes have the same contents.
replTest.awaitSecondaryNodes();
replTest.awaitReplication();
// Check both nodes agree with each other and with the expected set of views and collections.
print("All done, check that both nodes have the expected collections, views and md5.");
printjson(checkFinalResults([a1, b1], ["coll", "x"], []));
printjson(checkFinalResults([a2, b2], ["coll", "system.views"], ["y"]));
printjson(checkFinalResults([a3, b3], ["coll", "system.views"], ["z"]));
// Verify data consistency between nodes.
replTest.checkReplicatedDataHashes();
replTest.checkOplogs();
replTest.stopSet();
}());