238 lines
7.6 KiB
JavaScript
238 lines
7.6 KiB
JavaScript
var wait;
|
|
var occasionally;
|
|
var reconnect;
|
|
var getLatestOp;
|
|
var waitForAllMembers;
|
|
var reconfig;
|
|
var awaitOpTime;
|
|
var startSetIfSupportsReadMajority;
|
|
var waitUntilAllNodesCaughtUp;
|
|
var updateConfigIfNotDurable;
|
|
|
|
(function() {
|
|
"use strict";
|
|
var count = 0;
|
|
var w = 0;
|
|
|
|
wait = function(f, msg) {
|
|
w++;
|
|
var n = 0;
|
|
while (!f()) {
|
|
if (n % 4 == 0)
|
|
print("waiting " + w);
|
|
if (++n == 4) {
|
|
print("" + f);
|
|
}
|
|
if (n >= 200) {
|
|
throw new Error('tried 200 times, giving up on ' + msg);
|
|
}
|
|
sleep(1000);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Use this to do something once every 4 iterations.
|
|
*
|
|
* <pre>
|
|
* for (i=0; i<1000; i++) {
|
|
* occasionally(function() { print("4 more iterations"); });
|
|
* }
|
|
* </pre>
|
|
*/
|
|
occasionally = function(f, n) {
|
|
var interval = n || 4;
|
|
if (count % interval == 0) {
|
|
f();
|
|
}
|
|
count++;
|
|
};
|
|
|
|
reconnect = function(a) {
|
|
wait(function() {
|
|
var db;
|
|
try {
|
|
// make this work with either dbs or connections
|
|
if (typeof(a.getDB) == "function") {
|
|
db = a.getDB('foo');
|
|
} else {
|
|
db = a;
|
|
}
|
|
db.bar.stats();
|
|
if (jsTest.options().keyFile) { // SERVER-4241: Shell connections don't
|
|
// re-authenticate on reconnect
|
|
return jsTest.authenticate(db.getMongo());
|
|
}
|
|
return true;
|
|
} catch (e) {
|
|
print(e);
|
|
return false;
|
|
}
|
|
});
|
|
};
|
|
|
|
getLatestOp = function(server) {
|
|
server.getDB("admin").getMongo().setSlaveOk();
|
|
var log = server.getDB("local")['oplog.rs'];
|
|
var cursor = log.find({}).sort({'$natural': -1}).limit(1);
|
|
if (cursor.hasNext()) {
|
|
return cursor.next();
|
|
}
|
|
return null;
|
|
};
|
|
|
|
waitForAllMembers = function(master, timeout) {
|
|
var failCount = 0;
|
|
|
|
assert.soon(function() {
|
|
var state = null;
|
|
try {
|
|
state = master.getSisterDB("admin").runCommand({replSetGetStatus: 1});
|
|
failCount = 0;
|
|
} catch (e) {
|
|
// Connection can get reset on replica set failover causing a socket exception
|
|
print("Calling replSetGetStatus failed");
|
|
print(e);
|
|
return false;
|
|
}
|
|
occasionally(function() {
|
|
printjson(state);
|
|
}, 10);
|
|
|
|
for (var m in state.members) {
|
|
if (state.members[m].state != 1 && // PRIMARY
|
|
state.members[m].state != 2 && // SECONDARY
|
|
state.members[m].state != 7) { // ARBITER
|
|
return false;
|
|
}
|
|
}
|
|
printjson(state);
|
|
return true;
|
|
}, "not all members ready", timeout || 60000);
|
|
|
|
print("All members are now in state PRIMARY, SECONDARY, or ARBITER");
|
|
};
|
|
|
|
reconfig = function(rs, config, force) {
|
|
"use strict";
|
|
var admin = rs.getPrimary().getDB("admin");
|
|
var e;
|
|
var master;
|
|
try {
|
|
assert.commandWorked(admin.runCommand({replSetReconfig: config, force: force}));
|
|
} catch (e) {
|
|
if (tojson(e).indexOf("error doing query: failed") < 0) {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
var master = rs.getPrimary().getDB("admin");
|
|
waitForAllMembers(master);
|
|
|
|
return master;
|
|
};
|
|
|
|
awaitOpTime = function(node, opTime) {
|
|
var ts, ex;
|
|
assert.soon(
|
|
function() {
|
|
try {
|
|
// The following statement extracts the timestamp field from the most recent
|
|
// element of
|
|
// the oplog, and stores it in "ts".
|
|
ts = node.getDB("local")['oplog.rs']
|
|
.find({})
|
|
.sort({'$natural': -1})
|
|
.limit(1)
|
|
.next()
|
|
.ts;
|
|
if ((ts.t == opTime.t) && (ts.i == opTime.i)) {
|
|
return true;
|
|
}
|
|
ex = null;
|
|
return false;
|
|
} catch (ex) {
|
|
return false;
|
|
}
|
|
},
|
|
function() {
|
|
var message = "Node " + node + " only reached optime " + tojson(ts) + " not " +
|
|
tojson(opTime);
|
|
if (ex) {
|
|
message += "; last attempt failed with exception " + tojson(ex);
|
|
}
|
|
return message;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Uses the results of running replSetGetStatus against an arbitrary replset node to wait until
|
|
* all nodes in the set are replicated through the same optime.
|
|
* 'rs' is an array of connections to replica set nodes. This function is useful when you
|
|
* don't have a ReplSetTest object to use, otherwise ReplSetTest.awaitReplication is preferred.
|
|
*/
|
|
waitUntilAllNodesCaughtUp = function(rs, timeout) {
|
|
var rsStatus;
|
|
var firstConflictingIndex;
|
|
var ot;
|
|
var otherOt;
|
|
assert.soon(
|
|
function() {
|
|
rsStatus = rs[0].adminCommand('replSetGetStatus');
|
|
if (rsStatus.ok != 1) {
|
|
return false;
|
|
}
|
|
assert.eq(rs.length, rsStatus.members.length, tojson(rsStatus));
|
|
ot = rsStatus.members[0].optime;
|
|
for (var i = 1; i < rsStatus.members.length; ++i) {
|
|
otherOt = rsStatus.members[i].optime;
|
|
if (bsonWoCompare({ts: otherOt.ts}, {ts: ot.ts}) ||
|
|
bsonWoCompare({t: otherOt.t}, {t: ot.t})) {
|
|
firstConflictingIndex = i;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
function() {
|
|
return "Optimes of members 0 (" + tojson(ot) + ") and " + firstConflictingIndex +
|
|
" (" + tojson(otherOt) + ") are different in " + tojson(rsStatus);
|
|
},
|
|
timeout);
|
|
};
|
|
|
|
/**
|
|
* Starts each node in the given replica set if the storage engine supports readConcern
|
|
*'majority'.
|
|
* Returns true if the replica set was started successfully and false otherwise.
|
|
*
|
|
* @param replSetTest - The instance of {@link ReplSetTest} to start
|
|
* @param options - The options passed to {@link ReplSetTest.startSet}
|
|
*/
|
|
startSetIfSupportsReadMajority = function(replSetTest, options) {
|
|
try {
|
|
replSetTest.startSet(options);
|
|
} catch (e) {
|
|
var conn = MongoRunner.runMongod();
|
|
if (!conn.getDB("admin").serverStatus().storageEngine.supportsCommittedReads) {
|
|
MongoRunner.stopMongod(conn);
|
|
return false;
|
|
}
|
|
throw e;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Changes the replica set config if journaling/ephemal storage engine to set
|
|
* writeConcernMajorityJournalDefault to false.
|
|
*/
|
|
updateConfigIfNotDurable = function(config) {
|
|
var runningWithoutJournaling = TestData.noJournal ||
|
|
0 != ["inMemory", "ephemeralForTest"].filter((a) => a == TestData.storageEngine).length;
|
|
if (runningWithoutJournaling) {
|
|
config.writeConcernMajorityJournalDefault = false;
|
|
}
|
|
return config;
|
|
};
|
|
}());
|