summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorMatthew Russotto <matthew.russotto@10gen.com>2018-01-08 17:06:56 -0500
committerMatthew Russotto <matthew.russotto@10gen.com>2018-01-08 17:07:32 -0500
commit8b766fdc6c896ba0a4db30d7b9d2f050b464db22 (patch)
tree107cf6c037074c53bdddb61423c4c6cb352b3003 /src/mongo
parent27ccd4a3bf1150a1bf4356de8a91e4dff64e0762 (diff)
downloadmongo-8b766fdc6c896ba0a4db30d7b9d2f050b464db22.tar.gz
SERVER-32162 Remove non-atomic (push-based replication) support from doTxn.
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/commands/apply_ops_cmd.cpp3
-rw-r--r--src/mongo/db/commands/do_txn_cmd.cpp150
-rw-r--r--src/mongo/db/commands/oplog_application_checks.cpp14
-rw-r--r--src/mongo/db/commands/oplog_application_checks.h5
-rw-r--r--src/mongo/db/repl/do_txn.cpp250
-rw-r--r--src/mongo/db/repl/do_txn.h2
-rw-r--r--src/mongo/db/repl/do_txn_test.cpp65
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