Files
mongo/jstests/replsets/rslib.js

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;
};
}());