diff options
author | Katherine Walker <katherine.walker@mongodb.com> | 2017-10-12 17:34:05 -0400 |
---|---|---|
committer | Katherine Walker <katherine.walker@mongodb.com> | 2017-10-20 10:53:27 -0400 |
commit | 999d491ed3b27d47dd0a8d9d86b4f7359f2dfae3 (patch) | |
tree | 4c55986caa1c445433c23e775dc880e648438ab4 /src/mongo | |
parent | b3d26b5f94636870f75b92582d6b7c31190a1295 (diff) | |
download | mongo-999d491ed3b27d47dd0a8d9d86b4f7359f2dfae3.tar.gz |
SERVER-31295 SERVER-31489 Allow applyOps to rollback without UUID
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/repl/rs_rollback.cpp | 47 | ||||
-rw-r--r-- | src/mongo/db/repl/rs_rollback.h | 4 | ||||
-rw-r--r-- | src/mongo/db/repl/rs_rollback_test.cpp | 79 |
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); { |