mirror of
https://github.com/mongodb/mongo.git
synced 2024-12-01 09:32:32 +01:00
656 lines
23 KiB
JavaScript
656 lines
23 KiB
JavaScript
/*
|
|
* Generally test that replica sets still function normally with mixed versions and mixed storage
|
|
* engines. This test will set up a replica set containing members of various versions and
|
|
* storage engines, do a bunch of random work, and assert that it replicates the same way on all
|
|
* nodes.
|
|
*/
|
|
load('jstests/libs/parallelTester.js');
|
|
|
|
// Seed random numbers and print the seed. To reproduce a failed test, look for the seed towards
|
|
// the beginning of the output, and give it as an argument to randomize.
|
|
jsTest.randomize();
|
|
|
|
/*
|
|
* Namespace for all random operation helpers. Actual tests start below
|
|
*/
|
|
var RandomOps = {
|
|
// Change this to print all operations run.
|
|
verbose: false,
|
|
// 'Random' documents will have various combinations of these names mapping to these values
|
|
fieldNames: ["a", "b", "c", "longerName", "numbered10", "dashed-name"],
|
|
fieldValues: [ true, false, 0, 44, -123, "", "String", [], [false, "x"],
|
|
["array", 1, {doc: true}, new Date().getTime()], {},
|
|
{embedded: "document", weird: ["values", 0, false]}, new Date().getTime()
|
|
],
|
|
|
|
/*
|
|
* Return a random element from Array a.
|
|
*/
|
|
randomChoice: function(a) {
|
|
if (a.length === 0) {
|
|
print("randomChoice called on empty input!");
|
|
return null;
|
|
}
|
|
var x = Random.rand();
|
|
while (x === 1.0) { // Would be out of bounds
|
|
x = Random.rand();
|
|
}
|
|
var i = Math.floor(x*a.length);
|
|
return a[i];
|
|
},
|
|
|
|
/*
|
|
* Uses above arrays to create a new doc with a random amount of fields mapping to random
|
|
* values.
|
|
*/
|
|
randomNewDoc: function() {
|
|
var doc = {};
|
|
for (var i = 0; i < Random.randInt(0,this.fieldNames.length); i++) {
|
|
doc[this.randomChoice(this.fieldNames)] = this.randomChoice(this.fieldValues);
|
|
}
|
|
return doc;
|
|
},
|
|
|
|
/*
|
|
* Returns the names of all 'user created' (non admin/local) databases which have some data in
|
|
* them, or an empty list if none exist.
|
|
*/
|
|
getCreatedDatabases: function(conn) {
|
|
var created = [];
|
|
var dbs = conn.getDBs().databases;
|
|
for (var i in dbs) {
|
|
var db = dbs[i];
|
|
if (db.name !== 'local' && db.name !== 'admin' && db.empty === false) {
|
|
created.push(db.name);
|
|
}
|
|
}
|
|
return created;
|
|
},
|
|
|
|
/*
|
|
* Return a random non-system.indexes collection from the 'user created' collections.
|
|
*/
|
|
getRandomExistingCollection: function(conn) {
|
|
var dbs = this.getCreatedDatabases(conn);
|
|
if (dbs.length === 0) { return null; }
|
|
var dbName = this.randomChoice(dbs);
|
|
var db = conn.getDB(dbName);
|
|
if (db.getCollectionNames().length <= 1) {
|
|
return null;
|
|
}
|
|
var coll = this.randomChoice(db.getCollectionNames());
|
|
while (coll == "system.indexes") {
|
|
coll = this.randomChoice(db.getCollectionNames());
|
|
}
|
|
return db[coll];
|
|
},
|
|
|
|
getRandomDoc: function(collection) {
|
|
try {
|
|
var randIndex = Random.randInt(0, collection.find().count());
|
|
return collection.find().sort({$natural: 1}).skip(randIndex).limit(1)[0];
|
|
} catch(e) {
|
|
return undefined;
|
|
}
|
|
},
|
|
|
|
/*
|
|
* Returns a random user defined collection, selecting from only those for which filterFn
|
|
* returns true, or null if there are none.
|
|
*/
|
|
getRandomCollectionWFilter: function(conn, filterFn) {
|
|
var matched = [];
|
|
var dbs = this.getCreatedDatabases(conn);
|
|
for (var i in dbs) {
|
|
var dbName = dbs[i];
|
|
var colls = conn.getDB(dbName).getCollectionNames();
|
|
for (var j in colls) {
|
|
var coll = colls[j];
|
|
if (filterFn(dbName, coll)) {
|
|
matched.push(coll);
|
|
}
|
|
}
|
|
}
|
|
if (matched.length === 0) { return null; }
|
|
return this.randomChoice(matched);
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// RANDOMIZED CRUD OPERATIONS
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
* Insert a random document into a random collection, with a random writeconcern
|
|
*/
|
|
insert: function(conn) {
|
|
var databases = ["tic", "tac", "toe"];
|
|
var collections = ["eeny", "meeny", "miny", "moe"];
|
|
var writeConcerns = [-1, 0, 1, 2, 3, 4, 5, 6, "majority"];
|
|
|
|
var db = this.randomChoice(databases);
|
|
var coll = this.randomChoice(collections);
|
|
var doc = this.randomNewDoc();
|
|
if (Random.rand() < 0.5) {
|
|
doc._id = new ObjectId().str; // Vary whether or not we include the _id
|
|
}
|
|
var writeConcern = this.randomChoice(writeConcerns);
|
|
var journal = this.randomChoice([true, false]);
|
|
if (this.verbose) {
|
|
print("Inserting: ");
|
|
printjson(doc);
|
|
print("With write concern: " + writeConcern + " and journal: " + journal);
|
|
}
|
|
var result = conn.getDB(db)[coll].insert(doc,
|
|
{writeConcern: {w: writeConcern},
|
|
journal: journal});
|
|
assert.eq(result.ok, 1);
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
/*
|
|
* remove a random document from a random collection
|
|
*/
|
|
remove: function(conn) {
|
|
var coll = this.getRandomExistingCollection(conn);
|
|
if (coll === null || coll.find().count() === 0) {
|
|
return null; // No data, can't delete anything.
|
|
}
|
|
var doc = this.getRandomDoc(coll);
|
|
if (doc === undefined) {
|
|
// If multithreaded, there could have been issues finding a random doc.
|
|
// If so, just skip this operation.
|
|
return;
|
|
}
|
|
|
|
if (this.verbose) {
|
|
print("Deleting:");
|
|
printjson(doc);
|
|
}
|
|
try {
|
|
coll.remove(doc);
|
|
} catch(e) {
|
|
if (this.verbose) { print("Caught exception in remove: " + e); }
|
|
}
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
/*
|
|
* Update a random document from a random collection. Set a random field to a (possibly) new
|
|
* value.
|
|
*/
|
|
update: function(conn) {
|
|
var coll = this.getRandomExistingCollection(conn);
|
|
if (coll === null || coll.find().count() === 0) {
|
|
return null; // No data, can't update anything.
|
|
}
|
|
var doc = this.getRandomDoc(coll);
|
|
if (doc === undefined) {
|
|
// If multithreaded, there could have been issues finding a random doc.
|
|
// If so, just skip this operation.
|
|
return;
|
|
}
|
|
|
|
var field = this.randomChoice(this.fieldNames);
|
|
var updateDoc = {$set: {}};
|
|
updateDoc.$set[field] = this.randomChoice(this.fieldValues);
|
|
if (this.verbose) {
|
|
print("Updating:");
|
|
printjson(doc);
|
|
print("with:");
|
|
printjson(updateDoc);
|
|
}
|
|
// If multithreaded, doc might not exist anymore.
|
|
try {
|
|
coll.update(doc, updateDoc);
|
|
} catch(e) {
|
|
if (this.verbose) { print("Caught exception in update: " + e); }
|
|
}
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// RANDOMIZED COMMANDS
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
* Randomly rename a collection to a new name. New name will be an ObjectId string.
|
|
*/
|
|
renameCollection: function(conn) {
|
|
var coll = this.getRandomExistingCollection(conn);
|
|
if (coll === null) { return null; }
|
|
var newName = coll.getDB() + "." + new ObjectId().str;
|
|
if (this.verbose) {
|
|
print("renaming collection " + coll.getFullName() + " to " + newName);
|
|
}
|
|
assert.commandWorked(
|
|
conn.getDB("admin").runCommand({renameCollection: coll.getFullName(), to: newName})
|
|
);
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
/*
|
|
* Randomly drop a user created database.
|
|
*/
|
|
dropDatabase: function(conn) {
|
|
var dbs = this.getCreatedDatabases(conn);
|
|
if (dbs.length === 0) { return null; }
|
|
var dbName = this.randomChoice(dbs);
|
|
if (this.verbose) {
|
|
print("Dropping database " + dbName);
|
|
}
|
|
assert.commandWorked(
|
|
conn.getDB(dbName).runCommand({dropDatabase: 1})
|
|
);
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
/*
|
|
* Randomly drop a user created collection
|
|
*/
|
|
dropCollection: function(conn) {
|
|
var coll = this.getRandomExistingCollection(conn);
|
|
if (coll === null) { return null; }
|
|
if (this.verbose) {
|
|
print("Dropping collection " + coll.getFullName());
|
|
}
|
|
assert.commandWorked(
|
|
conn.getDB(coll.getDB()).runCommand({drop: coll.getName()})
|
|
);
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
/*
|
|
* Randomly create an index on a random field in a random user defined collection.
|
|
*/
|
|
createIndex: function(conn) {
|
|
var coll = this.getRandomExistingCollection(conn);
|
|
if (coll === null) { return null; }
|
|
var index = {};
|
|
index[this.randomChoice(this.fieldNames)] = this.randomChoice([-1, 1]);
|
|
if (this.verbose) {
|
|
print("Adding index " + tojsononeline(index) + " to " + coll.getFullName());
|
|
}
|
|
coll.ensureIndex(index);
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
/*
|
|
* Randomly drop one existing index on a random user defined collection
|
|
*/
|
|
dropIndex: function(conn) {
|
|
var coll = this.getRandomExistingCollection(conn);
|
|
if (coll === null) { return null; }
|
|
var index = this.randomChoice(coll.getIndices());
|
|
if (index.name === "_id_") {
|
|
return null; // Don't drop that one.
|
|
}
|
|
if (this.verbose) {
|
|
print("Dropping index " + tojsononeline(index.key) + " from " + coll.getFullName());
|
|
}
|
|
assert.commandWorked(
|
|
coll.dropIndex(index.name)
|
|
);
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
/*
|
|
* Select a random collection and flip the user flag for usePowerOf2Sizes
|
|
*/
|
|
collMod: function(conn) {
|
|
var coll = this.getRandomExistingCollection(conn);
|
|
if (coll === null) { return null; }
|
|
var toggle = !coll.stats().userFlags;
|
|
if (this.verbose) {
|
|
print("Modifying usePowerOf2Sizes to " + toggle + " on collection " +
|
|
coll.getFullName());
|
|
}
|
|
conn.getDB(coll.getDB()).runCommand({collMod: coll.getName(), usePowerOf2Sizes: toggle});
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
/*
|
|
* Select a random user-defined collection and empty it
|
|
*/
|
|
emptyCapped: function(conn) {
|
|
var isCapped = function(dbName, coll) {
|
|
return conn.getDB(dbName)[coll].isCapped();
|
|
};
|
|
var coll = this.getRandomCollectionWFilter(conn, isCapped);
|
|
if (coll === null) { return null; }
|
|
if (this.verbose) {
|
|
print("Emptying capped collection: " + coll.getFullName());
|
|
}
|
|
assert.commandWorked(
|
|
conn.getDB(coll.getDB()).runCommand({emptycapped: coll.getName()})
|
|
);
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
/*
|
|
* Apply some ops to a random collection. For now we'll just have insert ops.
|
|
*/
|
|
applyOps: function(conn) {
|
|
// Check if there are any valid collections to choose from.
|
|
if (this.getRandomExistingCollection(conn) === null) { return null; }
|
|
var ops = [];
|
|
// Insert between 1 and 10 things.
|
|
for (var i = 0; i < Random.randInt(1, 10); i++) {
|
|
var coll = this.getRandomExistingCollection(conn);
|
|
var doc = this.randomNewDoc();
|
|
doc._id = new ObjectId();
|
|
if (coll !== null) {
|
|
ops.push({op: "i", ns: coll.getFullName(), o: doc});
|
|
}
|
|
}
|
|
if (this.verbose) {
|
|
print("Applying the following ops: ");
|
|
printjson(ops);
|
|
}
|
|
assert.commandWorked(
|
|
conn.getDB("admin").runCommand({applyOps: ops})
|
|
);
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
/*
|
|
* Create a random collection. Use an ObjectId for the name
|
|
*/
|
|
createCollection: function(conn) {
|
|
var dbs = this.getCreatedDatabases(conn);
|
|
if (dbs.length === 0) { return null; }
|
|
var dbName = this.randomChoice(dbs);
|
|
var newName = new ObjectId().str;
|
|
if (this.verbose) {
|
|
print("Creating new collection: " + "dbName" + "." + newName);
|
|
}
|
|
assert.commandWorked(
|
|
conn.getDB(dbName).runCommand({create: newName})
|
|
);
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
/*
|
|
* Convert a random non-capped collection to a capped one with size 1MB.
|
|
*/
|
|
convertToCapped: function(conn) {
|
|
var isNotCapped = function(dbName, coll) {
|
|
return conn.getDB(dbName)[coll].isCapped();
|
|
};
|
|
var coll = this.getRandomCollectionWFilter(conn, isNotCapped);
|
|
if (coll === null) { return null; }
|
|
if (this.verbose) {
|
|
print("Converting " + coll.getFullName() + " to a capped collection.");
|
|
}
|
|
assert.commandWorked(
|
|
conn.getDB(coll.getDB()).runCommand({convertToCapped: coll.getName(), size: 1024*1024})
|
|
);
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
appendOplogNote: function(conn) {
|
|
var note = "Test note " + new ObjectId().str;
|
|
if (this.verbose) {
|
|
print("Appending oplog note: " + note);
|
|
}
|
|
assert.commandWorked(
|
|
conn.getDB("admin").runCommand({appendOplogNote: note, data: {some: 'doc'}})
|
|
);
|
|
if (this.verbose) { print("done."); }
|
|
},
|
|
|
|
/*
|
|
* Repeatedly call methods numOps times, choosing randomly from possibleOps, which should be
|
|
* a list of strings representing method names of the RandomOps object.
|
|
*/
|
|
doRandomWork: function(conn, numOps, possibleOps) {
|
|
for (var i = 0; i < numOps; i++) {
|
|
op = this.randomChoice(possibleOps);
|
|
this[op](conn);
|
|
}
|
|
}
|
|
|
|
}; // End of RandomOps
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// OTHER HELPERS
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function isArbiter(conn) {
|
|
return conn.adminCommand({isMaster: 1}).arbiterOnly === true;
|
|
}
|
|
|
|
function removeFromArray(elem, a) {
|
|
a.splice(a.indexOf(elem), 1);
|
|
}
|
|
|
|
/*
|
|
* builds a function to be passed to assert.soon. Needs to know which node to expect as the new
|
|
* primary
|
|
*/
|
|
var primaryChanged = function(conns, replTest, primaryIndex) {
|
|
return function() {
|
|
return conns[primaryIndex] == replTest.getPrimary();
|
|
};
|
|
};
|
|
|
|
/*
|
|
* If we have determined a collection doesn't match on two hosts, use this to get a string of the
|
|
* differences.
|
|
*/
|
|
function getCollectionDiff(db1, db2, collName) {
|
|
var coll1 = db1[collName];
|
|
var coll2 = db2[collName];
|
|
var cur1 = coll1.find().sort({$natural: 1});
|
|
var cur2 = coll2.find().sort({$natural: 1});
|
|
var diffText = "";
|
|
while (cur1.hasNext() && cur2.hasNext()) {
|
|
var doc1 = cur1.next();
|
|
var doc2 = cur2.next();
|
|
if (doc1 != doc2) {
|
|
diffText += "mismatching doc:" + tojson(doc1) + tojson(doc2);
|
|
}
|
|
}
|
|
if (cur1.hasNext()) {
|
|
diffText += db1.getMongo().host + " has extra documents:";
|
|
while (cur1.hasNext()) {
|
|
diffText += "\n" + tojson(cur1.next());
|
|
}
|
|
}
|
|
if (cur2.hasNext()) {
|
|
diffText += db2.getMongo().host + " has extra documents:";
|
|
while (cur2.hasNext()) {
|
|
diffText += "\n" + tojson(cur2.next());
|
|
}
|
|
}
|
|
return diffText;
|
|
}
|
|
|
|
/*
|
|
* Check if two databases are equal. If not, print out what the differences are to aid with
|
|
* debugging.
|
|
*/
|
|
function assertDBsEq(db1, db2) {
|
|
assert.eq(db1.getName(), db2.getName());
|
|
var hash1 = db1.runCommand({dbHash: 1});
|
|
var hash2 = db2.runCommand({dbHash: 1});
|
|
var host1 = db1.getMongo().host;
|
|
var host2 = db2.getMongo().host;
|
|
var success = true;
|
|
var collNames1 = db1.getCollectionNames();
|
|
var collNames2 = db2.getCollectionNames();
|
|
var diffText = "";
|
|
if (db1.getName() === 'local') {
|
|
// We don't expect the entire local collection to be the same, not even the oplog, since
|
|
// it's a capped collection.
|
|
return;
|
|
}
|
|
else if (hash1.md5 != hash2.md5) {
|
|
for (var i = 0; i < Math.min(collNames1.length, collNames2.length); i++) {
|
|
var collName = collNames1[i];
|
|
if (hash1.collections[collName] !== hash2.collections[collName]) {
|
|
if (db1[collName].stats().capped) {
|
|
if (!db2[collName].stats().capped) {
|
|
success = false;
|
|
diffText += "\n" + collName + " is capped on " + host1 + " but not on " +
|
|
host2;
|
|
}
|
|
else {
|
|
// Skip capped collections. They are not expected to be the same from host
|
|
// to host.
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
success = false;
|
|
diffText += "\n" + collName + " differs: " +
|
|
getCollectionDiff(db1, db2, collName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
assert.eq(success, true, "Database " + db1.getName() + " differs on " + host1 + " and " +
|
|
host2 + "\nCollections: " + collNames1 + " vs. " + collNames2 + "\n" + diffText);
|
|
}
|
|
|
|
/*
|
|
* Check the database hashes of all databases to ensure each node of the replica set has the same
|
|
* data.
|
|
*/
|
|
function assertSameData(primary, conns) {
|
|
var dbs = primary.getDBs().databases;
|
|
for (var i in dbs) {
|
|
var db1 = primary.getDB(dbs[i].name);
|
|
for (var j in conns) {
|
|
var conn = conns[j];
|
|
if (!isArbiter(conn)) {
|
|
var db2 = conn.getDB(dbs[i].name);
|
|
assertDBsEq(db1, db2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* function to pass to a thread to make it start doing random commands/CRUD operations.
|
|
*/
|
|
function startCmds(randomOps, host) {
|
|
var ops = [
|
|
"insert", "remove", "update", "renameCollection", "dropDatabase",
|
|
"dropCollection", "createIndex", "dropIndex", "collMod", "emptyCapped", "applyOps",
|
|
"createCollection", "convertToCapped", "appendOplogNote"
|
|
];
|
|
var m = new Mongo(host);
|
|
var numOps = 200;
|
|
randomOps.doRandomWork(m, numOps, ops);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* function to pass to a thread to make it start doing random CRUD operations.
|
|
*/
|
|
function startCRUD(randomOps, host) {
|
|
var m = new Mongo(host);
|
|
var numOps = 500;
|
|
randomOps.doRandomWork(m, numOps, ["insert", "update", "remove"]);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* To avoid race conditions on things like trying to drop a collection while another thread is
|
|
* trying to rename it, just have one thread that might issue commands, and the others do random
|
|
* CRUD operations. To be clear, this is something that the Mongod should be able to handle, but
|
|
* this test code does not have atomic random operations. E.g. it has to first randomly select
|
|
* a collection to drop an index from, which may not be there by the time it tries to get a list
|
|
* of indices on the collection.
|
|
*/
|
|
function doMultiThreadedWork(primary, numThreads) {
|
|
var threads = [];
|
|
// The command thread
|
|
// Note we pass the hostname, as we have to re-establish the connection in the new thread.
|
|
var cmdThread = new ScopedThread(startCmds, RandomOps, primary.host);
|
|
threads.push(cmdThread);
|
|
cmdThread.start();
|
|
// Other CRUD threads
|
|
for (var i = 1; i < numThreads; i++) {
|
|
var crudThread = new ScopedThread(startCRUD, RandomOps, primary.host);
|
|
threads.push(crudThread);
|
|
crudThread.start();
|
|
}
|
|
for (var j = 0; j < numThreads; j++) {
|
|
assert.eq(threads[j].returnData(), true);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// START ACTUAL TESTING
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
(function() {
|
|
"use strict";
|
|
var name = "mixed_storage_and_version";
|
|
// Create a replica set with 2 nodes of each of the types below, plus one arbiter.
|
|
var oldVersion = "last-stable";
|
|
var newVersion = "latest";
|
|
var setups = [{binVersion: newVersion, storageEngine: 'mmapv1'},
|
|
{binVersion: newVersion, storageEngine: 'wiredTiger'},
|
|
{binVersion: oldVersion}
|
|
];
|
|
var nodes = {};
|
|
var node = 0;
|
|
// Add each one twice.
|
|
for (var i in setups) {
|
|
nodes["n" + node] = setups[i];
|
|
node++;
|
|
nodes["n" + node] = setups[i];
|
|
node++;
|
|
}
|
|
nodes["n" + 2 * setups.length] = {arbiter: true};
|
|
var replTest = new ReplSetTest({nodes: nodes, name: name});
|
|
var conns = replTest.startSet();
|
|
|
|
var config = replTest.getReplSetConfig();
|
|
// Make sure everyone is syncing from the primary, to ensure we have all combinations of
|
|
// primary/secondary syncing.
|
|
config.settings = {chainingAllowed: false};
|
|
replTest.initiate();
|
|
// Ensure all are synced.
|
|
replTest.awaitSecondaryNodes(120000);
|
|
var primary = replTest.getPrimary();
|
|
|
|
// Keep track of the indices of different types of primaries.
|
|
// We'll rotate to get a primary of each type.
|
|
var possiblePrimaries = [0,2,4];
|
|
var highestPriority = 2;
|
|
while (possiblePrimaries.length > 0) {
|
|
config = primary.getDB("local").system.replset.findOne();
|
|
var primaryIndex = RandomOps.randomChoice(possiblePrimaries);
|
|
print("TRANSITIONING to " + tojsononeline(setups[primaryIndex/2]) + " as primary");
|
|
// Remove chosen type from future choices.
|
|
removeFromArray(primaryIndex, possiblePrimaries);
|
|
config.members[primaryIndex].priority = highestPriority;
|
|
if (config.version === undefined) {
|
|
config.version = 2;
|
|
}
|
|
else {
|
|
config.version++;
|
|
}
|
|
highestPriority++;
|
|
printjson(config);
|
|
try {
|
|
primary.getDB("admin").runCommand({replSetReconfig: config});
|
|
}
|
|
catch(e) {
|
|
// Expected to fail, as we'll have to reconnect.
|
|
}
|
|
replTest.awaitReplication(60000); // 2 times the election period.
|
|
assert.soon(primaryChanged(conns, replTest, primaryIndex),
|
|
"waiting for higher priority primary to be elected", 100000);
|
|
print("New primary elected, doing a bunch of work");
|
|
primary = replTest.getPrimary();
|
|
doMultiThreadedWork(primary, 10);
|
|
replTest.awaitReplication(50000);
|
|
print("Work done, checking to see all nodes match");
|
|
assertSameData(primary, conns);
|
|
}
|
|
})();
|