261 lines
6.9 KiB
JavaScript
Executable File
261 lines
6.9 KiB
JavaScript
Executable File
// test rollback in replica sets
|
|
|
|
// try running as :
|
|
//
|
|
// mongo --nodb rollback.js | tee out | grep -v ^m31
|
|
//
|
|
load("jstests/replsets/rslib.js");
|
|
|
|
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("rollback3.js waiting " + w);
|
|
if (++n == 4) {
|
|
print("" + f);
|
|
}
|
|
if (n == 200) {
|
|
print("rollback3.js failing waited too long");
|
|
throw "wait error";
|
|
}
|
|
sleep(1000);
|
|
}
|
|
}
|
|
|
|
function dbs_match(a, b) {
|
|
print("dbs_match");
|
|
|
|
var ac = a.system.namespaces.find().sort({name:1}).toArray();
|
|
var bc = b.system.namespaces.find().sort({name:1}).toArray();
|
|
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]);
|
|
// system.indexes doesn't have _id so the more involved sort here:
|
|
if (!friendlyEqual(a[c[i]].find().sort({ _id: 1, ns:1, name:1 }).toArray(), b[c[i]].find().sort({ _id: 1, ns:1,name:1 }).toArray())) {
|
|
print("dbs_match: collections don't match " + c[i]);
|
|
if (a[c[i]].count() < 12) {
|
|
printjson(a[c[i]].find().sort({ _id: 1 }).toArray());
|
|
printjson(b[c[i]].find().sort({ _id: 1 }).toArray());
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* these writes will be initial data and replicate everywhere. */
|
|
function doInitialWrites(db) {
|
|
db.b.insert({ x: 1 });
|
|
db.b.ensureIndex({ x: 1 });
|
|
db.oldname.insert({ y: 1 });
|
|
db.oldname.insert({ y: 2 });
|
|
db.oldname.ensureIndex({ y: 1 },true);
|
|
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: 40333333, a: 1 });
|
|
for (var i = 0; i < 200; i++) t.insert({ i: i });
|
|
t.insert({ q: 40, a: 2 });
|
|
t.insert({ q: 70, txt: 'willremove' });
|
|
|
|
db.createCollection("kap", { capped: true, size: 5000 });
|
|
db.kap.insert({ foo: 1 })
|
|
}
|
|
|
|
/* 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");
|
|
|
|
// drop a collection - we'll need all its data back!
|
|
t.drop();
|
|
|
|
// drop an index - verify it comes back
|
|
db.b.dropIndexes();
|
|
|
|
// two to see if we transitively rollback?
|
|
db.oldname.renameCollection("newname");
|
|
db.newname.renameCollection("fooname");
|
|
|
|
assert(db.fooname.count() > 0, "count rename");
|
|
|
|
// test roll back (drop) a whole database
|
|
abc = db.getSisterDB("abc");
|
|
abc.foo.insert({ x: 1 });
|
|
abc.bar.insert({ y: 999 });
|
|
|
|
// test making and dropping a database
|
|
//mkd = db.getSisterDB("mkd");
|
|
//mkd.c.insert({ y: 99 });
|
|
//mkd.dropDatabase();
|
|
}
|
|
|
|
function doWritesToKeep2(db) {
|
|
t = db.bar;
|
|
t.insert({ txt: 'foo' });
|
|
t.remove({ q: 70 });
|
|
t.update({ q: 0 }, { $inc: { y: 33} });
|
|
}
|
|
|
|
doTest = function (signal) {
|
|
|
|
var replTest = new ReplSetTest({ name: 'unicomplex', 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
|
|
var master = replTest.getMaster();
|
|
a_conn = conns[0];
|
|
A = a_conn.getDB("admin");
|
|
b_conn = conns[1];
|
|
a_conn.setSlaveOk();
|
|
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.");
|
|
|
|
// Wait for initial replication
|
|
var a = a_conn.getDB("foo");
|
|
var b = b_conn.getDB("foo");
|
|
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(); }); }); });
|
|
|
|
A.runCommand({ replSetTest: 1, blind: true });
|
|
reconnect(a,b);
|
|
wait(function () { try { return B.isMaster().ismaster; } catch(e) { return false; } });
|
|
|
|
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);
|
|
|
|
wait(function () { try { return !B.isMaster().ismaster; } catch(e) { return false; } });
|
|
wait(function () { try { return A.isMaster().ismaster; } catch(e) { return false; } });
|
|
|
|
assert(a.bar.count() >= 1, "count check");
|
|
doWritesToKeep2(a);
|
|
|
|
// 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);
|
|
|
|
wait(function () { return B.isMaster().ismaster || B.isMaster().secondary; });
|
|
|
|
// everyone is up here...
|
|
assert(A.isMaster().ismaster || A.isMaster().secondary, "A up");
|
|
assert(B.isMaster().ismaster || B.isMaster().secondary, "B up");
|
|
replTest.awaitReplication();
|
|
|
|
assert.soon(function() { return B.isMaster().secondary; });
|
|
|
|
assert( dbs_match(a,b), "server data sets do not match after rollback, something is wrong");
|
|
|
|
pause("rollback3.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("rollback3.js");
|
|
doTest( 15 );
|