diff options
author | Andrew Shuvalov <andrew.shuvalov@mongodb.com> | 2021-05-27 20:42:38 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-05-27 21:14:02 +0000 |
commit | fcfc73be325777faf3964289f599b63ef3131c3a (patch) | |
tree | 5c1c4afe934fca7437b5f77cb2721d2335d57a11 | |
parent | 55208a316dd277c71138f3ed4d0338fb73e77b08 (diff) | |
download | mongo-fcfc73be325777faf3964289f599b63ef3131c3a.tar.gz |
SERVER-56376: [RRFaM] part 1, add preImage extraction from config.image_collection
-rw-r--r-- | src/mongo/db/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops_retryability.cpp | 67 | ||||
-rw-r--r-- | src/mongo/db/repl/oplog_entry.cpp | 13 | ||||
-rw-r--r-- | src/mongo/db/repl/oplog_entry.h | 4 |
4 files changed, 78 insertions, 9 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 30058745a85..df8709ce746 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -2086,6 +2086,9 @@ env.Library( 'service_context', 'update/update_driver', ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/repl/image_collection_entry', + ], ) env.CppUnitTest( diff --git a/src/mongo/db/ops/write_ops_retryability.cpp b/src/mongo/db/ops/write_ops_retryability.cpp index 866385c73fe..619aed300f8 100644 --- a/src/mongo/db/ops/write_ops_retryability.cpp +++ b/src/mongo/db/ops/write_ops_retryability.cpp @@ -26,6 +26,7 @@ * exception statement from all source files in the program, then also delete * it in the license file. */ +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand #include "mongo/platform/basic.h" @@ -35,7 +36,9 @@ #include "mongo/db/namespace_string.h" #include "mongo/db/ops/find_and_modify_result.h" #include "mongo/db/query/find_and_modify_request.h" +#include "mongo/db/repl/image_collection_entry_gen.h" #include "mongo/logger/redaction.h" +#include "mongo/util/log.h" namespace mongo { namespace { @@ -50,6 +53,7 @@ void validateFindAndModifyRetryability(const FindAndModifyRequest& request, const repl::OplogEntry& oplogWithCorrectLinks) { auto opType = oplogEntry.getOpType(); auto ts = oplogEntry.getTimestamp(); + const bool needsRetryImage = oplogEntry.getNeedsRetryImage().is_initialized(); if (opType == repl::OpTypeEnum::kDelete) { uassert( @@ -62,7 +66,7 @@ void validateFindAndModifyRetryability(const FindAndModifyRequest& request, uassert(40607, str::stream() << "No pre-image available for findAndModify retry request:" << redact(request.toBSON({})), - oplogWithCorrectLinks.getPreImageOpTime()); + oplogWithCorrectLinks.getPreImageOpTime() || needsRetryImage); } else if (opType == repl::OpTypeEnum::kInsert) { uassert( 40608, @@ -86,14 +90,14 @@ void validateFindAndModifyRetryability(const FindAndModifyRequest& request, << " wants the document after update returned, but only before " "update document is stored, oplogTs: " << ts.toString() << ", oplog: " << redact(oplogEntry.toBSON()), - oplogWithCorrectLinks.getPostImageOpTime()); + oplogWithCorrectLinks.getPostImageOpTime() || needsRetryImage); } else { uassert(40612, str::stream() << "findAndModify retry request: " << redact(request.toBSON({})) << " wants the document before update returned, but only after " "update document is stored, oplogTs: " << ts.toString() << ", oplog: " << redact(oplogEntry.toBSON()), - oplogWithCorrectLinks.getPreImageOpTime()); + oplogWithCorrectLinks.getPreImageOpTime() || needsRetryImage); } } } @@ -103,11 +107,64 @@ void validateFindAndModifyRetryability(const FindAndModifyRequest& request, * oplog. */ BSONObj extractPreOrPostImage(OperationContext* opCtx, const repl::OplogEntry& oplog) { - invariant(oplog.getPreImageOpTime() || oplog.getPostImageOpTime()); + invariant(oplog.getPreImageOpTime() || oplog.getPostImageOpTime() || + oplog.getNeedsRetryImage()); + DBDirectClient client(opCtx); + if (oplog.getNeedsRetryImage()) { + // Extract image from side collection. + const LogicalSessionId sessionId = oplog.getSessionId().get(); + const auto sessionIdBson = sessionId.toBSON(); + TxnNumber txnNumber = oplog.getTxnNumber().get(); + Timestamp ts = oplog.getTimestamp(); + const auto query = BSON("_id" << sessionIdBson); + BSONObj imageDoc = client.findOne(NamespaceString::kConfigImagesNamespace.ns(), query); + if (imageDoc.isEmpty()) { + warning() << "Image lookup for a retryable findAndModify was not found for sessionId: " + << sessionIdBson << " txnNumber " << txnNumber << " timestamp " << ts; + uasserted( + 5637601, + str::stream() + << "image collection no longer contains the complete write history of this " + "transaction, record with sessionId: " + << sessionIdBson << " cannot be found"); + } + + auto entry = + repl::ImageEntry::parse(IDLParserErrorContext("ImageEntryForRequest"), imageDoc); + if (entry.getInvalidated()) { + // This case is expected when a node could not correctly compute a retry image due + // to data inconsistency while in initial sync. + uasserted(ErrorCodes::IncompleteTransactionHistory, + str::stream() << "Incomplete transaction history for sessionId: " + << sessionIdBson << " txnNumber: " << txnNumber); + } + + if (entry.getTs() != oplog.getTimestamp() || entry.getTxnNumber() != oplog.getTxnNumber()) { + // We found a corresponding image document, but the timestamp and transaction number + // associated with the session record did not match the expected values from the oplog + // entry. + + // Otherwise, it's unclear what went wrong. + warning() << "Image lookup for a retryable findAndModify was unable to be verified for " + "sessionId: " + << sessionIdBson << " txnNumberRequested: " << txnNumber + << " timestampRequested " << ts << " txnNumberFound " << entry.getTxnNumber() + << " timestampFound " << entry.getTs(); + uasserted( + 5637602, + str::stream() + << "image collection no longer contains the complete write history of this " + "transaction, record with sessionId: " + << sessionIdBson << " cannot be found"); + } + + return entry.getImage(); + } + + // Extract image from oplog. auto opTime = oplog.getPreImageOpTime() ? oplog.getPreImageOpTime().value() : oplog.getPostImageOpTime().value(); - DBDirectClient client(opCtx); auto oplogDoc = client.findOne(NamespaceString::kRsOplogNamespace.ns(), opTime.asQuery(), nullptr, diff --git a/src/mongo/db/repl/oplog_entry.cpp b/src/mongo/db/repl/oplog_entry.cpp index 81a6c03d777..0ee07fb03ee 100644 --- a/src/mongo/db/repl/oplog_entry.cpp +++ b/src/mongo/db/repl/oplog_entry.cpp @@ -104,7 +104,8 @@ BSONObj makeOplogEntryDoc(OpTime opTime, const boost::optional<StmtId>& statementId, const boost::optional<OpTime>& prevWriteOpTimeInTransaction, const boost::optional<OpTime>& preImageOpTime, - const boost::optional<OpTime>& postImageOpTime) { + const boost::optional<OpTime>& postImageOpTime, + const boost::optional<repl::RetryImageEnum>& needsRetryImage) { BSONObjBuilder builder; sessionInfo.serialize(&builder); builder.append(OplogEntryBase::kTimestampFieldName, opTime.getTimestamp()); @@ -147,6 +148,10 @@ BSONObj makeOplogEntryDoc(OpTime opTime, const BSONObj localObject = postImageOpTime.get().toBSON(); builder.append(OplogEntryBase::kPostImageOpTimeFieldName, localObject); } + if (needsRetryImage) { + builder.append(OplogEntryBase::kNeedsRetryImageFieldName, + RetryImage_serializer(needsRetryImage.get())); + } return builder.obj(); } @@ -230,7 +235,8 @@ OplogEntry::OplogEntry(OpTime opTime, const boost::optional<StmtId>& statementId, const boost::optional<OpTime>& prevWriteOpTimeInTransaction, const boost::optional<OpTime>& preImageOpTime, - const boost::optional<OpTime>& postImageOpTime) + const boost::optional<OpTime>& postImageOpTime, + const boost::optional<repl::RetryImageEnum>& needsRetryImage) : OplogEntry(makeOplogEntryDoc(opTime, hash, opType, @@ -246,7 +252,8 @@ OplogEntry::OplogEntry(OpTime opTime, statementId, prevWriteOpTimeInTransaction, preImageOpTime, - postImageOpTime)) {} + postImageOpTime, + needsRetryImage)) {} bool OplogEntry::isCommand() const { return getOpType() == OpTypeEnum::kCommand; diff --git a/src/mongo/db/repl/oplog_entry.h b/src/mongo/db/repl/oplog_entry.h index 5deaf149a73..7462d70694e 100644 --- a/src/mongo/db/repl/oplog_entry.h +++ b/src/mongo/db/repl/oplog_entry.h @@ -103,6 +103,7 @@ public: using OplogEntryBase::getDurableReplOperation; using OplogEntryBase::getFromMigrate; using OplogEntryBase::getHash; + using OplogEntryBase::getNeedsRetryImage; using OplogEntryBase::getNss; using OplogEntryBase::getObject; using OplogEntryBase::getObject2; @@ -178,7 +179,8 @@ public: const boost::optional<StmtId>& statementId, const boost::optional<OpTime>& prevWriteOpTimeInTransaction, const boost::optional<OpTime>& preImageOpTime, - const boost::optional<OpTime>& postImageOpTime); + const boost::optional<OpTime>& postImageOpTime, + const boost::optional<repl::RetryImageEnum>& needsRetryImage = boost::none); // DEPRECATED: This constructor can throw. Use static parse method instead. explicit OplogEntry(BSONObj raw); |