2014-10-13 15:33:16 -04:00
|
|
|
/*
|
|
|
|
|
* Basic test of successful rollback in replica sets.
|
|
|
|
|
*
|
2014-10-13 11:48:32 -04:00
|
|
|
* This test sets up a 3-node set, with an arbiter and 2 data-bearing nodes, A and B.
|
2014-10-13 15:33:16 -04:00
|
|
|
* A is the initial primary node.
|
|
|
|
|
*
|
|
|
|
|
* The test inserts 3 documents into A, and waits for them to replicate to B. Then, it partitions A
|
|
|
|
|
* from the other nodes, causing it to step down and causing B to be elected primary.
|
|
|
|
|
*
|
|
|
|
|
* Next, 3 more documents inserted into B, and B is partitioned from the arbiter.
|
|
|
|
|
*
|
|
|
|
|
* Next, A is allowed to connect to the arbiter again, and gets reelected primary. Because the
|
|
|
|
|
* arbiter doesn't know about the writes that B accepted, A becomes primary and we insert 3 new
|
|
|
|
|
* documents. Now, A and B have diverged. We heal the remaining network partition, bringing B back
|
|
|
|
|
* into the network.
|
|
|
|
|
*
|
|
|
|
|
* Finally, we expect either A or B to roll back its 3 divergent documents and acquire the other
|
|
|
|
|
* node's.
|
|
|
|
|
*/
|
2014-10-15 10:50:05 -04:00
|
|
|
load("jstests/replsets/rslib.js");
|
2014-10-13 15:33:16 -04:00
|
|
|
|
|
|
|
|
(function () {
|
|
|
|
|
"use strict";
|
2014-10-13 11:48:32 -04:00
|
|
|
// helper function for verifying contents at the end of the test
|
|
|
|
|
var checkFinalResults = function(db) {
|
|
|
|
|
var x = db.bar.find().sort({q: 1}).toArray();
|
2014-11-13 11:31:45 -05:00
|
|
|
assert.eq(5, x.length, "incorrect number of documents found. Docs found: " + tojson(x));
|
2014-10-13 11:48:32 -04:00
|
|
|
assert.eq(1, x[0].q);
|
|
|
|
|
assert.eq(2, x[1].q);
|
|
|
|
|
assert.eq(3, x[2].q);
|
|
|
|
|
assert.eq(7, x[3].q);
|
|
|
|
|
assert.eq(8, x[4].q);
|
2014-12-02 15:43:13 -05:00
|
|
|
};
|
2010-10-06 10:40:07 -04:00
|
|
|
|
2015-11-06 13:40:59 -05:00
|
|
|
var replTest = new ReplSetTest({ name: 'unicomplex', nodes: 3, oplogSize: 1, useBridge: true });
|
2010-10-06 10:40:07 -04:00
|
|
|
var nodes = replTest.nodeList();
|
|
|
|
|
|
|
|
|
|
var conns = replTest.startSet();
|
|
|
|
|
var r = replTest.initiate({ "_id": "unicomplex",
|
|
|
|
|
"members": [
|
2014-10-13 11:48:32 -04:00
|
|
|
{ "_id": 0, "host": nodes[0], "priority": 3 },
|
2010-10-06 10:40:07 -04:00
|
|
|
{ "_id": 1, "host": nodes[1] },
|
|
|
|
|
{ "_id": 2, "host": nodes[2], arbiterOnly: true}]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Make sure we have a master
|
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 15:33:16 -04:00
|
|
|
var a_conn = conns[0];
|
|
|
|
|
var A = a_conn.getDB("admin");
|
|
|
|
|
var b_conn = conns[1];
|
2010-10-06 10:40:07 -04:00
|
|
|
a_conn.setSlaveOk();
|
|
|
|
|
b_conn.setSlaveOk();
|
2014-10-13 15:33:16 -04:00
|
|
|
var B = b_conn.getDB("admin");
|
2010-10-06 10:40:07 -04:00
|
|
|
assert(master == conns[0], "conns[0] assumed to be master");
|
|
|
|
|
assert(a_conn == master);
|
|
|
|
|
|
|
|
|
|
// Wait for initial replication
|
|
|
|
|
var a = a_conn.getDB("foo");
|
|
|
|
|
var b = b_conn.getDB("foo");
|
|
|
|
|
|
|
|
|
|
/* force the oplog to roll */
|
|
|
|
|
if (new Date() % 2 == 0) {
|
2014-10-15 10:50:05 -04:00
|
|
|
jsTest.log("ROLLING OPLOG AS PART OF TEST (we only do this sometimes)");
|
2010-10-06 10:40:07 -04:00
|
|
|
var pass = 1;
|
|
|
|
|
var first = a.getSisterDB("local").oplog.rs.find().sort({ $natural: 1 }).limit(1)[0];
|
|
|
|
|
a.roll.insert({ x: 1 });
|
|
|
|
|
while (1) {
|
2014-03-03 11:27:18 -05:00
|
|
|
var bulk = a.roll.initializeUnorderedBulkOp();
|
|
|
|
|
for (var i = 0; i < 1000; i++) {
|
|
|
|
|
bulk.find({}).update({ $inc: { x: 1 }});
|
|
|
|
|
}
|
2014-12-02 15:43:13 -05:00
|
|
|
// unlikely secondary isn't keeping up, but let's avoid possible intermittent
|
|
|
|
|
// issues with that.
|
2014-03-03 11:27:18 -05:00
|
|
|
bulk.execute({ w: 2 });
|
|
|
|
|
|
2010-10-06 10:40:07 -04:00
|
|
|
var op = a.getSisterDB("local").oplog.rs.find().sort({ $natural: 1 }).limit(1)[0];
|
|
|
|
|
if (tojson(op.h) != tojson(first.h)) {
|
|
|
|
|
printjson(op);
|
|
|
|
|
printjson(first);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
pass++;
|
|
|
|
|
}
|
2014-10-15 10:50:05 -04:00
|
|
|
jsTest.log("PASSES FOR OPLOG ROLL: " + pass);
|
2010-10-06 10:40:07 -04:00
|
|
|
}
|
|
|
|
|
else {
|
2014-10-15 10:50:05 -04:00
|
|
|
jsTest.log("NO ROLL");
|
2010-10-06 10:40:07 -04:00
|
|
|
}
|
|
|
|
|
|
2014-10-13 15:33:16 -04:00
|
|
|
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" }, { writeConcern: { w: 2 } }));
|
2010-10-06 10:40:07 -04:00
|
|
|
|
2014-10-13 15:33:16 -04:00
|
|
|
assert.eq(a.bar.count(), 3, "a.count");
|
|
|
|
|
assert.eq(b.bar.count(), 3, "b.count");
|
2010-10-06 10:40:07 -04:00
|
|
|
|
2015-11-06 13:40:59 -05:00
|
|
|
conns[0].disconnect(conns[1]);
|
|
|
|
|
conns[0].disconnect(conns[2]);
|
2014-10-13 15:33:16 -04:00
|
|
|
assert.soon(function () { try { return B.isMaster().ismaster; } catch(e) { return false; } });
|
2010-10-06 10:40:07 -04:00
|
|
|
|
2015-09-10 10:38:44 -04:00
|
|
|
// These 97 documents will be rolled back eventually.
|
|
|
|
|
for (var i = 4; i <= 100; i++) {
|
|
|
|
|
b.bar.insert({ q: i });
|
|
|
|
|
}
|
|
|
|
|
assert.eq(100, b.bar.count(), "u.count");
|
2010-10-06 10:40:07 -04:00
|
|
|
|
2014-10-13 15:33:16 -04:00
|
|
|
// a should not have the new data as it was partitioned.
|
2015-11-06 13:40:59 -05:00
|
|
|
conns[1].disconnect(conns[2]);
|
2014-10-15 10:50:05 -04:00
|
|
|
jsTest.log("*************** wait for server to reconnect ****************");
|
2015-11-06 13:40:59 -05:00
|
|
|
conns[0].reconnect(conns[2]);
|
2010-10-07 10:38:35 -04:00
|
|
|
|
2014-10-15 10:50:05 -04:00
|
|
|
jsTest.log("*************** B ****************");
|
2014-10-13 15:33:16 -04:00
|
|
|
assert.soon(function () { try { return !B.isMaster().ismaster; } catch(e) { return false; } });
|
2014-10-15 10:50:05 -04:00
|
|
|
jsTest.log("*************** A ****************");
|
2014-10-13 15:33:16 -04:00
|
|
|
assert.soon(function () { try { return A.isMaster().ismaster; } catch(e) { return false; } });
|
2010-10-06 10:40:07 -04:00
|
|
|
|
|
|
|
|
assert(a.bar.count() == 3, "t is 3");
|
2014-10-15 10:50:05 -04:00
|
|
|
assert.writeOK(a.bar.insert({ q: 7 }));
|
|
|
|
|
assert.writeOK(a.bar.insert({ q: 8 }));
|
|
|
|
|
|
2010-10-06 10:40:07 -04:00
|
|
|
// A is 1 2 3 7 8
|
2015-09-10 10:38:44 -04:00
|
|
|
// B is 1 2 3 4 5 6 ... 100
|
2010-10-06 10:40:07 -04:00
|
|
|
|
2015-09-10 10:38:44 -04:00
|
|
|
var connectionsCreatedOnPrimaryBeforeRollback = a.serverStatus().connections.totalCreated;
|
2012-05-17 00:34:03 -04:00
|
|
|
// bring B back online
|
2015-11-06 13:40:59 -05:00
|
|
|
conns[0].reconnect(conns[1]);
|
|
|
|
|
conns[1].reconnect(conns[2]);
|
2014-10-15 10:50:05 -04:00
|
|
|
|
|
|
|
|
awaitOpTime(b.getMongo(), getLatestOp(a_conn).ts);
|
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);
|
2010-10-06 10:40:07 -04:00
|
|
|
|
2015-09-10 10:38:44 -04:00
|
|
|
var connectionsCreatedOnPrimaryAfterRollback = a.serverStatus().connections.totalCreated;
|
|
|
|
|
var connectionsCreatedOnPrimaryDuringRollback =
|
|
|
|
|
connectionsCreatedOnPrimaryAfterRollback - connectionsCreatedOnPrimaryBeforeRollback;
|
|
|
|
|
jsTest.log('connections created during rollback = ' + connectionsCreatedOnPrimaryDuringRollback);
|
|
|
|
|
assert.lt(connectionsCreatedOnPrimaryDuringRollback, 50,
|
|
|
|
|
'excessive number of connections made by secondary to primary during rollback');
|
|
|
|
|
|
2014-10-13 15:33:16 -04:00
|
|
|
replTest.stopSet(15);
|
|
|
|
|
}());
|