summaryrefslogtreecommitdiff
path: root/jstests/replsets/rollback2.js
diff options
context:
space:
mode:
authormatt dannenberg <matt.dannenberg@10gen.com>2014-10-13 11:48:32 -0400
committermatt dannenberg <matt.dannenberg@10gen.com>2014-10-14 06:14:55 -0400
commit0548f4764e876c71677be3735636857d194b7f44 (patch)
treea017f419849a47c4b15ff5e7c97b77e1bc3d8a63 /jstests/replsets/rollback2.js
parenteba8e42b691f704c523fcec1ab5acb9aee8666c3 (diff)
downloadmongo-0548f4764e876c71677be3735636857d194b7f44.tar.gz
SERVER-15535 refactor jstests/replsets/rollback*.js to use bridging
Diffstat (limited to 'jstests/replsets/rollback2.js')
-rw-r--r--jstests/replsets/rollback2.js325
1 files changed, 92 insertions, 233 deletions
diff --git a/jstests/replsets/rollback2.js b/jstests/replsets/rollback2.js
index e187af18bc1..301ca7a8792 100644
--- a/jstests/replsets/rollback2.js
+++ b/jstests/replsets/rollback2.js
@@ -1,258 +1,117 @@
-// a test of rollback in replica sets
-//
-// try running as :
-//
-// mongo --nodb rollback2.js | tee out | grep -v ^m31
-//
-
-var debugging = 0;
-
-function ifReady(db, f) {
- var stats = db.adminCommand({ replSetGetStatus: 1 });
-
-
- // only eval if state isn't recovery
- if (stats && stats.myState != 3) {
- return f();
- }
-
- return false;
-}
-
-function pause(s) {
- print(s);
- while (debugging) {
- sleep(3000);
- print(s);
- }
-}
-
-function deb(obj) {
- if( debugging ) {
- print("\n\n\n" + obj + "\n\n");
- }
-}
-
-w = 0;
-
-function wait(f) {
- w++;
- var n = 0;
- while (!f()) {
- if (n % 4 == 0)
- print("rollback2.js waiting " + w);
- if (++n == 4) {
- print("" + f);
- }
- assert(n < 200, 'tried 200 times, giving up');
- sleep(1000);
- }
-}
-
-function dbs_match(a, b) {
- print("dbs_match");
-
- var ac = a.getCollectionNames();
- var bc = b.getCollectionNames();
- if (!friendlyEqual(ac, bc)) {
- print("dbs_match: namespaces don't match");
- print("\n\n");
- printjson(ac);
- print("\n\n");
- printjson(bc);
- print("\n\n");
- return false;
- }
-
- var c = a.getCollectionNames();
- for( var i in c ) {
- print("checking " + c[i]);
- if( !friendlyEqual( a[c[i]].find().sort({_id:1}).toArray(), b[c[i]].find().sort({_id:1}).toArray() ) ) {
- print("dbs_match: collections don't match " + c[i]);
- return false;
- }
+/*
+ * 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.
+ */
+
+(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());
}
- return true;
-}
-
-/* these writes will be initial data and replicate everywhere. */
-function doInitialWrites(db) {
- t = db.bar;
- t.insert({ q:0});
- t.insert({ q: 1, a: "foo" });
- t.insert({ q: 2, a: "foo", x: 1 });
- t.insert({ q: 3, bb: 9, a: "foo" });
- t.insert({ q: 40, a: 1 });
- t.insert({ q: 40, a: 2 });
- t.insert({ q: 70, txt: 'willremove' });
-
- db.createCollection("kap", { capped: true, size: 5000 });
- db.kap.insert({ foo: 1 })
-
- // going back to empty on capped is a special case and must be tested
- db.createCollection("kap2", { capped: true, size: 5501 });
-}
-
-/* these writes on one primary only and will be rolled back. */
-function doItemsToRollBack(db) {
- t = db.bar;
- t.insert({ q: 4 });
- t.update({ q: 3 }, { q: 3, rb: true });
-
- t.remove({ q: 40 }); // multi remove test
-
- t.update({ q: 2 }, { q: 39, rb: true });
-
- // rolling back a delete will involve reinserting the item(s)
- t.remove({ q: 1 });
-
- t.update({ q: 0 }, { $inc: { y: 1} });
- db.kap.insert({ foo: 2 })
- db.kap2.insert({ foo: 2 })
-
- // create a collection (need to roll back the whole thing)
- db.newcoll.insert({ a: true });
-
- // create a new empty collection (need to roll back the whole thing)
- db.createCollection("abc");
-}
-
-function doWritesToKeep2(db) {
- t = db.bar;
- t.insert({ txt: 'foo' });
- t.remove({ q: 70 });
- t.update({ q: 0 }, { $inc: { y: 33} });
-}
-
-function verify(db) {
- print("verify");
- t = db.bar;
- assert(t.find({ q: 1 }).count() == 1);
- assert(t.find({ txt: 'foo' }).count() == 1);
- assert(t.find({ q: 4 }).count() == 0);
-}
-
-doTest = function (signal) {
-
- var replTest = new ReplSetTest({ name: 'unicomplex', nodes: 3 });
+ var name = "rollback2js";
+ var replTest = new ReplSetTest({ name: name, nodes: 3 });
var nodes = replTest.nodeList();
- //print(tojson(nodes));
var conns = replTest.startSet();
- var r = replTest.initiate({ "_id": "unicomplex",
- "members": [
- { "_id": 0, "host": nodes[0] },
- { "_id": 1, "host": nodes[1] },
- { "_id": 2, "host": nodes[2], arbiterOnly: true}]
- });
-
- // Make sure we have a master
+ replTest.initiate({ "_id": name,
+ "members": [
+ { "_id": 0, "host": nodes[0], priority: 3 },
+ { "_id": 1, "host": nodes[1] },
+ { "_id": 2, "host": nodes[2], arbiterOnly: true}
+ ]});
+ replTest.bridge();
+
+ // Make sure we have a master and that that master is node A
var master = replTest.getMaster();
- a_conn = conns[0];
- A = a_conn.getDB("admin");
- b_conn = conns[1];
+ var a_conn = conns[0];
a_conn.setSlaveOk();
+ var A = a_conn.getDB("admin");
+ var b_conn = conns[1];
b_conn.setSlaveOk();
- B = b_conn.getDB("admin");
- assert(master == conns[0], "conns[0] assumed to be master");
- assert(a_conn == master);
-
- //deb(master);
-
- // Make sure we have an arbiter
- assert.soon(function () {
- res = conns[2].getDB("admin").runCommand({ replSetGetStatus: 1 });
- return res.myState == 7;
- }, "Arbiter failed to initialize.");
+ var B = b_conn.getDB("admin");
+ assert.eq(master, conns[0], "conns[0] assumed to be master");
+ assert.eq(a_conn, master);
// Wait for initial replication
var a = a_conn.getDB("foo");
var b = b_conn.getDB("foo");
- wait(function () {
- var status = A.runCommand({replSetGetStatus : 1});
- return status.members[1].state == 2;
- });
-
- doInitialWrites(a);
- // wait for secondary to get this data
- wait(function () { return ifReady(a, function() { return ifReady(b, function() { return b.bar.count() == a.bar.count(); }); }); });
- wait(function () {
- var status = A.runCommand({replSetGetStatus : 1});
- return status.members[1].state == 2;
- });
+ // 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();
- A.runCommand({ replSetTest: 1, blind: true });
- reconnect(a, b);
+ // isolate A and wait for B to become master
+ replTest.partition(0, 1);
+ replTest.partition(0, 2);
+ assert.soon(function () { try { return B.isMaster().ismaster; } catch(e) { return false; } });
- wait(function () { return B.isMaster().ismaster; });
-
- doItemsToRollBack(b);
-
- // a should not have the new data as it was in blind state.
- B.runCommand({ replSetTest: 1, blind: true });
- reconnect(a, b);
- A.runCommand({ replSetTest: 1, blind: false });
- reconnect(a,b);
+ // 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");
- wait(function () { try { return !B.isMaster().ismaster; } catch(e) { return false; } });
- wait(function () { try { return A.isMaster().ismaster; } catch(e) { return false; } });
+ // 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
+ replTest.partition(1, 2);
+ assert.soon(function () { try { return !B.isMaster().ismaster; } catch(e) { return false; } });
+ replTest.unPartition(0, 2);
+ assert.soon(function () { try { return A.isMaster().ismaster; } catch(e) { return false; } });
assert(a.bar.count() >= 1, "count check");
- doWritesToKeep2(a);
+ assert.writeOK(a.bar.insert({ txt: 'foo' }));
+ assert.writeOK(a.bar.remove({ q: 70 }));
+ assert.writeOK(a.bar.update({ q: 0 }, { $inc: { y: 33} }));
// A is 1 2 3 7 8
// B is 1 2 3 4 5 6
-
- // bring B back online
- // as A is primary, B will roll back and then catch up
- B.runCommand({ replSetTest: 1, blind: false });
- reconnect(a,b);
+ // put B back in contact with A and arbiter, as A is primary, B will rollback and then catch up
+ replTest.unPartition(1, 2);
+ replTest.unPartition(0, 1);
- wait(function () { return B.isMaster().ismaster || B.isMaster().secondary; });
+ assert.soon(function () { try { return B.isMaster().secondary; } catch(e) { return false; } });
- // everyone is up here...
+ // await steady state and ensure the two nodes have the same contents
replTest.awaitReplication();
+ checkFinalResults(a);
+ checkFinalResults(b);
- // theoretically, a read could slip in between StateBox::change() printing
- // replSet SECONDARY
- // and the replset actually becoming secondary
- // so we're trying to wait for that here
- print("waiting for secondary");
- assert.soon(function() {
- try {
- var aim = A.isMaster();
- var bim = B.isMaster();
- return (aim.ismaster || aim.secondary) &&
- (bim.ismaster || bim.secondary);
- }
- catch(e) {
- print("checking A and B: "+e);
- }
- });
-
- verify(a);
-
- assert( dbs_match(a,b), "server data sets do not match after rollback, something is wrong");
-
- pause("rollback2.js SUCCESS");
- replTest.stopSet(signal);
-};
-
-var reconnect = function(a,b) {
- wait(function() {
- try {
- a.bar.stats();
- b.bar.stats();
- return true;
- } catch(e) {
- print(e);
- return false;
- }
- });
-};
-
-print("rollback2.js");
-
-doTest( 15 );
+ replTest.stopSet(15);
+}());