summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKatherine Walker <katherine.walker@mongodb.com>2017-10-12 17:34:05 -0400
committerKatherine Walker <katherine.walker@mongodb.com>2017-10-20 10:53:27 -0400
commit999d491ed3b27d47dd0a8d9d86b4f7359f2dfae3 (patch)
tree4c55986caa1c445433c23e775dc880e648438ab4
parentb3d26b5f94636870f75b92582d6b7c31190a1295 (diff)
downloadmongo-999d491ed3b27d47dd0a8d9d86b4f7359f2dfae3.tar.gz
SERVER-31295 SERVER-31489 Allow applyOps to rollback without UUID
-rw-r--r--src/mongo/db/repl/rs_rollback.cpp47
-rw-r--r--src/mongo/db/repl/rs_rollback.h4
-rw-r--r--src/mongo/db/repl/rs_rollback_test.cpp79
3 files changed, 96 insertions, 34 deletions
diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp
index 56131915223..b54eea9a7d8 100644
--- a/src/mongo/db/repl/rs_rollback.cpp
+++ b/src/mongo/db/repl/rs_rollback.cpp
@@ -182,7 +182,8 @@ Status FixUpInfo::recordDropTargetInfo(const BSONElement& dropTarget,
}
Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInfo,
- const BSONObj& ourObj) {
+ const BSONObj& ourObj,
+ bool isNestedApplyOpsCommand) {
// Checks that the oplog entry is smaller than 512 MB. We do not roll back if the
// oplog entry is larger than 512 MB.
@@ -190,10 +191,31 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInf
throw RSFatalException(str::stream() << "Rollback too large, oplog size: "
<< ourObj.objsize());
- // Parse the oplog entry.
- auto oplogEntry = OplogEntry(ourObj);
+ // If required fields are not present in the BSONObj for an applyOps entry, create these fields
+ // and populate them with dummy values before parsing ourObj as an oplog entry.
+ BSONObjBuilder bob;
+ if (isNestedApplyOpsCommand) {
+ if (!ourObj.hasField("ts")) {
+ bob.appendTimestamp("ts");
+ }
+ if (!ourObj.hasField("h")) {
+ long long dummyHash = 0;
+ bob.append("h", dummyHash);
+ }
+ }
+ bob.appendElements(ourObj);
+ BSONObj fixedObj = bob.obj();
- LOG(2) << "Updating rollback FixUpInfo for local oplog entry: " << oplogEntry.toBSON();
+ // Parse the oplog entry.
+ auto oplogEntry = OplogEntry(fixedObj);
+
+ if (isNestedApplyOpsCommand) {
+ LOG(2) << "Updating rollback FixUpInfo for nested applyOps entry: "
+ << redact(oplogEntry.toBSON());
+ } else {
+ LOG(2) << "Updating rollback FixUpInfo for local oplog entry: "
+ << redact(oplogEntry.toBSON());
+ }
// Extract the op's collection namespace and UUID.
NamespaceString nss = oplogEntry.getNamespace();
@@ -202,10 +224,6 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInf
if (oplogEntry.getOpType() == OpTypeEnum::kNoop)
return Status::OK();
- // If we are inserting/updating/deleting a document in the oplog entry, we will update
- // the doc._id field when we actually insert the docID into the docsToRefetch set.
- DocID doc = DocID(oplogEntry.raw, BSONElement(), *uuid);
-
if (oplogEntry.getNamespace().isEmpty()) {
throw RSFatalException(str::stream() << "Local op on rollback has no ns: "
<< redact(oplogEntry.toBSON()));
@@ -312,7 +330,7 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInf
if (!status.isOK()) {
severe()
<< "Missing index name in dropIndexes operation on rollback, document: "
- << redact(doc.ownedObj);
+ << redact(oplogEntry.toBSON());
throw RSFatalException(
"Missing index name in dropIndexes operation on rollback.");
}
@@ -340,7 +358,7 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInf
if (!status.isOK()) {
severe()
<< "Missing index name in createIndexes operation on rollback, document: "
- << redact(doc.ownedObj);
+ << redact(oplogEntry.toBSON());
throw RSFatalException(
"Missing index name in createIndexes operation on rollback.");
}
@@ -487,7 +505,6 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInf
// }]
// }
// }
-
if (first.type() != Array) {
std::string message = str::stream()
<< "Expected applyOps argument to be an array; found " << redact(first);
@@ -507,7 +524,7 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInf
// needed for rollback that is contained within the applyOps, creating a nested
// call.
auto subStatus =
- updateFixUpInfoFromLocalOplogEntry(fixUpInfo, subopElement.Obj());
+ updateFixUpInfoFromLocalOplogEntry(fixUpInfo, subopElement.Obj(), true);
if (!subStatus.isOK()) {
return subStatus;
}
@@ -523,6 +540,10 @@ Status rollback_internal::updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInf
}
}
+ // If we are inserting/updating/deleting a document in the oplog entry, we will update
+ // the doc._id field when we actually insert the docID into the docsToRefetch set.
+ DocID doc = DocID(oplogEntry.raw, BSONElement(), *uuid);
+
doc._id = oplogEntry.getIdElement();
if (doc._id.eoo()) {
std::string message = str::stream() << "Cannot roll back op with no _id. ns: " << nss.ns();
@@ -860,7 +881,7 @@ Status _syncRollback(OperationContext* opCtx,
try {
auto processOperationForFixUp = [&how](const BSONObj& operation) {
- return updateFixUpInfoFromLocalOplogEntry(how, operation);
+ return updateFixUpInfoFromLocalOplogEntry(how, operation, false);
};
// Calls syncRollBackLocalOperations to run updateFixUpInfoFromLocalOplogEntry
diff --git a/src/mongo/db/repl/rs_rollback.h b/src/mongo/db/repl/rs_rollback.h
index d3722c39ed9..265e3104a62 100644
--- a/src/mongo/db/repl/rs_rollback.h
+++ b/src/mongo/db/repl/rs_rollback.h
@@ -356,7 +356,9 @@ private:
* rolling back node from after the common point. "ourObj" is the oplog document that needs
* to be reverted.
*/
-Status updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInfo, const BSONObj& ourObj);
+Status updateFixUpInfoFromLocalOplogEntry(FixUpInfo& fixUpInfo,
+ const BSONObj& ourObj,
+ bool isNestedApplyOpsCommand);
/**
* This function uses the FixUpInfo struct to undo all of the operations that occurred after the
diff --git a/src/mongo/db/repl/rs_rollback_test.cpp b/src/mongo/db/repl/rs_rollback_test.cpp
index b916ad93fdb..ad413f2db21 100644
--- a/src/mongo/db/repl/rs_rollback_test.cpp
+++ b/src/mongo/db/repl/rs_rollback_test.cpp
@@ -1378,11 +1378,12 @@ TEST_F(RSRollbackTest, RollbackDropDatabaseCommand) {
_replicationProcess.get()));
}
-BSONObj makeApplyOpsOplogEntry(Timestamp ts, UUID uuid, std::initializer_list<BSONObj> ops) {
+BSONObj makeApplyOpsOplogEntry(Timestamp ts, std::initializer_list<BSONObj> ops) {
+ // applyOps oplog entries are special and do not include a UUID field.
BSONObjBuilder entry;
entry << "ts" << ts << "h" << 1LL << "op"
<< "c"
- << "ui" << uuid << "ns"
+ << "ns"
<< "admin";
{
BSONObjBuilder cmd(entry.subobjStart("o"));
@@ -1435,7 +1436,6 @@ TEST_F(RSRollbackTest, RollbackApplyOpsCommand) {
std::make_pair(BSON("ts" << Timestamp(Seconds(1), 0) << "h" << 1LL), RecordId(1));
const auto applyOpsOperation =
std::make_pair(makeApplyOpsOplogEntry(Timestamp(Seconds(2), 0),
- uuid,
{BSON("op"
<< "u"
<< "ui"
@@ -1495,6 +1495,42 @@ TEST_F(RSRollbackTest, RollbackApplyOpsCommand) {
<< "ns"
<< "test.t"
<< "o"
+ << BSON("_id" << 4)),
+ // applyOps internal oplog entries are not required
+ // to have a timestamp and/or hash.
+ BSON("op"
+ << "i"
+ << "ui"
+ << uuid
+ << "ts"
+ << Timestamp(4, 1)
+ << "t"
+ << 1LL
+ << "ns"
+ << "test.t"
+ << "o"
+ << BSON("_id" << 4)),
+ BSON("op"
+ << "i"
+ << "ui"
+ << uuid
+ << "t"
+ << 1LL
+ << "h"
+ << 2LL
+ << "ns"
+ << "test.t"
+ << "o"
+ << BSON("_id" << 4)),
+ BSON("op"
+ << "i"
+ << "ui"
+ << uuid
+ << "t"
+ << 1LL
+ << "ns"
+ << "test.t"
+ << "o"
<< BSON("_id" << 4))}),
RecordId(2));
@@ -1861,7 +1897,7 @@ TEST(RSRollbackTest, LocalEntryWithoutNsIsFatal) {
<< "o"
<< BSON("_id" << 1 << "a" << 1));
FixUpInfo fui;
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, validOplogEntry));
+ ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, validOplogEntry, false));
const auto invalidOplogEntry = BSON("op"
<< "i"
<< "ui"
@@ -1876,8 +1912,9 @@ TEST(RSRollbackTest, LocalEntryWithoutNsIsFatal) {
<< ""
<< "o"
<< BSON("_id" << 1 << "a" << 1));
- ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(fui, invalidOplogEntry).transitional_ignore(),
- RSFatalException);
+ ASSERT_THROWS(
+ updateFixUpInfoFromLocalOplogEntry(fui, invalidOplogEntry, false).transitional_ignore(),
+ RSFatalException);
}
TEST(RSRollbackTest, LocalEntryWithoutOIsFatal) {
@@ -1896,7 +1933,7 @@ TEST(RSRollbackTest, LocalEntryWithoutOIsFatal) {
<< "o"
<< BSON("_id" << 1 << "a" << 1));
FixUpInfo fui;
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, validOplogEntry));
+ ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, validOplogEntry, false));
const auto invalidOplogEntry = BSON("op"
<< "i"
<< "ui"
@@ -1911,8 +1948,9 @@ TEST(RSRollbackTest, LocalEntryWithoutOIsFatal) {
<< "test.t"
<< "o"
<< BSONObj());
- ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(fui, invalidOplogEntry).transitional_ignore(),
- RSFatalException);
+ ASSERT_THROWS(
+ updateFixUpInfoFromLocalOplogEntry(fui, invalidOplogEntry, false).transitional_ignore(),
+ RSFatalException);
}
TEST(RSRollbackTest, LocalEntryWithoutO2IsFatal) {
@@ -1933,7 +1971,7 @@ TEST(RSRollbackTest, LocalEntryWithoutO2IsFatal) {
<< "o2"
<< BSON("_id" << 1));
FixUpInfo fui;
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, validOplogEntry));
+ ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, validOplogEntry, false));
const auto invalidOplogEntry = BSON("op"
<< "u"
<< "ui"
@@ -1950,8 +1988,9 @@ TEST(RSRollbackTest, LocalEntryWithoutO2IsFatal) {
<< BSON("_id" << 1 << "a" << 1)
<< "o2"
<< BSONObj());
- ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(fui, invalidOplogEntry).transitional_ignore(),
- RSFatalException);
+ ASSERT_THROWS(
+ updateFixUpInfoFromLocalOplogEntry(fui, invalidOplogEntry, false).transitional_ignore(),
+ RSFatalException);
}
DEATH_TEST_F(RSRollbackTest, LocalEntryWithTxnNumberWithoutSessionIdIsFatal, "invariant") {
@@ -1964,14 +2003,14 @@ DEATH_TEST_F(RSRollbackTest, LocalEntryWithTxnNumberWithoutSessionIdIsFatal, "in
<< "o"
<< BSON("_id" << 1 << "a" << 1));
FixUpInfo fui;
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, validOplogEntry));
+ ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, validOplogEntry, false));
const auto txnNumber = BSON("txnNumber" << 1LL);
const auto noSessionIdOrStmtId = validOplogEntry.addField(txnNumber.firstElement());
const auto stmtId = BSON("stmtId" << 1);
const auto noSessionId = noSessionIdOrStmtId.addField(stmtId.firstElement());
- ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(fui, noSessionId).transitional_ignore(),
+ ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(fui, noSessionId, false).transitional_ignore(),
RSFatalException);
}
@@ -1985,7 +2024,7 @@ DEATH_TEST_F(RSRollbackTest, LocalEntryWithTxnNumberWithoutStmtIdIsFatal, "invar
<< "o"
<< BSON("_id" << 1 << "a" << 1));
FixUpInfo fui;
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, validOplogEntry));
+ ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, validOplogEntry, false));
const auto txnNumber = BSON("txnNumber" << 1LL);
const auto noSessionIdOrStmtId = validOplogEntry.addField(txnNumber.firstElement());
@@ -1993,7 +2032,7 @@ DEATH_TEST_F(RSRollbackTest, LocalEntryWithTxnNumberWithoutStmtIdIsFatal, "invar
const auto lsid = makeLogicalSessionIdForTest();
const auto sessionId = BSON("lsid" << lsid.toBSON());
const auto noStmtId = noSessionIdOrStmtId.addField(sessionId.firstElement());
- ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(fui, noStmtId).transitional_ignore(),
+ ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(fui, noStmtId, false).transitional_ignore(),
RSFatalException);
}
@@ -2018,7 +2057,7 @@ TEST_F(RSRollbackTest, LocalEntryWithTxnNumberWithoutTxnTableUUIDIsFatal) {
<< lsid.toBSON());
FixUpInfo fui;
- ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(fui, entryWithTxnNumber).ignore(),
+ ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(fui, entryWithTxnNumber, false).ignore(),
RSFatalException);
}
@@ -2036,7 +2075,7 @@ TEST_F(RSRollbackTest, LocalEntryWithTxnNumberAddsTransactionTableDocToBeRefetch
<< "o"
<< BSON("_id" << 2 << "a" << 2));
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, entryWithoutTxnNumber));
+ ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, entryWithoutTxnNumber, false));
ASSERT_EQ(fui.docsToRefetch.size(), 1U);
// If txnNumber is present, and the transaction table exists and has a UUID, the session
@@ -2062,7 +2101,7 @@ TEST_F(RSRollbackTest, LocalEntryWithTxnNumberAddsTransactionTableDocToBeRefetch
UUID transactionTableUUID = UUID::gen();
fui.transactionTableUUID = transactionTableUUID;
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, entryWithTxnNumber));
+ ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, entryWithTxnNumber, false));
ASSERT_EQ(fui.docsToRefetch.size(), 3U);
auto expectedObj = BSON("_id" << lsid.toBSON());
@@ -2104,7 +2143,7 @@ TEST_F(RSRollbackTest, RollbackFailsIfTransactionDocumentRefetchReturnsDifferent
fui.rbid = 1;
// The FixUpInfo will have an extra doc to refetch: the corresponding transaction table entry.
- ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, entryWithTxnNumber));
+ ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, entryWithTxnNumber, false));
ASSERT_EQ(fui.docsToRefetch.size(), 2U);
{