summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristina <kristina@10gen.com>2012-10-01 10:37:58 -0400
committerEric Milkie <milkie@10gen.com>2012-10-03 13:59:27 -0400
commit3e1e25a4d1d6b2b70669787242cbf77d78f99387 (patch)
treefc295b61cf9708f4f00ffff82998120dfb127ab9
parente76de3ccd440962bec402d7b47f238e596fccad7 (diff)
downloadmongo-3e1e25a4d1d6b2b70669787242cbf77d78f99387.tar.gz
SERVER-7198 Prevent rollback in inconsistent state
-rw-r--r--jstests/slowWeekly/minvalid2.js70
-rw-r--r--src/mongo/db/repl/rs_rollback.cpp17
2 files changed, 87 insertions, 0 deletions
diff --git a/jstests/slowWeekly/minvalid2.js b/jstests/slowWeekly/minvalid2.js
new file mode 100644
index 00000000000..f7d5dd534d4
--- /dev/null
+++ b/jstests/slowWeekly/minvalid2.js
@@ -0,0 +1,70 @@
+/**
+ * This checks rollback, which shouldn't happen unless we have reached minvalid.
+ * 1. make 3-member set w/arb (2)
+ * 2. shut down 1
+ * 3. do writes to 0
+ * 4. modify 0's minvalid
+ * 5. shut down 0
+ * 6. start up 1
+ * 7. writes on 1
+ * 8. start up 0
+ * 9. check 0 does not rollback
+ */
+
+print("1. make 3-member set w/arb (2)");
+var name = "minvalid"
+var replTest = new ReplSetTest({name: name, nodes: 3, oplogSize:1});
+var host = getHostName();
+
+var nodes = replTest.startSet();
+replTest.initiate({_id : name, members : [
+ {_id : 0, host : host+":"+replTest.ports[0]},
+ {_id : 1, host : host+":"+replTest.ports[1]},
+ {_id : 2, host : host+":"+replTest.ports[2], arbiterOnly : true}
+]});
+var master = replTest.getMaster();
+var mdb = master.getDB("foo");
+
+mdb.foo.save({a: 1000});
+replTest.awaitReplication();
+
+print("2: shut down 1");
+replTest.stop(1);
+
+print("3: do writes to 0");
+mdb.foo.save({a: 1001});
+
+print("4: modify 0's minvalid");
+var local = master.getDB("local");
+var lastOp = local.oplog.rs.find().sort({$natural:-1}).limit(1).next();
+printjson(lastOp);
+
+local.replset.minvalid.insert({ts:new Timestamp(lastOp.ts.t, lastOp.ts.i+1),
+ h:new NumberLong("1234567890")});
+printjson(local.replset.minvalid.findOne());
+
+print("5: shut down 0");
+replTest.stop(0);
+
+print("6: start up 1");
+replTest.restart(1);
+
+print("7: writes on 1")
+master = replTest.getMaster();
+mdb1 = master.getDB("foo");
+mdb1.foo.save({a:1002});
+
+print("8: start up 0");
+replTest.restart(0);
+
+print("9: check 0 does not rollback");
+assert.soon(function(){
+ var status = master.adminCommand({replSetGetStatus:1});
+ var stateStr = status.members[0].stateStr;
+ assert(stateStr != "ROLLBACK" &&
+ stateStr != "SECONDARY" &&
+ stateStr != "PRIMARY", tojson(status));
+ return stateStr == "FATAL";
+});
+
+replTest.stopSet(15);
diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp
index d6724966313..c912116f9d5 100644
--- a/src/mongo/db/repl/rs_rollback.cpp
+++ b/src/mongo/db/repl/rs_rollback.cpp
@@ -573,6 +573,23 @@ namespace mongo {
}
void ReplSetImpl::syncRollback(OplogReader&r) {
+ // check that we are at minvalid, otherwise we cannot rollback as we may be in an
+ // inconsistent state
+ {
+ Lock::DBRead lk("local.replset.minvalid");
+ BSONObj mv;
+ if( Helpers::getSingleton("local.replset.minvalid", mv) ) {
+ OpTime minvalid = mv["ts"]._opTime();
+ if( minvalid > lastOpTimeWritten ) {
+ log() << "replSet need to rollback, but in inconsistent state" << endl;
+ log() << "minvalid: " << minvalid.toString() << " our last optime: "
+ << lastOpTimeWritten.toString() << endl;
+ changeState(MemberState::RS_FATAL);
+ return;
+ }
+ }
+ }
+
unsigned s = _syncRollback(r);
if( s )
sleepsecs(s);