2014-10-13 11:48:32 -04:00
|
|
|
/*
|
|
|
|
|
* Basic test of a succesful replica set rollback for CRUD 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.
|
|
|
|
|
*/
|
2014-10-15 10:50:05 -04:00
|
|
|
load("jstests/replsets/rslib.js");
|
2014-10-13 11:48:32 -04:00
|
|
|
|
|
|
|
|
(function () {
|
|
|
|
|
"use strict";
|
|
|
|
|
// helper function for verifying contents at the end of the test
|
|
|
|
|
var checkFinalResults = function(db) {
|
|
|
|
|
assert.eq(0, db.bar.count({q: 70}));
|
|
|
|
|
assert.eq(2, db.bar.count({q: 40}));
|
|
|
|
|
assert.eq(3, db.bar.count({a: "foo"}));
|
|
|
|
|
assert.eq(6, db.bar.count({q: {$gt: -1}}));
|
|
|
|
|
assert.eq(1, db.bar.count({txt: "foo"}));
|
|
|
|
|
assert.eq(33, db.bar.findOne({q: 0})["y"]);
|
|
|
|
|
assert.eq(1, db.kap.count());
|
|
|
|
|
assert.eq(0, db.kap2.count());
|
2010-09-10 11:25:28 -04:00
|
|
|
}
|
|
|
|
|
|
2014-10-13 11:48:32 -04:00
|
|
|
var name = "rollback2js";
|
2015-11-06 13:40:59 -05:00
|
|
|
var replTest = new ReplSetTest({ name: name, nodes: 3, useBridge: true });
|
2010-09-10 11:25:28 -04:00
|
|
|
var nodes = replTest.nodeList();
|
|
|
|
|
|
|
|
|
|
var conns = replTest.startSet();
|
2014-10-13 11:48:32 -04:00
|
|
|
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 master and that that master is node A
|
2015-12-10 10:21:51 -05:00
|
|
|
replTest.waitForState(replTest.nodes[0], ReplSetTest.State.PRIMARY, 60 * 1000);
|
2015-11-25 11:20:43 -05:00
|
|
|
var master = replTest.getPrimary();
|
2014-10-13 11:48:32 -04:00
|
|
|
var a_conn = conns[0];
|
2010-09-10 11:25:28 -04:00
|
|
|
a_conn.setSlaveOk();
|
2014-10-13 11:48:32 -04:00
|
|
|
var A = a_conn.getDB("admin");
|
|
|
|
|
var b_conn = conns[1];
|
2010-09-10 11:25:28 -04:00
|
|
|
b_conn.setSlaveOk();
|
2014-10-13 11:48:32 -04:00
|
|
|
var B = b_conn.getDB("admin");
|
|
|
|
|
assert.eq(master, conns[0], "conns[0] assumed to be master");
|
|
|
|
|
assert.eq(a_conn, master);
|
2010-09-10 11:25:28 -04:00
|
|
|
|
|
|
|
|
// Wait for initial replication
|
|
|
|
|
var a = a_conn.getDB("foo");
|
|
|
|
|
var b = b_conn.getDB("foo");
|
|
|
|
|
|
2014-10-13 11:48:32 -04:00
|
|
|
// initial data for both nodes
|
|
|
|
|
assert.writeOK(a.bar.insert({ q:0}));
|
|
|
|
|
assert.writeOK(a.bar.insert({ q: 1, a: "foo" }));
|
|
|
|
|
assert.writeOK(a.bar.insert({ q: 2, a: "foo", x: 1 }));
|
|
|
|
|
assert.writeOK(a.bar.insert({ q: 3, bb: 9, a: "foo" }));
|
|
|
|
|
assert.writeOK(a.bar.insert({ q: 40, a: 1 }));
|
|
|
|
|
assert.writeOK(a.bar.insert({ q: 40, a: 2 }));
|
|
|
|
|
assert.writeOK(a.bar.insert({ q: 70, txt: 'willremove' }));
|
|
|
|
|
a.createCollection("kap", { capped: true, size: 5000 });
|
|
|
|
|
assert.writeOK(a.kap.insert({ foo: 1 }));
|
|
|
|
|
// going back to empty on capped is a special case and must be tested
|
|
|
|
|
a.createCollection("kap2", { capped: true, size: 5501 });
|
|
|
|
|
replTest.awaitReplication();
|
2010-09-10 11:25:28 -04:00
|
|
|
|
2015-11-11 05:34:02 -05:00
|
|
|
var timeout;
|
|
|
|
|
if (replTest.getConfigFromPrimary().protocolVersion == 1) {
|
|
|
|
|
timeout = 30 * 1000;
|
|
|
|
|
} else {
|
|
|
|
|
timeout = 60 * 1000;
|
|
|
|
|
}
|
2014-10-13 11:48:32 -04:00
|
|
|
// isolate A and wait for B to become master
|
2015-11-06 13:40:59 -05:00
|
|
|
conns[0].disconnect(conns[1]);
|
|
|
|
|
conns[0].disconnect(conns[2]);
|
2015-11-11 05:34:02 -05:00
|
|
|
assert.soon(function () {
|
|
|
|
|
try {
|
|
|
|
|
return B.isMaster().ismaster;
|
|
|
|
|
} catch(e) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2015-11-11 10:22:21 -05:00
|
|
|
}, "node B did not become master as expected", timeout);
|
2015-11-11 05:34:02 -05:00
|
|
|
|
2014-10-13 11:48:32 -04:00
|
|
|
// do operations on B and B alone, these will be rolled back
|
|
|
|
|
assert.writeOK(b.bar.insert({ q: 4 }));
|
|
|
|
|
assert.writeOK(b.bar.update({ q: 3 }, { q: 3, rb: true }));
|
|
|
|
|
assert.writeOK(b.bar.remove({ q: 40 })); // multi remove test
|
|
|
|
|
assert.writeOK(b.bar.update({ q: 2 }, { q: 39, rb: true }));
|
|
|
|
|
// rolling back a delete will involve reinserting the item(s)
|
|
|
|
|
assert.writeOK(b.bar.remove({ q: 1 }));
|
|
|
|
|
assert.writeOK(b.bar.update({ q: 0 }, { $inc: { y: 1} }));
|
|
|
|
|
assert.writeOK(b.kap.insert({ foo: 2 }));
|
|
|
|
|
assert.writeOK(b.kap2.insert({ foo: 2 }));
|
|
|
|
|
// create a collection (need to roll back the whole thing)
|
|
|
|
|
assert.writeOK(b.newcoll.insert({ a: true }));
|
|
|
|
|
// create a new empty collection (need to roll back the whole thing)
|
|
|
|
|
b.createCollection("abc");
|
2010-10-06 10:40:07 -04:00
|
|
|
|
2014-10-13 11:48:32 -04:00
|
|
|
// isolate B, bring A back into contact with the arbiter, then wait for A to become master
|
|
|
|
|
// insert new data into A so that B will need to rollback when it reconnects to A
|
2015-11-06 13:40:59 -05:00
|
|
|
conns[1].disconnect(conns[2]);
|
2014-10-13 11:48:32 -04:00
|
|
|
assert.soon(function () { try { return !B.isMaster().ismaster; } catch(e) { return false; } });
|
2010-09-10 11:25:28 -04:00
|
|
|
|
2015-11-06 13:40:59 -05:00
|
|
|
conns[0].reconnect(conns[2]);
|
2014-10-13 11:48:32 -04:00
|
|
|
assert.soon(function () { try { return A.isMaster().ismaster; } catch(e) { return false; } });
|
2010-09-10 11:25:28 -04:00
|
|
|
assert(a.bar.count() >= 1, "count check");
|
2014-10-13 11:48:32 -04:00
|
|
|
assert.writeOK(a.bar.insert({ txt: 'foo' }));
|
|
|
|
|
assert.writeOK(a.bar.remove({ q: 70 }));
|
|
|
|
|
assert.writeOK(a.bar.update({ q: 0 }, { $inc: { y: 33} }));
|
2010-09-10 11:25:28 -04:00
|
|
|
|
|
|
|
|
// A is 1 2 3 7 8
|
|
|
|
|
// B is 1 2 3 4 5 6
|
2014-10-13 11:48:32 -04:00
|
|
|
// put B back in contact with A and arbiter, as A is primary, B will rollback and then catch up
|
2015-11-06 13:40:59 -05:00
|
|
|
conns[1].reconnect(conns[2]);
|
|
|
|
|
conns[0].reconnect(conns[1]);
|
2014-10-15 10:50:05 -04:00
|
|
|
|
|
|
|
|
awaitOpTime(b.getMongo(), getLatestOp(a_conn).ts);
|
|
|
|
|
|
2014-10-13 11:48:32 -04:00
|
|
|
// await steady state and ensure the two nodes have the same contents
|
2015-10-28 05:40:21 -04:00
|
|
|
replTest.awaitSecondaryNodes();
|
2010-10-14 17:14:05 -04:00
|
|
|
replTest.awaitReplication();
|
2014-10-13 11:48:32 -04:00
|
|
|
checkFinalResults(a);
|
|
|
|
|
checkFinalResults(b);
|
2011-07-25 22:14:14 -04:00
|
|
|
|
2014-10-13 11:48:32 -04:00
|
|
|
replTest.stopSet(15);
|
|
|
|
|
}());
|