diff options
author | XueruiFa <xuerui.fa@mongodb.com> | 2021-02-05 15:56:28 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-02-06 01:53:22 +0000 |
commit | 395a73ccd6d053c964de7f0fe4f16920cb40350a (patch) | |
tree | 2764fc6c58c3f263d8ed572e3fda85316634d108 | |
parent | bd538c0b67fdb21c2be9ed4d124480f3d6fe9fa3 (diff) | |
download | mongo-395a73ccd6d053c964de7f0fe4f16920cb40350a.tar.gz |
SERVER-54339: Prevent spontaneous elections in rollback_crud_op_sequences.js
-rw-r--r-- | jstests/replsets/rollback_crud_op_sequences.js | 167 |
1 files changed, 52 insertions, 115 deletions
diff --git a/jstests/replsets/rollback_crud_op_sequences.js b/jstests/replsets/rollback_crud_op_sequences.js index 74a10594bb7..54c580b6bd1 100644 --- a/jstests/replsets/rollback_crud_op_sequences.js +++ b/jstests/replsets/rollback_crud_op_sequences.js @@ -1,22 +1,14 @@ /* * 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. */ +load('jstests/replsets/libs/rollback_test.js'); load("jstests/replsets/rslib.js"); (function() { "use strict"; -// helper function for verifying contents at the end of the test -var checkFinalResults = function(db) { + +// Helper function for verifying contents at the end of the test. +const 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"})); @@ -27,117 +19,62 @@ var checkFinalResults = function(db) { assert.eq(0, db.kap2.find().itcount()); }; -var name = "rollback_crud_op_sequences"; -var replTest = new ReplSetTest({name: name, nodes: 3, useBridge: true}); -var nodes = replTest.nodeList(); - -var 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); -var primary = replTest.getPrimary(); -var a_conn = conns[0]; -a_conn.setSecondaryOk(); -var A = a_conn.getDB("admin"); -var b_conn = conns[1]; -b_conn.setSecondaryOk(); -var B = b_conn.getDB("admin"); -assert.eq(primary, conns[0], "conns[0] assumed to be primary"); -assert.eq(a_conn, primary); - -// Wait for initial replication -var a = a_conn.getDB("foo"); -var b = b_conn.getDB("foo"); +const rollbackTest = new RollbackTest(); -// initial data for both nodes -assert.commandWorked(a.bar.insert({q: 0})); -assert.commandWorked(a.bar.insert({q: 1, a: "foo"})); -assert.commandWorked(a.bar.insert({q: 2, a: "foo", x: 1})); -assert.commandWorked(a.bar.insert({q: 3, bb: 9, a: "foo"})); -assert.commandWorked(a.bar.insert({q: 40, a: 1})); -assert.commandWorked(a.bar.insert({q: 40, a: 2})); -assert.commandWorked(a.bar.insert({q: 70, txt: 'willremove'})); -a.createCollection("kap", {capped: true, size: 5000}); -assert.commandWorked(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(); +const rollbackNode = rollbackTest.getPrimary(); +rollbackNode.setSecondaryOk(); +const syncSource = rollbackTest.getSecondary(); +syncSource.setSecondaryOk(); -// isolate A and wait for B to become primary -conns[0].disconnect(conns[1]); -conns[0].disconnect(conns[2]); -assert.soon(function() { - try { - return B.hello().isWritablePrimary; - } catch (e) { - return false; - } -}, "node B did not become primary as expected", ReplSetTest.kDefaultTimeoutMS); +const rollbackNodeDB = rollbackNode.getDB("foo"); +const syncSourceDB = syncSource.getDB("foo"); -// do operations on B and B alone, these will be rolled back -assert.commandWorked(b.bar.insert({q: 4})); -assert.commandWorked(b.bar.update({q: 3}, {q: 3, rb: true})); -assert.commandWorked(b.bar.remove({q: 40})); // multi remove test -assert.commandWorked(b.bar.update({q: 2}, {q: 39, rb: true})); -// rolling back a delete will involve reinserting the item(s) -assert.commandWorked(b.bar.remove({q: 1})); -assert.commandWorked(b.bar.update({q: 0}, {$inc: {y: 1}})); -assert.commandWorked(b.kap.insert({foo: 2})); -assert.commandWorked(b.kap2.insert({foo: 2})); -// create a collection (need to roll back the whole thing) -assert.commandWorked(b.newcoll.insert({a: true})); -// create a new empty collection (need to roll back the whole thing) -b.createCollection("abc"); +// Insert initial data for both nodes. +assert.commandWorked(rollbackNodeDB.bar.insert({q: 0})); +assert.commandWorked(rollbackNodeDB.bar.insert({q: 1, a: "foo"})); +assert.commandWorked(rollbackNodeDB.bar.insert({q: 2, a: "foo", x: 1})); +assert.commandWorked(rollbackNodeDB.bar.insert({q: 3, bb: 9, a: "foo"})); +assert.commandWorked(rollbackNodeDB.bar.insert({q: 40, a: 1})); +assert.commandWorked(rollbackNodeDB.bar.insert({q: 40, a: 2})); +assert.commandWorked(rollbackNodeDB.bar.insert({q: 70, txt: 'willremove'})); +rollbackNodeDB.createCollection("kap", {capped: true, size: 5000}); +assert.commandWorked(rollbackNodeDB.kap.insert({foo: 1})); +// Going back to empty on capped is a special case and must be tested. +rollbackNodeDB.createCollection("kap2", {capped: true, size: 5501}); +rollbackTest.awaitReplication(); -// 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 -conns[1].disconnect(conns[2]); -assert.soon(function() { - try { - return !B.hello().isWritablePrimary; - } catch (e) { - return false; - } -}); +rollbackTest.transitionToRollbackOperations(); -conns[0].reconnect(conns[2]); -assert.soon(function() { - try { - return A.hello().isWritablePrimary; - } catch (e) { - return false; - } -}); -assert.gte(a.bar.find().itcount(), 1, "count check"); -assert.commandWorked(a.bar.insert({txt: 'foo'})); -assert.commandWorked(a.bar.remove({q: 70})); -assert.commandWorked(a.bar.update({q: 0}, {$inc: {y: 33}})); +// These operations are only done on 'rollbackNode' and should eventually be rolled back. +assert.commandWorked(rollbackNodeDB.bar.insert({q: 4})); +assert.commandWorked(rollbackNodeDB.bar.update({q: 3}, {q: 3, rb: true})); +assert.commandWorked(rollbackNodeDB.bar.remove({q: 40})); // multi remove test +assert.commandWorked(rollbackNodeDB.bar.update({q: 2}, {q: 39, rb: true})); +// Rolling back a delete will involve reinserting the item(s). +assert.commandWorked(rollbackNodeDB.bar.remove({q: 1})); +assert.commandWorked(rollbackNodeDB.bar.update({q: 0}, {$inc: {y: 1}})); +assert.commandWorked(rollbackNodeDB.kap.insert({foo: 2})); +assert.commandWorked(rollbackNodeDB.kap2.insert({foo: 2})); +// Create a collection (need to roll back the whole thing). +assert.commandWorked(rollbackNodeDB.newcoll.insert({a: true})); +// Create a new empty collection (need to roll back the whole thing). +rollbackNodeDB.createCollection("abc"); -// A is 1 2 3 7 8 -// B is 1 2 3 4 5 6 -// put B back in contact with A and arbiter, as A is primary, B will rollback and then catch up -conns[1].reconnect(conns[2]); -conns[0].reconnect(conns[1]); +rollbackTest.transitionToSyncSourceOperationsBeforeRollback(); -awaitOpTime(b_conn, a_conn); +// Insert new data into syncSource so that rollbackNode enters rollback when it is reconnected. +// These operations should not be rolled back. +assert.gte(syncSourceDB.bar.find().itcount(), 1, "count check"); +assert.commandWorked(syncSourceDB.bar.insert({txt: 'foo'})); +assert.commandWorked(syncSourceDB.bar.remove({q: 70})); +assert.commandWorked(syncSourceDB.bar.update({q: 0}, {$inc: {y: 33}})); -// await steady state and ensure the two nodes have the same contents -replTest.awaitSecondaryNodes(); -replTest.awaitReplication(); -checkFinalResults(a); -checkFinalResults(b); +rollbackTest.transitionToSyncSourceOperationsDuringRollback(); +rollbackTest.transitionToSteadyStateOperations(); -// Verify data consistency between nodes. -replTest.checkReplicatedDataHashes(); -replTest.checkOplogs(); +rollbackTest.awaitReplication(); +checkFinalResults(rollbackNodeDB); +checkFinalResults(syncSourceDB); -replTest.stopSet(15); +rollbackTest.stop(); }()); |