diff options
author | Matthew Russotto <matthew.russotto@10gen.com> | 2018-01-08 17:06:56 -0500 |
---|---|---|
committer | Matthew Russotto <matthew.russotto@10gen.com> | 2018-01-08 17:07:32 -0500 |
commit | 8b766fdc6c896ba0a4db30d7b9d2f050b464db22 (patch) | |
tree | 107cf6c037074c53bdddb61423c4c6cb352b3003 /src/mongo/db | |
parent | 27ccd4a3bf1150a1bf4356de8a91e4dff64e0762 (diff) | |
download | mongo-8b766fdc6c896ba0a4db30d7b9d2f050b464db22.tar.gz |
SERVER-32162 Remove non-atomic (push-based replication) support from doTxn.
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/commands/apply_ops_cmd.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/commands/do_txn_cmd.cpp | 150 | ||||
-rw-r--r-- | src/mongo/db/commands/oplog_application_checks.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/commands/oplog_application_checks.h | 5 | ||||
-rw-r--r-- | src/mongo/db/repl/do_txn.cpp | 250 | ||||
-rw-r--r-- | src/mongo/db/repl/do_txn.h | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/do_txn_test.cpp | 65 |
7 files changed, 98 insertions, 391 deletions
diff --git a/src/mongo/db/commands/apply_ops_cmd.cpp b/src/mongo/db/commands/apply_ops_cmd.cpp index 4d51368a9e4..5822cf9b61b 100644 --- a/src/mongo/db/commands/apply_ops_cmd.cpp +++ b/src/mongo/db/commands/apply_ops_cmd.cpp @@ -219,8 +219,7 @@ public: const std::string& dbname, const BSONObj& cmdObj) override { OplogApplicationValidity validity = validateApplyOpsCommand(cmdObj); - return OplogApplicationChecks::checkAuthForCommand( - opCtx, dbname, cmdObj, validity, OplogApplicationCommand::kApplyOpsCmd); + return OplogApplicationChecks::checkAuthForCommand(opCtx, dbname, cmdObj, validity); } bool run(OperationContext* opCtx, diff --git a/src/mongo/db/commands/do_txn_cmd.cpp b/src/mongo/db/commands/do_txn_cmd.cpp index 3ed00c1e736..58965f0283f 100644 --- a/src/mongo/db/commands/do_txn_cmd.cpp +++ b/src/mongo/db/commands/do_txn_cmd.cpp @@ -49,6 +49,7 @@ #include "mongo/db/operation_context.h" #include "mongo/db/repl/do_txn.h" #include "mongo/db/repl/oplog.h" +#include "mongo/db/repl/oplog_entry_gen.h" #include "mongo/db/repl/repl_client_info.h" #include "mongo/db/repl/replication_coordinator_global.h" #include "mongo/db/service_context.h" @@ -59,35 +60,12 @@ namespace mongo { namespace { -bool checkCOperationType(const BSONObj& opObj, const StringData opName) { - BSONElement opTypeElem = opObj["op"]; - checkBSONType(BSONType::String, opTypeElem); - const StringData opType = opTypeElem.checkAndGetStringData(); - - if (opType == "c"_sd) { - BSONElement oElem = opObj["o"]; - checkBSONType(BSONType::Object, oElem); - BSONObj o = oElem.Obj(); - - if (o.firstElement().fieldNameStringData() == opName) { - return true; - } - } - return false; -}; - /** - * Returns kNeedsSuperuser, if the provided doTxn command contains - * an empty doTxn command or createCollection/renameCollection commands are mixed in doTxn - * batch. Returns kNeedForceAndUseUUID if an operation contains a UUID, and will create a collection - * with the user-specified UUID. Returns - * kNeedsUseUUID if the operation contains a UUID. Returns kOk if no conditions - * which must be specially handled are detected. May throw exceptions if the input is malformed. + * Returns kNeedsUseUUID if the operation contains a UUID. Returns kOk if no conditions + * which must be specially handled are detected. Throws an exception if the input is malformed or + * if a command is in the list of ops. */ -OplogApplicationValidity validateDoTxnCommand(const BSONObj& cmdObj) { - const size_t maxDoTxnDepth = 10; - std::stack<std::pair<size_t, BSONObj>> toCheck; - +OplogApplicationValidity validateDoTxnCommand(const BSONObj& doTxnObj) { auto operationContainsUUID = [](const BSONObj& opObj) { auto anyTopLevelElementIsUUID = [](const BSONObj& opObj) { for (const BSONElement opElement : opObj) { @@ -121,73 +99,31 @@ OplogApplicationValidity validateDoTxnCommand(const BSONObj& cmdObj) { OplogApplicationValidity ret = OplogApplicationValidity::kOk; - // Insert the top level doTxn command into the stack. - toCheck.emplace(std::make_pair(0, cmdObj)); - - while (!toCheck.empty()) { - size_t depth; - BSONObj doTxnObj; - std::tie(depth, doTxnObj) = toCheck.top(); - toCheck.pop(); - - checkBSONType(BSONType::Array, doTxnObj.firstElement()); - // Check if the doTxn command is empty. This is probably not something that should - // happen, so require a superuser to do this. - if (doTxnObj.firstElement().Array().empty()) { - return OplogApplicationValidity::kNeedsSuperuser; - } - - // createCollection and renameCollection are only allowed to be applied - // individually. Ensure there is no create/renameCollection in a batch - // of size greater than 1. - if (doTxnObj.firstElement().Array().size() > 1) { - for (const BSONElement& e : doTxnObj.firstElement().Array()) { - checkBSONType(BSONType::Object, e); - auto oplogEntry = e.Obj(); - if (checkCOperationType(oplogEntry, "create"_sd) || - checkCOperationType(oplogEntry, "renameCollection"_sd)) { - return OplogApplicationValidity::kNeedsSuperuser; - } - } - } + checkBSONType(BSONType::Array, doTxnObj.firstElement()); + // Check if the doTxn command is empty. There's no good reason for an empty transaction, + // so reject it. + uassert(ErrorCodes::InvalidOptions, + "An empty doTxn command is not allowed.", + !doTxnObj.firstElement().Array().empty()); - // For each doTxn command, iterate the ops. - for (BSONElement element : doTxnObj.firstElement().Array()) { - checkBSONType(BSONType::Object, element); - BSONObj opObj = element.Obj(); + // Iterate the ops. + for (BSONElement element : doTxnObj.firstElement().Array()) { + checkBSONType(BSONType::Object, element); + BSONObj opObj = element.Obj(); - bool opHasUUIDs = operationContainsUUID(opObj); - - if (serverGlobalParams.featureCompatibility.getVersion() == - ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34) { - uassert(ErrorCodes::OplogOperationUnsupported, - "doTxn with UUID requires upgrading to FeatureCompatibilityVersion 3.6", - !opHasUUIDs); - } - - // If the op uses any UUIDs at all then the user must possess extra privileges. - if (opHasUUIDs && ret == OplogApplicationValidity::kOk) - ret = OplogApplicationValidity::kNeedsUseUUID; - if (opHasUUIDs && checkCOperationType(opObj, "create"_sd)) { - // If the op is 'c' and forces the server to ingest a collection - // with a specific, user defined UUID. - ret = OplogApplicationValidity::kNeedsForceAndUseUUID; - } - - // If the op contains a nested doTxn... - if (checkCOperationType(opObj, "doTxn"_sd)) { - // And we've recursed too far, then bail out. - uassert(ErrorCodes::FailedToParse, "Too many nested doTxn", depth < maxDoTxnDepth); - - // Otherwise, if the op contains an doTxn, but we haven't recursed too far: - // extract the doTxn command, and insert it into the stack. - checkBSONType(BSONType::Object, opObj["o"]); - BSONObj oObj = opObj["o"].Obj(); - toCheck.emplace(std::make_pair(depth + 1, std::move(oObj))); - } + // If the op is a command, it's illegal. + BSONElement opTypeElem = opObj["op"]; + const StringData opTypeStr = opTypeElem.checkAndGetStringData(); + auto opType = repl::OpType_parse(IDLParserErrorContext("validateDoTxnCommand"), opTypeStr); + uassert(ErrorCodes::InvalidOptions, + "Commands cannot be applied via doTxn.", + opType != repl::OpTypeEnum::kCommand); + + // If the op uses any UUIDs at all then the user must possess extra privileges. + if (operationContainsUUID(opObj)) { + ret = OplogApplicationValidity::kNeedsUseUUID; } } - return ret; } @@ -212,8 +148,7 @@ public: const std::string& dbname, const BSONObj& cmdObj) override { OplogApplicationValidity validity = validateDoTxnCommand(cmdObj); - return OplogApplicationChecks::checkAuthForCommand( - opCtx, dbname, cmdObj, validity, OplogApplicationCommand::kDoTxnCmd); + return OplogApplicationChecks::checkAuthForCommand(opCtx, dbname, cmdObj, validity); } bool run(OperationContext* opCtx, @@ -241,36 +176,7 @@ public: // was acknowledged. To fix this, we should wait for replication of the node’s last applied // OpTime if the last write operation was a no-op write. - // We set the OplogApplication::Mode argument based on the mode argument given in the - // command object. If no mode is given, default to the 'kApplyOpsCmd' mode. - repl::OplogApplication::Mode oplogApplicationMode = - repl::OplogApplication::Mode::kApplyOpsCmd; // the default mode. - std::string oplogApplicationModeString; - status = bsonExtractStringField( - cmdObj, DoTxn::kOplogApplicationModeFieldName, &oplogApplicationModeString); - - if (status.isOK()) { - auto modeSW = repl::OplogApplication::parseMode(oplogApplicationModeString); - if (!modeSW.isOK()) { - // Unable to parse the mode argument. - return appendCommandStatus( - result, - modeSW.getStatus().withContext(str::stream() << "Could not parse " + - DoTxn::kOplogApplicationModeFieldName)); - } - oplogApplicationMode = modeSW.getValue(); - } else if (status != ErrorCodes::NoSuchKey) { - // NoSuchKey means the user did not supply a mode. - return appendCommandStatus(result, - Status(status.code(), - str::stream() << "Could not parse out " - << DoTxn::kOplogApplicationModeFieldName - << ": " - << status.reason())); - } - - auto doTxnStatus = appendCommandStatus( - result, doTxn(opCtx, dbname, cmdObj, oplogApplicationMode, &result)); + auto doTxnStatus = appendCommandStatus(result, doTxn(opCtx, dbname, cmdObj, &result)); return doTxnStatus; } diff --git a/src/mongo/db/commands/oplog_application_checks.cpp b/src/mongo/db/commands/oplog_application_checks.cpp index 39b8b41c2f6..1fbbe738a33 100644 --- a/src/mongo/db/commands/oplog_application_checks.cpp +++ b/src/mongo/db/commands/oplog_application_checks.cpp @@ -44,7 +44,6 @@ Status OplogApplicationChecks::checkOperationAuthorization(OperationContext* opC const std::string& dbname, const BSONObj& oplogEntry, AuthorizationSession* authSession, - OplogApplicationCommand command, bool alwaysUpsert) { BSONElement opTypeElem = oplogEntry["op"]; checkBSONType(BSONType::String, opTypeElem); @@ -76,9 +75,6 @@ Status OplogApplicationChecks::checkOperationAuthorization(OperationContext* opC BSONObj o = oElem.Obj(); if (opType == "c"_sd) { - if (command == OplogApplicationCommand::kDoTxnCmd) { - return Status(ErrorCodes::IllegalOperation, "Commands cannot be applied via doTxn."); - } StringData commandName = o.firstElement().fieldNameStringData(); Command* commandInOplogEntry = Command::findCommand(commandName); if (!commandInOplogEntry) { @@ -195,8 +191,7 @@ Status OplogApplicationChecks::checkOperation(const BSONElement& e) { Status OplogApplicationChecks::checkAuthForCommand(OperationContext* opCtx, const std::string& dbname, const BSONObj& cmdObj, - OplogApplicationValidity validity, - OplogApplicationCommand command) { + OplogApplicationValidity validity) { AuthorizationSession* authSession = AuthorizationSession::get(opCtx->getClient()); if (validity == OplogApplicationValidity::kNeedsSuperuser) { std::vector<Privilege> universalPrivileges; @@ -234,12 +229,7 @@ Status OplogApplicationChecks::checkAuthForCommand(OperationContext* opCtx, for (const BSONElement& e : cmdObj.firstElement().Array()) { checkBSONType(BSONType::Object, e); Status status = OplogApplicationChecks::checkOperationAuthorization( - opCtx, - dbname, - e.Obj(), - authSession, - OplogApplicationCommand::kApplyOpsCmd, - alwaysUpsert); + opCtx, dbname, e.Obj(), authSession, alwaysUpsert); if (!status.isOK()) { return status; } diff --git a/src/mongo/db/commands/oplog_application_checks.h b/src/mongo/db/commands/oplog_application_checks.h index ec63c6ab505..3dc1d201e1c 100644 --- a/src/mongo/db/commands/oplog_application_checks.h +++ b/src/mongo/db/commands/oplog_application_checks.h @@ -52,7 +52,6 @@ class OperationContext; // // Only kOk and kNeedsUseUUID are valid for 'doTxn'. All are valid for 'applyOps'. enum class OplogApplicationValidity { kOk, kNeedsUseUUID, kNeedsForceAndUseUUID, kNeedsSuperuser }; -enum class OplogApplicationCommand { kApplyOpsCmd, kDoTxnCmd }; // OplogApplicationChecks contains helper functions for checking the applyOps and doTxn commands. class OplogApplicationChecks { @@ -63,8 +62,7 @@ public: static Status checkAuthForCommand(OperationContext* opCtx, const std::string& dbname, const BSONObj& cmdObj, - OplogApplicationValidity validity, - OplogApplicationCommand command); + OplogApplicationValidity validity); /** * Checks that 'opsElement' is an array and all elements of the array are valid operations. @@ -82,7 +80,6 @@ private: const std::string& dbname, const BSONObj& oplogEntry, AuthorizationSession* authSession, - OplogApplicationCommand command, bool alwaysUpsert); /** * Returns OK if 'e' contains a valid operation. diff --git a/src/mongo/db/repl/do_txn.cpp b/src/mongo/db/repl/do_txn.cpp index 1bd9d13d8a1..44de9e8590c 100644 --- a/src/mongo/db/repl/do_txn.cpp +++ b/src/mongo/db/repl/do_txn.cpp @@ -56,7 +56,6 @@ namespace mongo { constexpr StringData DoTxn::kPreconditionFieldName; -constexpr StringData DoTxn::kOplogApplicationModeFieldName; namespace { @@ -84,7 +83,6 @@ bool _areOpsCrudOnly(const BSONObj& doTxnCmd) { // Only consider CRUD operations. switch (*opType) { case 'd': - case 'n': case 'u': break; case 'i': @@ -102,7 +100,6 @@ bool _areOpsCrudOnly(const BSONObj& doTxnCmd) { Status _doTxn(OperationContext* opCtx, const std::string& dbName, const BSONObj& doTxnCmd, - repl::OplogApplication::Mode oplogApplicationMode, BSONObjBuilder* result, int* numApplied, BSONArrayBuilder* opsBuilder) { @@ -113,168 +110,80 @@ Status _doTxn(OperationContext* opCtx, BSONObjIterator i(ops); BSONArrayBuilder ab; - const bool alwaysUpsert = - doTxnCmd.hasField("alwaysUpsert") ? doTxnCmd["alwaysUpsert"].trueValue() : true; - const bool haveWrappingWUOW = opCtx->lockState()->inAWriteUnitOfWork(); + invariant(opCtx->lockState()->inAWriteUnitOfWork()); // Apply each op in the given 'doTxn' command object. while (i.more()) { BSONElement e = i.next(); const BSONObj& opObj = e.Obj(); - // Ignore 'n' operations. - const char* opType = opObj["op"].valuestrsafe(); - if (*opType == 'n') - continue; - const NamespaceString nss(opObj["ns"].String()); // Need to check this here, or OldClientContext may fail an invariant. - if (*opType != 'c' && !nss.isValid()) + if (!nss.isValid()) return {ErrorCodes::InvalidNamespace, "invalid ns: " + nss.ns()}; Status status(ErrorCodes::InternalError, ""); - if (haveWrappingWUOW) { - invariant(opCtx->lockState()->isW()); - invariant(*opType != 'c'); - - auto db = dbHolder().get(opCtx, nss.ns()); - if (!db) { - // Retry in non-atomic mode, since MMAP cannot implicitly create a new database - // within an active WriteUnitOfWork. - uasserted(ErrorCodes::AtomicityFailure, - "cannot create a database in atomic doTxn mode; will retry without " - "atomicity"); - } - - // When processing an update on a non-existent collection, applyOperation_inlock() - // returns UpdateOperationFailed on updates and allows the collection to be - // implicitly created on upserts. We detect both cases here and fail early with - // NamespaceNotFound. - // Additionally for inserts, we fail early on non-existent collections. - auto collection = db->getCollection(opCtx, nss); - if (!collection && !nss.isSystemDotIndexes() && (*opType == 'i' || *opType == 'u')) { - uasserted( - ErrorCodes::AtomicityFailure, - str::stream() - << "cannot apply insert or update operation on a non-existent namespace " - << nss.ns() - << " in atomic doTxn mode: " - << redact(opObj)); - } + invariant(opCtx->lockState()->isW()); - // Cannot specify timestamp values in an atomic doTxn. - if (opObj.hasField("ts")) { - uasserted(ErrorCodes::AtomicityFailure, - "cannot apply an op with a timestamp in atomic doTxn mode; " - "will retry without atomicity"); - } + auto db = dbHolder().get(opCtx, nss.ns()); + if (!db) { + uasserted(ErrorCodes::NamespaceNotFound, + str::stream() << "cannot apply insert, delete, or update operation on a " + "non-existent namespace " + << nss.ns() + << ": " + << mongo::redact(opObj)); + } - OldClientContext ctx(opCtx, nss.ns()); - - status = repl::applyOperation_inlock( - opCtx, ctx.db(), opObj, alwaysUpsert, oplogApplicationMode); - if (!status.isOK()) - return status; - - // Append completed op, including UUID if available, to 'opsBuilder'. - if (opsBuilder) { - if (opObj.hasField("ui") || nss.isSystemDotIndexes() || - !(collection && collection->uuid())) { - // No changes needed to operation document. - opsBuilder->append(opObj); - } else { - // Operation document has no "ui" field and collection has a UUID. - auto uuid = collection->uuid(); - BSONObjBuilder opBuilder; - opBuilder.appendElements(opObj); - uuid->appendToBuilder(&opBuilder, "ui"); - opsBuilder->append(opBuilder.obj()); - } - } - } else { - try { - status = writeConflictRetry( - opCtx, - "doTxn", - nss.ns(), - [opCtx, nss, opObj, opType, alwaysUpsert, oplogApplicationMode] { - if (*opType == 'c') { - invariant(opCtx->lockState()->isW()); - return repl::applyCommand_inlock(opCtx, opObj, oplogApplicationMode); - } + // When processing an update on a non-existent collection, applyOperation_inlock() + // returns UpdateOperationFailed on updates and allows the collection to be + // implicitly created on upserts. We detect both cases here and fail early with + // NamespaceNotFound. + // Additionally for inserts, we fail early on non-existent collections. + auto collection = db->getCollection(opCtx, nss); + if (!collection && db->getViewCatalog()->lookup(opCtx, nss.ns())) { + uasserted(ErrorCodes::CommandNotSupportedOnView, + str::stream() << "doTxn not supported on a view: " << redact(opObj)); + } + if (!collection) { + uasserted(ErrorCodes::NamespaceNotFound, + str::stream() << "cannot apply operation on a non-existent namespace " + << nss.ns() + << " with doTxn: " + << redact(opObj)); + } - AutoGetCollection autoColl(opCtx, nss, MODE_IX); - if (!autoColl.getCollection() && !nss.isSystemDotIndexes()) { - // For idempotency reasons, return success on delete operations. - if (*opType == 'd') { - return Status::OK(); - } - uasserted(ErrorCodes::NamespaceNotFound, - str::stream() - << "cannot apply insert or update operation on a " - "non-existent namespace " - << nss.ns() - << ": " - << mongo::redact(opObj)); - } + // Cannot specify timestamp values in an atomic doTxn. + if (opObj.hasField("ts")) { + uasserted(ErrorCodes::AtomicityFailure, + "cannot apply an op with a timestamp with doTxn; "); + } - OldClientContext ctx(opCtx, nss.ns()); + OldClientContext ctx(opCtx, nss.ns()); - if (!nss.isSystemDotIndexes()) { - return repl::applyOperation_inlock( - opCtx, ctx.db(), opObj, alwaysUpsert, oplogApplicationMode); - } + // Setting alwaysUpsert to true makes sense only during oplog replay, and doTxn commands + // should not be executed during oplog replay. + const bool alwaysUpsert = false; + status = repl::applyOperation_inlock( + opCtx, ctx.db(), opObj, alwaysUpsert, repl::OplogApplication::Mode::kApplyOpsCmd); + if (!status.isOK()) + return status; - auto fieldO = opObj["o"]; - BSONObj indexSpec; - NamespaceString indexNss; - std::tie(indexSpec, indexNss) = - repl::prepForApplyOpsIndexInsert(fieldO, opObj, nss); - if (!indexSpec["collation"]) { - // If the index spec does not include a collation, explicitly specify - // the simple collation, so the index does not inherit the collection - // default collation. - auto indexVersion = indexSpec["v"]; - // The index version is populated by prepForApplyOpsIndexInsert(). - invariant(indexVersion); - if (indexVersion.isNumber() && - (indexVersion.numberInt() >= - static_cast<int>(IndexDescriptor::IndexVersion::kV2))) { - BSONObjBuilder bob; - bob.append("collation", CollationSpec::kSimpleSpec); - bob.appendElements(indexSpec); - indexSpec = bob.obj(); - } - } - BSONObjBuilder command; - command.append("createIndexes", indexNss.coll()); - { - BSONArrayBuilder indexes(command.subarrayStart("indexes")); - indexes.append(indexSpec); - indexes.doneFast(); - } - const BSONObj commandObj = command.done(); - - DBDirectClient client(opCtx); - BSONObj infoObj; - client.runCommand(nss.db().toString(), commandObj, infoObj); - - // Uassert to stop doTxn only when building indexes, but not for CRUD - // ops. - uassertStatusOK(getStatusFromCommandResult(infoObj)); - - return Status::OK(); - }); - } catch (const DBException& ex) { - ab.append(false); - result->append("applied", ++(*numApplied)); - result->append("code", ex.code()); - result->append("codeName", ErrorCodes::errorString(ex.code())); - result->append("errmsg", ex.what()); - result->append("results", ab.arr()); - return Status(ErrorCodes::UnknownError, ex.what()); + // Append completed op, including UUID if available, to 'opsBuilder'. + if (opsBuilder) { + if (opObj.hasField("ui") || nss.isSystemDotIndexes() || + !(collection && collection->uuid())) { + // No changes needed to operation document. + opsBuilder->append(opObj); + } else { + // Operation document has no "ui" field and collection has a UUID. + auto uuid = collection->uuid(); + BSONObjBuilder opBuilder; + opBuilder.appendElements(opObj); + uuid->appendToBuilder(&opBuilder, "ui"); + opsBuilder->append(opBuilder.obj()); } } @@ -364,34 +273,12 @@ Status _checkPrecondition(OperationContext* opCtx, Status doTxn(OperationContext* opCtx, const std::string& dbName, const BSONObj& doTxnCmd, - repl::OplogApplication::Mode oplogApplicationMode, BSONObjBuilder* result) { - bool allowAtomic = false; - uassertStatusOK( - bsonExtractBooleanFieldWithDefault(doTxnCmd, "allowAtomic", true, &allowAtomic)); - auto areOpsCrudOnly = _areOpsCrudOnly(doTxnCmd); - auto isAtomic = allowAtomic && areOpsCrudOnly; + uassert( + ErrorCodes::InvalidOptions, "doTxn supports only CRUD opts.", _areOpsCrudOnly(doTxnCmd)); auto hasPrecondition = _hasPrecondition(doTxnCmd); - if (hasPrecondition) { - uassert(ErrorCodes::InvalidOptions, - "Cannot use preCondition with {allowAtomic: false}.", - allowAtomic); - uassert(ErrorCodes::InvalidOptions, - "Cannot use preCondition when operations include commands.", - areOpsCrudOnly); - } - - boost::optional<Lock::GlobalWrite> globalWriteLock; - boost::optional<Lock::DBLock> dbWriteLock; - - // There's only one case where we are allowed to take the database lock instead of the global - // lock - no preconditions; only CRUD ops; and non-atomic mode. - if (!hasPrecondition && areOpsCrudOnly && !allowAtomic) { - dbWriteLock.emplace(opCtx, dbName, MODE_IX); - } else { - globalWriteLock.emplace(opCtx); - } + Lock::GlobalWrite globalWriteLock(opCtx); auto replCoord = repl::ReplicationCoordinator::get(opCtx); bool userInitiatedWritesAndNotPrimary = @@ -402,7 +289,6 @@ Status doTxn(OperationContext* opCtx, str::stream() << "Not primary while applying ops to database " << dbName); if (hasPrecondition) { - invariant(isAtomic); auto status = _checkPrecondition(opCtx, doTxnCmd, result); if (!status.isOK()) { return status; @@ -410,12 +296,6 @@ Status doTxn(OperationContext* opCtx, } int numApplied = 0; - if (!isAtomic) { - return _doTxn(opCtx, dbName, doTxnCmd, oplogApplicationMode, result, &numApplied, nullptr); - } - - // Perform write ops atomically - invariant(globalWriteLock); try { writeConflictRetry(opCtx, "doTxn", dbName, [&] { @@ -430,13 +310,8 @@ Status doTxn(OperationContext* opCtx, { // Suppress replication for atomic operations until end of doTxn. repl::UnreplicatedWritesBlock uwb(opCtx); - uassertStatusOK(_doTxn(opCtx, - dbName, - doTxnCmd, - oplogApplicationMode, - &intermediateResult, - &numApplied, - opsBuilder.get())); + uassertStatusOK(_doTxn( + opCtx, dbName, doTxnCmd, &intermediateResult, &numApplied, opsBuilder.get())); } // Generate oplog entry for all atomic ops collectively. if (opCtx->writesAreReplicated()) { @@ -475,11 +350,6 @@ Status doTxn(OperationContext* opCtx, result->appendElements(intermediateResult.obj()); }); } catch (const DBException& ex) { - if (ex.code() == ErrorCodes::AtomicityFailure) { - // Retry in non-atomic mode. - return _doTxn( - opCtx, dbName, doTxnCmd, oplogApplicationMode, result, &numApplied, nullptr); - } BSONArrayBuilder ab; ++numApplied; for (int j = 0; j < numApplied; j++) diff --git a/src/mongo/db/repl/do_txn.h b/src/mongo/db/repl/do_txn.h index 022474bcbf1..0b6d6c1182c 100644 --- a/src/mongo/db/repl/do_txn.h +++ b/src/mongo/db/repl/do_txn.h @@ -36,7 +36,6 @@ class OperationContext; class DoTxn { public: static constexpr StringData kPreconditionFieldName = "preCondition"_sd; - static constexpr StringData kOplogApplicationModeFieldName = "oplogApplicationMode"_sd; }; /** @@ -52,7 +51,6 @@ public: Status doTxn(OperationContext* opCtx, const std::string& dbName, const BSONObj& doTxnCmd, - repl::OplogApplication::Mode oplogApplicationMode, BSONObjBuilder* result); } // namespace mongo diff --git a/src/mongo/db/repl/do_txn_test.cpp b/src/mongo/db/repl/do_txn_test.cpp index cb50d3a1d57..de0de6ab4b9 100644 --- a/src/mongo/db/repl/do_txn_test.cpp +++ b/src/mongo/db/repl/do_txn_test.cpp @@ -136,11 +136,10 @@ Status getStatusFromDoTxnResult(const BSONObj& result) { TEST_F(DoTxnTest, AtomicDoTxnWithNoOpsReturnsSuccess) { auto opCtx = cc().makeOperationContext(); - auto mode = OplogApplication::Mode::kApplyOpsCmd; BSONObjBuilder resultBuilder; auto cmdObj = BSON("doTxn" << BSONArray()); auto expectedCmdObj = BSON("applyOps" << BSONArray()); - ASSERT_OK(doTxn(opCtx.get(), "test", cmdObj, mode, &resultBuilder)); + ASSERT_OK(doTxn(opCtx.get(), "test", cmdObj, &resultBuilder)); ASSERT_BSONOBJ_EQ(expectedCmdObj, _opObserver->onApplyOpsCmdObj); } @@ -185,13 +184,11 @@ BSONObj makeApplyOpsWithInsertOperation(const NamespaceString& nss, TEST_F(DoTxnTest, AtomicDoTxnInsertIntoNonexistentCollectionReturnsNamespaceNotFoundInResult) { auto opCtx = cc().makeOperationContext(); - auto mode = OplogApplication::Mode::kApplyOpsCmd; NamespaceString nss("test.t"); auto documentToInsert = BSON("_id" << 0); auto cmdObj = makeDoTxnWithInsertOperation(nss, boost::none, documentToInsert); BSONObjBuilder resultBuilder; - ASSERT_EQUALS(ErrorCodes::UnknownError, - doTxn(opCtx.get(), "test", cmdObj, mode, &resultBuilder)); + ASSERT_EQUALS(ErrorCodes::UnknownError, doTxn(opCtx.get(), "test", cmdObj, &resultBuilder)); auto result = resultBuilder.obj(); auto status = getStatusFromDoTxnResult(result); ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, status); @@ -199,7 +196,6 @@ TEST_F(DoTxnTest, AtomicDoTxnInsertIntoNonexistentCollectionReturnsNamespaceNotF TEST_F(DoTxnTest, AtomicDoTxnInsertIntoCollectionWithoutUuid) { auto opCtx = cc().makeOperationContext(); - auto mode = OplogApplication::Mode::kApplyOpsCmd; NamespaceString nss("test.t"); // Collection has no uuid. @@ -210,13 +206,12 @@ TEST_F(DoTxnTest, AtomicDoTxnInsertIntoCollectionWithoutUuid) { auto cmdObj = makeDoTxnWithInsertOperation(nss, boost::none, documentToInsert); auto expectedCmdObj = makeApplyOpsWithInsertOperation(nss, boost::none, documentToInsert); BSONObjBuilder resultBuilder; - ASSERT_OK(doTxn(opCtx.get(), "test", cmdObj, mode, &resultBuilder)); + ASSERT_OK(doTxn(opCtx.get(), "test", cmdObj, &resultBuilder)); ASSERT_BSONOBJ_EQ(expectedCmdObj, _opObserver->onApplyOpsCmdObj); } TEST_F(DoTxnTest, AtomicDoTxnInsertWithUuidIntoCollectionWithUuid) { auto opCtx = cc().makeOperationContext(); - auto mode = OplogApplication::Mode::kApplyOpsCmd; NamespaceString nss("test.t"); auto uuid = UUID::gen(); @@ -229,13 +224,12 @@ TEST_F(DoTxnTest, AtomicDoTxnInsertWithUuidIntoCollectionWithUuid) { auto cmdObj = makeDoTxnWithInsertOperation(nss, uuid, documentToInsert); auto expectedCmdObj = makeApplyOpsWithInsertOperation(nss, uuid, documentToInsert); BSONObjBuilder resultBuilder; - ASSERT_OK(doTxn(opCtx.get(), "test", cmdObj, mode, &resultBuilder)); + ASSERT_OK(doTxn(opCtx.get(), "test", cmdObj, &resultBuilder)); ASSERT_BSONOBJ_EQ(expectedCmdObj, _opObserver->onApplyOpsCmdObj); } TEST_F(DoTxnTest, AtomicDoTxnInsertWithUuidIntoCollectionWithoutUuid) { auto opCtx = cc().makeOperationContext(); - auto mode = OplogApplication::Mode::kApplyOpsCmd; NamespaceString nss("test.t"); auto uuid = UUID::gen(); @@ -249,8 +243,7 @@ TEST_F(DoTxnTest, AtomicDoTxnInsertWithUuidIntoCollectionWithoutUuid) { auto documentToInsert = BSON("_id" << 0); auto cmdObj = makeDoTxnWithInsertOperation(nss, uuid, documentToInsert); BSONObjBuilder resultBuilder; - ASSERT_EQUALS(ErrorCodes::UnknownError, - doTxn(opCtx.get(), "test", cmdObj, mode, &resultBuilder)); + ASSERT_EQUALS(ErrorCodes::UnknownError, doTxn(opCtx.get(), "test", cmdObj, &resultBuilder)); auto result = resultBuilder.obj(); auto status = getStatusFromDoTxnResult(result); ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, status); @@ -258,7 +251,6 @@ TEST_F(DoTxnTest, AtomicDoTxnInsertWithUuidIntoCollectionWithoutUuid) { TEST_F(DoTxnTest, AtomicDoTxnInsertWithoutUuidIntoCollectionWithUuid) { auto opCtx = cc().makeOperationContext(); - auto mode = OplogApplication::Mode::kApplyOpsCmd; NamespaceString nss("test.t"); auto uuid = UUID::gen(); @@ -270,7 +262,7 @@ TEST_F(DoTxnTest, AtomicDoTxnInsertWithoutUuidIntoCollectionWithUuid) { auto documentToInsert = BSON("_id" << 0); auto cmdObj = makeDoTxnWithInsertOperation(nss, boost::none, documentToInsert); BSONObjBuilder resultBuilder; - ASSERT_OK(doTxn(opCtx.get(), "test", cmdObj, mode, &resultBuilder)); + ASSERT_OK(doTxn(opCtx.get(), "test", cmdObj, &resultBuilder)); // Insert operation provided by caller did not contain collection uuid but doTxn() should add // the uuid to the oplog entry. @@ -278,51 +270,6 @@ TEST_F(DoTxnTest, AtomicDoTxnInsertWithoutUuidIntoCollectionWithUuid) { ASSERT_BSONOBJ_EQ(expectedCmdObj, _opObserver->onApplyOpsCmdObj); } -TEST_F(DoTxnTest, DoTxnPropagatesOplogApplicationMode) { - auto opCtx = cc().makeOperationContext(); - - // Increase log component verbosity to check for op application messages. - logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogComponent::kReplication, - logger::LogSeverity::Debug(3)); - - // Test that the 'doTxn' function passes the oplog application mode through correctly to the - // underlying op application functions. - NamespaceString nss("test.coll"); - auto uuid = UUID::gen(); - - // Create a collection for us to insert documents into. - CollectionOptions collectionOptions; - collectionOptions.uuid = uuid; - ASSERT_OK(_storage->createCollection(opCtx.get(), nss, collectionOptions)); - - BSONObjBuilder resultBuilder; - - // Make sure the oplog application mode is passed through via 'doTxn' correctly. - startCapturingLogMessages(); - - auto docToInsert0 = BSON("_id" << 0); - auto cmdObj = makeDoTxnWithInsertOperation(nss, uuid, docToInsert0); - - ASSERT_OK(doTxn(opCtx.get(), - nss.coll().toString(), - cmdObj, - OplogApplication::Mode::kInitialSync, - &resultBuilder)); - ASSERT_EQUALS(1, countLogLinesContaining("oplog application mode: InitialSync")); - - auto docToInsert1 = BSON("_id" << 1); - cmdObj = makeDoTxnWithInsertOperation(nss, uuid, docToInsert1); - - ASSERT_OK(doTxn(opCtx.get(), - nss.coll().toString(), - cmdObj, - OplogApplication::Mode::kSecondary, - &resultBuilder)); - ASSERT_EQUALS(1, countLogLinesContaining("oplog application mode: Secondary")); - - stopCapturingLogMessages(); -} - } // namespace } // namespace repl } // namespace mongo |