summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Shuvalov <andrew.shuvalov@mongodb.com>2021-05-27 20:42:38 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-05-27 21:14:02 +0000
commitfcfc73be325777faf3964289f599b63ef3131c3a (patch)
tree5c1c4afe934fca7437b5f77cb2721d2335d57a11
parent55208a316dd277c71138f3ed4d0338fb73e77b08 (diff)
downloadmongo-fcfc73be325777faf3964289f599b63ef3131c3a.tar.gz
SERVER-56376: [RRFaM] part 1, add preImage extraction from config.image_collection
-rw-r--r--src/mongo/db/SConscript3
-rw-r--r--src/mongo/db/ops/write_ops_retryability.cpp67
-rw-r--r--src/mongo/db/repl/oplog_entry.cpp13
-rw-r--r--src/mongo/db/repl/oplog_entry.h4
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);