Skip to content

Commit

Permalink
SERVER-9339 ensure all rollback update/delete paths are recorded
Browse files Browse the repository at this point in the history
Conflicts:
	src/mongo/db/ops/update.cpp
  • Loading branch information
milkie committed Aug 29, 2013
1 parent e62a094 commit 0364ef4
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 8 deletions.
103 changes: 103 additions & 0 deletions jstests/replsets/rollback5.js
Original file line number Diff line number Diff line change
@@ -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;
}
});
};

4 changes: 2 additions & 2 deletions src/mongo/db/dbhelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,6 @@ namespace mongo {
stringstream ss;
ss << why << "." << terseCurrentTime(false) << "." << NUM++ << ".bson";
_file /= ss.str();

}

RemoveSaver::~RemoveSaver() {
Expand All @@ -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;
Expand Down
3 changes: 0 additions & 3 deletions src/mongo/db/ops/delete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,6 @@ namespace mongo {
}
}

if ( rs )
rs->goingToDelete( rloc.obj() /*cc->c->current()*/ );

theDataFileMgr.deleteRecord(ns, rloc.rec(), rloc);
nDeleted++;
if ( foundAllResults ) {
Expand Down
3 changes: 0 additions & 3 deletions src/mongo/db/ops/update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 10 additions & 0 deletions src/mongo/db/repl/rs_rollback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
9 changes: 9 additions & 0 deletions src/mongo/shell/shell_utils_launcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down Expand Up @@ -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 );
}
}
Expand Down

0 comments on commit 0364ef4

Please sign in to comment.