diff options
author | matt dannenberg <matt.dannenberg@10gen.com> | 2014-10-13 11:48:32 -0400 |
---|---|---|
committer | matt dannenberg <matt.dannenberg@10gen.com> | 2014-10-14 06:14:55 -0400 |
commit | 0548f4764e876c71677be3735636857d194b7f44 (patch) | |
tree | a017f419849a47c4b15ff5e7c97b77e1bc3d8a63 /jstests/replsets/rollback2.js | |
parent | eba8e42b691f704c523fcec1ab5acb9aee8666c3 (diff) | |
download | mongo-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.js | 325 |
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); +}()); |