summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Milkie <milkie@10gen.com>2013-04-22 14:48:02 -0400
committerEric Milkie <milkie@10gen.com>2013-08-29 16:02:08 -0400
commit0364ef4bdd4c17a07801457334aee32b9f601828 (patch)
tree4262ee595a58d7e1a127894a7154488c662ee963
parente62a094fd0c39c3a2d56f990d904da2fa9653b58 (diff)
downloadmongo-0364ef4bdd4c17a07801457334aee32b9f601828.tar.gz
SERVER-9339 ensure all rollback update/delete paths are recorded
Conflicts: src/mongo/db/ops/update.cpp
-rw-r--r--jstests/replsets/rollback5.js103
-rw-r--r--src/mongo/db/dbhelpers.cpp4
-rw-r--r--src/mongo/db/ops/delete.cpp3
-rw-r--r--src/mongo/db/ops/update.cpp3
-rw-r--r--src/mongo/db/repl/rs_rollback.cpp10
-rw-r--r--src/mongo/shell/shell_utils_launcher.cpp9
6 files changed, 124 insertions, 8 deletions
diff --git a/jstests/replsets/rollback5.js b/jstests/replsets/rollback5.js
new file mode 100644
index 00000000000..5828dcf1b61
--- /dev/null
+++ b/jstests/replsets/rollback5.js
@@ -0,0 +1,103 @@
+// test that a rollback directory is created during a replica set rollback
+// this also tests that updates are recorded in the rollback file
+// (this test does no delete rollbacks)
+
+var replTest = new ReplSetTest({ name: 'rollback5', nodes: 3 });
+var nodes = replTest.nodeList();
+
+var conns = replTest.startSet();
+var r = replTest.initiate({ "_id": "rollback5",
+ "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();
+var a_conn = conns[0];
+var b_conn = conns[1];
+a_conn.setSlaveOk();
+b_conn.setSlaveOk();
+var A = a_conn.getDB("test");
+var B = b_conn.getDB("test");
+var AID = replTest.getNodeId(a_conn);
+var BID = replTest.getNodeId(b_conn);
+var Apath = "/data/db/rollback5-0/";
+var Bpath = "/data/db/rollback5-1/";
+assert(master == conns[0], "conns[0] assumed to be master");
+assert(a_conn.host == master.host);
+
+// 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.");
+
+A.foo.update({key:'value1'}, {$set: {req: 'req'}}, true);
+A.foo.runCommand({getLastError : 1, w : 2, wtimeout : 60000});
+replTest.stop(AID);
+
+master = replTest.getMaster();
+assert(b_conn.host == master.host);
+B.foo.update({key:'value1'}, {$set: {res: 'res'}}, true);
+B.foo.runCommand({getLastError : 1, w : 1, wtimeout : 60000});
+replTest.stop(BID);
+replTest.restart(AID);
+master = replTest.getMaster();
+assert(a_conn.host == master.host);
+A.foo.update({key:'value2'}, {$set: {req: 'req'}}, true);
+A.foo.runCommand({getLastError : 1, w : 1, wtimeout : 60000});
+replTest.restart(BID); // should rollback
+reconnect(B);
+
+print("BEFORE------------------");
+printjson(A.foo.find().toArray());
+printjson(B.foo.find().toArray());
+
+replTest.awaitReplication();
+replTest.awaitSecondaryNodes();
+
+print("AFTER------------------");
+printjson(A.foo.find().toArray());
+printjson(B.foo.find().toArray());
+assert.eq(2, A.foo.count());
+assert.eq('req', A.foo.findOne({key:'value1'}).req);
+assert.eq(null, A.foo.findOne({key:'value1'}).res);
+assert.eq(2, B.foo.count());
+assert.eq('req', B.foo.findOne({key:'value1'}).req);
+assert.eq(null, B.foo.findOne({key:'value1'}).res);
+
+// check here for rollback files
+var rollbackDir = Bpath + "rollback/";
+assert(pathExists(rollbackDir), "rollback directory was not created!");
+
+print("rollback5.js SUCCESS");
+replTest.stopSet(15);
+
+
+function wait(f) {
+ var n = 0;
+ while (!f()) {
+ if (n % 4 == 0)
+ print("rollback5.js waiting");
+ if (++n == 4) {
+ print("" + f);
+ }
+ assert(n < 200, 'tried 200 times, giving up');
+ sleep(1000);
+ }
+}
+
+function reconnect(a) {
+ wait(function() {
+ try {
+ a.bar.stats();
+ return true;
+ } catch(e) {
+ print(e);
+ return false;
+ }
+ });
+};
+
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp
index 85b38a1ff78..5e6736624a3 100644
--- a/src/mongo/db/dbhelpers.cpp
+++ b/src/mongo/db/dbhelpers.cpp
@@ -380,7 +380,6 @@ namespace mongo {
stringstream ss;
ss << why << "." << terseCurrentTime(false) << "." << NUM++ << ".bson";
_file /= ss.str();
-
}
RemoveSaver::~RemoveSaver() {
@@ -397,7 +396,8 @@ namespace mongo {
_out = new ofstream();
_out->open( _file.string().c_str() , ios_base::out | ios_base::binary );
if ( ! _out->good() ) {
- LOG( LL_WARNING ) << "couldn't create file: " << _file.string() << " for remove saving" << endl;
+ error() << "couldn't create file: " << _file.string() <<
+ " for remove saving" << endl;
delete _out;
_out = 0;
return;
diff --git a/src/mongo/db/ops/delete.cpp b/src/mongo/db/ops/delete.cpp
index 790dd543cd0..8d7b1cf9d3b 100644
--- a/src/mongo/db/ops/delete.cpp
+++ b/src/mongo/db/ops/delete.cpp
@@ -129,9 +129,6 @@ namespace mongo {
}
}
- if ( rs )
- rs->goingToDelete( rloc.obj() /*cc->c->current()*/ );
-
theDataFileMgr.deleteRecord(ns, rloc.rec(), rloc);
nDeleted++;
if ( foundAllResults ) {
diff --git a/src/mongo/db/ops/update.cpp b/src/mongo/db/ops/update.cpp
index 954d2e0b1f3..57c59c4446c 100644
--- a/src/mongo/db/ops/update.cpp
+++ b/src/mongo/db/ops/update.cpp
@@ -364,9 +364,6 @@ namespace mongo {
d->paddingFits();
}
else {
- if ( rs )
- rs->goingToDelete( onDisk );
-
BSONObj newObj = mss->createNewFromMods();
checkTooLarge(newObj);
DiskLoc newLoc = theDataFileMgr.updateRecord(ns,
diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp
index 043845873d5..c9c380e739d 100644
--- a/src/mongo/db/repl/rs_rollback.cpp
+++ b/src/mongo/db/repl/rs_rollback.cpp
@@ -465,6 +465,16 @@ namespace mongo {
// todo: lots of overhead in context, this can be faster
Client::Context c(d.ns);
+
+ // Add the doc to our rollback file
+ BSONObj obj;
+ bool found = Helpers::findOne(d.ns, pattern, obj, false);
+ if ( found ) {
+ rs->goingToDelete( obj );
+ } else {
+ error() << "rollback cannot find object by id" << endl;
+ }
+
if( i->second.isEmpty() ) {
// wasn't on the primary; delete.
/* TODO1.6 : can't delete from a capped collection. need to handle that here. */
diff --git a/src/mongo/shell/shell_utils_launcher.cpp b/src/mongo/shell/shell_utils_launcher.cpp
index cfe679d4ea2..3484139f8cf 100644
--- a/src/mongo/shell/shell_utils_launcher.cpp
+++ b/src/mongo/shell/shell_utils_launcher.cpp
@@ -533,6 +533,14 @@ namespace mongo {
return undefinedReturn;
}
+ BSONObj PathExists( const BSONObj &a, void* data ) {
+ verify( a.nFields() == 1 );
+ string path = a.firstElement().valuestrsafe();
+ verify( !path.empty() );
+ bool exists = boost::filesystem::exists(path);
+ return BSON( string( "" ) << exists );
+ }
+
void copyDir( const boost::filesystem::path &from, const boost::filesystem::path &to ) {
boost::filesystem::directory_iterator end;
boost::filesystem::directory_iterator i( from );
@@ -748,6 +756,7 @@ namespace mongo {
scope.injectNative( "clearRawMongoProgramOutput", ClearRawMongoProgramOutput );
scope.injectNative( "waitProgram" , WaitProgram );
scope.injectNative( "resetDbpath", ResetDbpath );
+ scope.injectNative( "pathExists", PathExists );
scope.injectNative( "copyDbpath", CopyDbpath );
}
}