summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/commands.cpp2
-rw-r--r--src/mongo/db/commands/SConscript1
-rw-r--r--src/mongo/db/commands/do_txn_cmd.cpp172
-rw-r--r--src/mongo/db/commands/oplog_application_checks.h4
-rw-r--r--src/mongo/db/initialize_operation_session_info.cpp16
-rw-r--r--src/mongo/db/repl/SConscript20
-rw-r--r--src/mongo/db/repl/do_txn.cpp313
-rw-r--r--src/mongo/db/repl/do_txn.h57
-rw-r--r--src/mongo/db/repl/do_txn_test.cpp335
-rw-r--r--src/mongo/db/transaction_validation.cpp2
10 files changed, 2 insertions, 920 deletions
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index 8efc03ab32d..17bc2af8fca 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -114,7 +114,6 @@ const StringMap<int> txnCmdWhitelist = {{"abortTransaction", 1},
{"coordinateCommitTransaction", 1},
{"delete", 1},
{"distinct", 1},
- {"doTxn", 1},
{"find", 1},
{"findandmodify", 1},
{"findAndModify", 1},
@@ -129,7 +128,6 @@ const StringMap<int> txnCmdWhitelist = {{"abortTransaction", 1},
const StringMap<int> txnAdminCommands = {{"abortTransaction", 1},
{"commitTransaction", 1},
{"coordinateCommitTransaction", 1},
- {"doTxn", 1},
{"prepareTransaction", 1}};
} // namespace
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index 61c39a599cd..d48a75d91b3 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -349,7 +349,6 @@ env.Library(
"dbcheck.cpp",
"dbcommands_d.cpp",
"dbhash.cpp",
- "do_txn_cmd.cpp",
"driverHelpers.cpp",
"haystack.cpp",
"mr.cpp",
diff --git a/src/mongo/db/commands/do_txn_cmd.cpp b/src/mongo/db/commands/do_txn_cmd.cpp
deleted file mode 100644
index fbc542f952a..00000000000
--- a/src/mongo/db/commands/do_txn_cmd.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * 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"
-
-#include <vector>
-
-#include "mongo/bson/util/bson_check.h"
-#include "mongo/bson/util/bson_extract.h"
-#include "mongo/db/auth/authorization_session.h"
-#include "mongo/db/catalog/collection_catalog.h"
-#include "mongo/db/catalog/document_validation.h"
-#include "mongo/db/client.h"
-#include "mongo/db/commands.h"
-#include "mongo/db/commands/oplog_application_checks.h"
-#include "mongo/db/commands/test_commands_enabled.h"
-#include "mongo/db/concurrency/write_conflict_exception.h"
-#include "mongo/db/db_raii.h"
-#include "mongo/db/dbdirectclient.h"
-#include "mongo/db/jsobj.h"
-#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/service_context.h"
-#include "mongo/util/log.h"
-#include "mongo/util/scopeguard.h"
-#include "mongo/util/uuid.h"
-
-namespace mongo {
-namespace {
-
-/**
- * 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& doTxnObj) {
- auto parseOp = [](const BSONObj& opObj) {
- try {
- return repl::ReplOperation::parse(IDLParserErrorContext("doTxn"), opObj);
- } catch (...) {
- uasserted(ErrorCodes::FailedToParse,
- str::stream() << "cannot apply a malformed operation in doTxn: "
- << redact(opObj) << ": " << exceptionToStatus().toString());
- }
- };
-
- OplogApplicationValidity ret = OplogApplicationValidity::kOk;
-
- 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());
-
- // Iterate the ops.
- for (BSONElement element : doTxnObj.firstElement().Array()) {
- checkBSONType(BSONType::Object, element);
- BSONObj opObj = element.Obj();
- auto op = parseOp(opObj);
-
- // If the op is a command, it's illegal.
- uassert(ErrorCodes::InvalidOptions,
- "Commands cannot be applied via doTxn.",
- op.getOpType() != repl::OpTypeEnum::kCommand);
-
- // If the op uses any UUIDs at all then the user must possess extra privileges.
- if (op.getUuid()) {
- ret = OplogApplicationValidity::kNeedsUseUUID;
- }
- }
- return ret;
-}
-
-class DoTxnCmd : public BasicCommand {
-public:
- DoTxnCmd() : BasicCommand("doTxn") {}
-
- AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
- return AllowedOnSecondary::kNever;
- }
-
- bool supportsWriteConcern(const BSONObj& cmd) const override {
- return true;
- }
-
- bool supportsReadConcern(const std::string& dbName,
- const BSONObj& cmdObj,
- repl::ReadConcernLevel level) const {
- // Support the read concerns before and after upconversion.
- return level == repl::ReadConcernLevel::kLocalReadConcern ||
- level == repl::ReadConcernLevel::kSnapshotReadConcern;
- }
-
- std::string help() const override {
- return "internal (sharding)\n{ doTxn : [ ] , preCondition : [ { ns : ... , q : ... , "
- "res : ... } ] }";
- }
-
- Status checkAuthForOperation(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj) const override {
- OplogApplicationValidity validity = validateDoTxnCommand(cmdObj);
- return OplogApplicationChecks::checkAuthForCommand(opCtx, dbname, cmdObj, validity);
- }
-
- bool run(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
- BSONObjBuilder& result) override {
-
- validateDoTxnCommand(cmdObj);
-
- boost::optional<DisableDocumentValidation> maybeDisableValidation;
- if (shouldBypassDocumentValidationForCommand(cmdObj))
- maybeDisableValidation.emplace(opCtx);
-
- auto status = OplogApplicationChecks::checkOperationArray(cmdObj.firstElement());
- uassertStatusOK(status);
-
- // TODO (SERVER-30217): When a write concern is provided to the doTxn command, we
- // normally wait on the OpTime of whichever operation successfully completed last. This is
- // erroneous, however, if the last operation in the array happens to be a write no-op and
- // thus isn’t assigned an OpTime. Let the second to last operation in the doTxn be write
- // A, the last operation in doTxn be write B. Let B do a no-op write and let the
- // operation that caused B to be a no-op be C. If C has an OpTime after A but before B,
- // then we won’t wait for C to be replicated and it could be rolled back, even though B
- // 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.
-
- auto doTxnStatus = CommandHelpers::appendCommandStatusNoThrow(
- result, doTxn(opCtx, dbname, cmdObj, &result));
-
- return doTxnStatus;
- }
-};
-
-MONGO_REGISTER_TEST_COMMAND(DoTxnCmd);
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/db/commands/oplog_application_checks.h b/src/mongo/db/commands/oplog_application_checks.h
index 4831950b3d4..3537dc716ab 100644
--- a/src/mongo/db/commands/oplog_application_checks.h
+++ b/src/mongo/db/commands/oplog_application_checks.h
@@ -50,11 +50,9 @@ class OperationContext;
// with a specified UUID, so both the forceUUID and useUUID actions must be authorized.
//
// kOk means no special conditions apply.
-//
-// Only kOk and kNeedsUseUUID are valid for 'doTxn'. All are valid for 'applyOps'.
enum class OplogApplicationValidity { kOk, kNeedsUseUUID, kNeedsForceAndUseUUID, kNeedsSuperuser };
-// OplogApplicationChecks contains helper functions for checking the applyOps and doTxn commands.
+// OplogApplicationChecks contains helper functions for checking the applyOps command.
class OplogApplicationChecks {
public:
/**
diff --git a/src/mongo/db/initialize_operation_session_info.cpp b/src/mongo/db/initialize_operation_session_info.cpp
index 9be38b08946..0257878e145 100644
--- a/src/mongo/db/initialize_operation_session_info.cpp
+++ b/src/mongo/db/initialize_operation_session_info.cpp
@@ -143,22 +143,6 @@ OperationSessionInfoFromClient initializeOperationSessionInfo(OperationContext*
osi.getStartTransaction().value());
}
- // Populate the session info for doTxn command.
- if (requestBody.firstElementFieldName() == "doTxn"_sd) {
- uassert(ErrorCodes::InvalidOptions,
- "doTxn can only be run with a transaction number.",
- osi.getTxnNumber());
- uassert(ErrorCodes::OperationNotSupportedInTransaction,
- "doTxn can not be run in a transaction",
- !osi.getAutocommit());
- // 'autocommit' and 'startTransaction' are populated for 'doTxn' to get the oplog
- // entry generation behavior used for multi-document transactions. The 'doTxn'
- // command still logically behaves as a commit.
- osi.setAutocommit(false);
- osi.setStartTransaction(true);
- }
-
-
return osi;
}
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index eda9c1c6315..871e321363d 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -32,7 +32,6 @@ env.Library(
target='oplog',
source=[
'apply_ops.cpp',
- 'do_txn.cpp',
'oplog.cpp',
'transaction_oplog_application.cpp',
env.Idlc('apply_ops.idl')[0],
@@ -116,25 +115,6 @@ env.Library(
],
)
-env.CppUnitTest(
- target='do_txn_test',
- source=[
- 'do_txn_test.cpp',
- ],
- LIBDEPS=[
- 'oplog',
- 'oplog_entry',
- 'oplog_interface_local',
- 'replmocks',
- 'storage_interface_impl',
- '$BUILD_DIR/mongo/db/auth/authmocks',
- '$BUILD_DIR/mongo/db/s/op_observer_sharding_impl',
- '$BUILD_DIR/mongo/db/service_context_d_test_fixture',
- '$BUILD_DIR/mongo/db/transaction',
- '$BUILD_DIR/mongo/rpc/command_status',
- ],
-)
-
env.Library(
target='rollback_source_impl',
source=[
diff --git a/src/mongo/db/repl/do_txn.cpp b/src/mongo/db/repl/do_txn.cpp
deleted file mode 100644
index d9d6f7bf2ce..00000000000
--- a/src/mongo/db/repl/do_txn.cpp
+++ /dev/null
@@ -1,313 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * 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"
-
-#include "mongo/db/repl/do_txn.h"
-
-#include "mongo/bson/util/bson_extract.h"
-#include "mongo/db/catalog/collection.h"
-#include "mongo/db/catalog/collection_catalog.h"
-#include "mongo/db/catalog/database.h"
-#include "mongo/db/catalog/database_holder.h"
-#include "mongo/db/catalog/document_validation.h"
-#include "mongo/db/client.h"
-#include "mongo/db/concurrency/lock_state.h"
-#include "mongo/db/concurrency/write_conflict_exception.h"
-#include "mongo/db/curop.h"
-#include "mongo/db/db_raii.h"
-#include "mongo/db/dbhelpers.h"
-#include "mongo/db/index/index_descriptor.h"
-#include "mongo/db/matcher/matcher.h"
-#include "mongo/db/op_observer.h"
-#include "mongo/db/operation_context.h"
-#include "mongo/db/query/collation/collation_spec.h"
-#include "mongo/db/repl/replication_coordinator.h"
-#include "mongo/db/service_context.h"
-#include "mongo/db/transaction_participant.h"
-#include "mongo/db/views/view_catalog.h"
-#include "mongo/rpc/get_status_from_command_result.h"
-#include "mongo/util/fail_point_service.h"
-#include "mongo/util/log.h"
-
-namespace mongo {
-
-constexpr StringData DoTxn::kPreconditionFieldName;
-
-namespace {
-
-// If enabled, causes loop in _doTxn() to hang after applying current operation.
-MONGO_FAIL_POINT_DEFINE(doTxnPauseBetweenOperations);
-
-/**
- * Return true iff the doTxnCmd can be executed in a single WriteUnitOfWork.
- */
-bool _areOpsCrudOnly(const BSONObj& doTxnCmd) {
- for (const auto& elem : doTxnCmd.firstElement().Obj()) {
- const char* opType = elem.Obj().getField("op").valuestrsafe();
-
- // All atomic ops have an opType of length 1.
- if (opType[0] == '\0' || opType[1] != '\0')
- return false;
-
- // Only consider CRUD operations.
- switch (*opType) {
- case 'd':
- case 'u':
- break;
- case 'i':
- break;
- // Fallthrough.
- default:
- return false;
- }
- }
-
- return true;
-}
-
-Status _doTxn(OperationContext* opCtx,
- const std::string& dbName,
- const BSONObj& doTxnCmd,
- BSONObjBuilder* result,
- int* numApplied) {
- BSONObj ops = doTxnCmd.firstElement().Obj();
- // apply
- *numApplied = 0;
- int errors = 0;
-
- BSONObjIterator i(ops);
- BSONArrayBuilder ab;
- 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();
-
- boost::optional<NamespaceString> nss = NamespaceString(opObj["ns"].String());
-
- // Need to check this here, or OldClientContext may fail an invariant.
- if (!nss->isValid())
- return {ErrorCodes::InvalidNamespace, "invalid ns: " + nss->ns()};
-
- Status status(ErrorCodes::InternalError, "");
-
- AutoGetDb autoDb(opCtx, nss->db(), MODE_IX);
- auto db = autoDb.getDb();
- if (!db) {
- uasserted(ErrorCodes::NamespaceNotFound,
- str::stream() << "cannot apply insert, delete, or update operation on a "
- "non-existent namespace "
- << nss->ns() << ": " << mongo::redact(opObj));
- }
-
- if (opObj.hasField("ui")) {
- auto uuidStatus = UUID::parse(opObj["ui"]);
- uassertStatusOK(uuidStatus.getStatus());
- // If "ui" is present, it overrides "nss" for the collection name.
- nss = CollectionCatalog::get(opCtx).lookupNSSByUUID(uuidStatus.getValue());
- uassert(ErrorCodes::NamespaceNotFound,
- str::stream() << "cannot find collection uuid " << uuidStatus.getValue(),
- nss);
- }
- Lock::CollectionLock collLock(opCtx, *nss, MODE_IX);
- auto collection = db->getCollection(opCtx, *nss);
-
- // 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.
- if (!collection && ViewCatalog::get(db)->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));
- }
-
- // 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, db, opObj, alwaysUpsert, repl::OplogApplication::Mode::kApplyOpsCmd);
- if (!status.isOK())
- return status;
-
- ab.append(status.isOK());
- if (!status.isOK()) {
- log() << "doTxn error applying: " << status;
- errors++;
- }
-
- (*numApplied)++;
-
- if (MONGO_FAIL_POINT(doTxnPauseBetweenOperations)) {
- MONGO_FAIL_POINT_PAUSE_WHILE_SET(doTxnPauseBetweenOperations);
- }
- }
-
- result->append("applied", *numApplied);
- result->append("results", ab.arr());
-
- if (errors != 0) {
- return Status(ErrorCodes::UnknownError, "doTxn had one or more errors applying ops");
- }
-
- return Status::OK();
-}
-
-bool _hasPrecondition(const BSONObj& doTxnCmd) {
- return doTxnCmd[DoTxn::kPreconditionFieldName].type() == Array;
-}
-
-Status _checkPrecondition(OperationContext* opCtx,
- const BSONObj& doTxnCmd,
- BSONObjBuilder* result) {
- // Precondition check must be done in a write unit of work to make sure it's
- // sharing the same snapshot as the writes.
- invariant(opCtx->lockState()->inAWriteUnitOfWork());
-
- invariant(_hasPrecondition(doTxnCmd));
-
- for (auto elem : doTxnCmd[DoTxn::kPreconditionFieldName].Obj()) {
- auto preCondition = elem.Obj();
- if (preCondition["ns"].type() != BSONType::String) {
- return {ErrorCodes::InvalidNamespace,
- str::stream() << "ns in preCondition must be a string, but found type: "
- << typeName(preCondition["ns"].type())};
- }
- const NamespaceString nss(preCondition["ns"].valueStringData());
- if (!nss.isValid()) {
- return {ErrorCodes::InvalidNamespace, "invalid ns: " + nss.ns()};
- }
-
- // Even if snapshot isolation is provided, database catalog still needs locking.
- // Only X and IX mode locks are stashed by the WriteUnitOfWork and IS->IX upgrade
- // is not supported, so we can not use IS mode here.
- AutoGetCollection autoColl(opCtx, nss, MODE_IX);
-
- Database* database = autoColl.getDb();
- if (!database) {
- return {ErrorCodes::NamespaceNotFound, "database in ns does not exist: " + nss.ns()};
- }
- Collection* collection = autoColl.getCollection();
- if (!collection) {
- return {ErrorCodes::NamespaceNotFound, "collection in ns does not exist: " + nss.ns()};
- }
-
- BSONObj realres;
- auto qrStatus = QueryRequest::fromLegacyQuery(nss, preCondition["q"].Obj(), {}, 0, 0, 0);
- if (!qrStatus.isOK()) {
- return qrStatus.getStatus();
- }
- auto recordId = Helpers::findOne(
- opCtx, autoColl.getCollection(), std::move(qrStatus.getValue()), false);
- if (!recordId.isNull()) {
- realres = collection->docFor(opCtx, recordId).value();
- }
-
-
- // Get collection default collation.
- const CollatorInterface* collator = collection->getDefaultCollator();
-
- boost::intrusive_ptr<ExpressionContext> expCtx(new ExpressionContext(opCtx, collator));
- Matcher matcher(preCondition["res"].Obj(), std::move(expCtx));
- if (!matcher.matches(realres)) {
- result->append("got", realres);
- result->append("whatFailed", preCondition);
- return {ErrorCodes::BadValue, "preCondition failed"};
- }
- }
-
- return Status::OK();
-}
-} // namespace
-
-Status doTxn(OperationContext* opCtx,
- const std::string& dbName,
- const BSONObj& doTxnCmd,
- BSONObjBuilder* result) {
- auto txnParticipant = TransactionParticipant::get(opCtx);
- uassert(ErrorCodes::InvalidOptions, "doTxn must be run within a transaction", txnParticipant);
- invariant(txnParticipant.inMultiDocumentTransaction());
- invariant(opCtx->getWriteUnitOfWork());
- uassert(
- ErrorCodes::InvalidOptions, "doTxn supports only CRUD opts.", _areOpsCrudOnly(doTxnCmd));
- auto hasPrecondition = _hasPrecondition(doTxnCmd);
-
-
- // Acquire global lock in IX mode so that the replication state check will remain valid.
- Lock::GlobalLock globalLock(opCtx, MODE_IX);
-
- auto replCoord = repl::ReplicationCoordinator::get(opCtx);
- bool userInitiatedWritesAndNotPrimary =
- opCtx->writesAreReplicated() && !replCoord->canAcceptWritesForDatabase(opCtx, dbName);
-
- if (userInitiatedWritesAndNotPrimary)
- return Status(ErrorCodes::NotMaster,
- str::stream() << "Not primary while applying ops to database " << dbName);
-
- int numApplied = 0;
-
- try {
- BSONObjBuilder intermediateResult;
-
- // The transaction takes place in a global unit of work, so the precondition check
- // and the writes will share the same snapshot.
- if (hasPrecondition) {
- uassertStatusOK(_checkPrecondition(opCtx, doTxnCmd, result));
- }
-
- numApplied = 0;
- uassertStatusOK(_doTxn(opCtx, dbName, doTxnCmd, &intermediateResult, &numApplied));
- txnParticipant.commitUnpreparedTransaction(opCtx);
- result->appendElements(intermediateResult.obj());
- } catch (const DBException& ex) {
- txnParticipant.abortActiveUnpreparedOrStashPreparedTransaction(opCtx);
- BSONArrayBuilder ab;
- ++numApplied;
- for (int j = 0; j < numApplied; j++)
- 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());
- }
-
- return Status::OK();
-}
-
-} // namespace mongo
diff --git a/src/mongo/db/repl/do_txn.h b/src/mongo/db/repl/do_txn.h
deleted file mode 100644
index 7bed2bf3700..00000000000
--- a/src/mongo/db/repl/do_txn.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-#include "mongo/base/status.h"
-#include "mongo/db/repl/oplog.h"
-
-namespace mongo {
-class BSONObj;
-class BSONObjBuilder;
-class OperationContext;
-
-class DoTxn {
-public:
- static constexpr StringData kPreconditionFieldName = "preCondition"_sd;
-};
-
-/**
- * Applies ops contained in 'doTxnCmd' and populates fields in 'result' to be returned to the
- * caller. The information contained in 'result' can be returned to the user if called as part
- * of the execution of an 'doTxn' command.
- *
- * The 'oplogApplicationMode' argument determines the semantics of the operations contained within
- * the given command object. This function may be called as part of a direct user invocation of the
- * 'doTxn' command, or as part of the application of an 'doTxn' oplog operation. In either
- * case, the mode can be set to determine how the internal ops are executed.
- */
-Status doTxn(OperationContext* opCtx,
- const std::string& dbName,
- const BSONObj& doTxnCmd,
- BSONObjBuilder* result);
-
-} // namespace mongo
diff --git a/src/mongo/db/repl/do_txn_test.cpp b/src/mongo/db/repl/do_txn_test.cpp
deleted file mode 100644
index 561579a069c..00000000000
--- a/src/mongo/db/repl/do_txn_test.cpp
+++ /dev/null
@@ -1,335 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/db/catalog/collection_options.h"
-#include "mongo/db/catalog/database_holder.h"
-#include "mongo/db/client.h"
-#include "mongo/db/op_observer_noop.h"
-#include "mongo/db/op_observer_registry.h"
-#include "mongo/db/repl/do_txn.h"
-#include "mongo/db/repl/oplog_interface_local.h"
-#include "mongo/db/repl/repl_client_info.h"
-#include "mongo/db/repl/replication_coordinator_mock.h"
-#include "mongo/db/repl/storage_interface_impl.h"
-#include "mongo/db/s/op_observer_sharding_impl.h"
-#include "mongo/db/service_context_d_test_fixture.h"
-#include "mongo/db/session_catalog_mongod.h"
-#include "mongo/db/transaction_participant.h"
-#include "mongo/logger/logger.h"
-#include "mongo/rpc/get_status_from_command_result.h"
-#include "mongo/stdx/memory.h"
-
-namespace mongo {
-namespace repl {
-namespace {
-
-boost::optional<OplogEntry> onAllTransactionCommit(OperationContext* opCtx) {
- OplogInterfaceLocal oplogInterface(opCtx);
- auto oplogIter = oplogInterface.makeIterator();
- auto opEntry = unittest::assertGet(oplogIter->next());
- return unittest::assertGet(OplogEntry::parse(opEntry.first));
-}
-
-/**
- * Mock OpObserver that tracks doTxn commit events.
- */
-class OpObserverMock : public OpObserverNoop {
-public:
- /**
- * Called by doTxn() when ops are ready to commit.
- */
- void onUnpreparedTransactionCommit(OperationContext* opCtx,
- const std::vector<repl::ReplOperation>& statements) override;
-
-
- /**
- * Called by doTxn() when ops are ready to commit.
- */
- void onPreparedTransactionCommit(
- OperationContext* opCtx,
- OplogSlot commitOplogEntryOpTime,
- Timestamp commitTimestamp,
- const std::vector<repl::ReplOperation>& statements) noexcept override;
-
- // If present, holds the applyOps oplog entry written out by the ObObserverImpl
- // onPreparedTransactionCommit or onUnpreparedTransactionCommit.
- boost::optional<OplogEntry> applyOpsOplogEntry;
-};
-
-void OpObserverMock::onUnpreparedTransactionCommit(
- OperationContext* opCtx, const std::vector<repl::ReplOperation>& statements) {
-
- applyOpsOplogEntry = onAllTransactionCommit(opCtx);
-}
-
-void OpObserverMock::onPreparedTransactionCommit(
- OperationContext* opCtx,
- OplogSlot commitOplogEntryOpTime,
- Timestamp commitTimestamp,
- const std::vector<repl::ReplOperation>& statements) noexcept {
- applyOpsOplogEntry = onAllTransactionCommit(opCtx);
-}
-
-/**
- * Test fixture for doTxn().
- */
-class DoTxnTest : public ServiceContextMongoDTest {
-private:
- void setUp() override;
- void tearDown() override;
-
-protected:
- OperationContext* opCtx() {
- return _opCtx.get();
- }
-
- void checkTxnTable() {
- auto result = _storage->findById(
- opCtx(),
- NamespaceString::kSessionTransactionsTableNamespace,
- BSON(SessionTxnRecord::kSessionIdFieldName << opCtx()->getLogicalSessionId()->toBSON())
- .firstElement());
- if (!_opObserver->applyOpsOplogEntry) {
- ASSERT_NOT_OK(result);
- return;
- }
- auto txnRecord = SessionTxnRecord::parse(IDLParserErrorContext("parse txn record for test"),
- unittest::assertGet(result));
-
- ASSERT(opCtx()->getTxnNumber());
- ASSERT_EQ(*opCtx()->getTxnNumber(), txnRecord.getTxnNum());
- ASSERT_EQ(_opObserver->applyOpsOplogEntry->getOpTime(), txnRecord.getLastWriteOpTime());
- ASSERT(_opObserver->applyOpsOplogEntry->getWallClockTime());
- ASSERT_EQ(*_opObserver->applyOpsOplogEntry->getWallClockTime(),
- txnRecord.getLastWriteDate());
- }
-
- OpObserverMock* _opObserver = nullptr;
- std::unique_ptr<StorageInterface> _storage;
- ServiceContext::UniqueOperationContext _opCtx;
- boost::optional<MongoDOperationContextSession> _ocs;
-};
-
-void DoTxnTest::setUp() {
- // Set up mongod.
- ServiceContextMongoDTest::setUp();
-
- const auto service = getServiceContext();
- _opCtx = cc().makeOperationContext();
-
- // Set up ReplicationCoordinator and create oplog.
- ReplicationCoordinator::set(service, stdx::make_unique<ReplicationCoordinatorMock>(service));
- setOplogCollectionName(service);
- createOplog(_opCtx.get());
-
- // Ensure that we are primary.
- auto replCoord = ReplicationCoordinator::get(_opCtx.get());
- ASSERT_OK(replCoord->setFollowerMode(MemberState::RS_PRIMARY));
-
- // onStepUp() relies on the storage interface to create the config.transactions table.
- repl::StorageInterface::set(service, std::make_unique<StorageInterfaceImpl>());
-
- // Set up session catalog
- MongoDSessionCatalog::onStepUp(_opCtx.get());
-
- // Need the OpObserverImpl in the registry in order for doTxn to work.
- OpObserverRegistry* opObserverRegistry =
- dynamic_cast<OpObserverRegistry*>(service->getOpObserver());
- opObserverRegistry->addObserver(stdx::make_unique<OpObserverShardingImpl>());
-
- // Use OpObserverMock to track applyOps calls generated by doTxn().
- auto opObserver = stdx::make_unique<OpObserverMock>();
- _opObserver = opObserver.get();
- opObserverRegistry->addObserver(std::move(opObserver));
-
- // This test uses StorageInterface to create collections and inspect documents inside
- // collections.
- _storage = stdx::make_unique<StorageInterfaceImpl>();
-
- // Set up the transaction and session.
- _opCtx->setLogicalSessionId(makeLogicalSessionIdForTest());
- _opCtx->setTxnNumber(0); // TxnNumber can always be 0 because we have a new session.
- _ocs.emplace(_opCtx.get());
-
- auto txnParticipant = TransactionParticipant::get(opCtx());
- txnParticipant.beginOrContinue(opCtx(), *opCtx()->getTxnNumber(), false, true);
- txnParticipant.unstashTransactionResources(opCtx(), "doTxn");
-}
-
-void DoTxnTest::tearDown() {
- _ocs = boost::none;
- _opCtx = nullptr;
- _storage = {};
- _opObserver = nullptr;
-
- // Reset default log level in case it was changed.
- logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogComponent::kReplication,
- logger::LogSeverity::Debug(0));
-
- ServiceContextMongoDTest::tearDown();
-}
-
-/**
- * Fixes up result document returned by doTxn and converts to Status.
- */
-Status getStatusFromDoTxnResult(const BSONObj& result) {
- if (result["ok"]) {
- return getStatusFromCommandResult(result);
- }
-
- BSONObjBuilder builder;
- builder.appendElements(result);
- auto code = result.getIntField("code");
- builder.appendIntOrLL("ok", code == 0);
- auto newResult = builder.obj();
- return getStatusFromCommandResult(newResult);
-}
-
-BSONObj makeInsertOperation(const NamespaceString& nss,
- const OptionalCollectionUUID& uuid,
- const BSONObj& documentToInsert) {
- return uuid ? BSON("op"
- << "i"
- << "ns" << nss.ns() << "o" << documentToInsert << "ui" << *uuid)
- : BSON("op"
- << "i"
- << "ns" << nss.ns() << "o" << documentToInsert);
-}
-
-/**
- * Creates an doTxn command object with a single insert operation.
- */
-BSONObj makeDoTxnWithInsertOperation(const NamespaceString& nss,
- const OptionalCollectionUUID& uuid,
- const BSONObj& documentToInsert) {
- auto insertOp = makeInsertOperation(nss, uuid, documentToInsert);
- return BSON("doTxn" << BSON_ARRAY(insertOp));
-}
-
-/**
- * Creates an applyOps command object with a single insert operation.
- */
-BSONObj makeApplyOpsWithInsertOperation(const NamespaceString& nss,
- const OptionalCollectionUUID& uuid,
- const BSONObj& documentToInsert) {
- auto insertOp = makeInsertOperation(nss, uuid, documentToInsert);
- return BSON("applyOps" << BSON_ARRAY(insertOp));
-}
-
-TEST_F(DoTxnTest, AtomicDoTxnInsertIntoNonexistentCollectionReturnsNamespaceNotFoundInResult) {
- NamespaceString nss("test.t");
- auto documentToInsert = BSON("_id" << 0);
- auto cmdObj = makeDoTxnWithInsertOperation(nss, boost::none, documentToInsert);
- BSONObjBuilder resultBuilder;
- ASSERT_EQUALS(ErrorCodes::UnknownError, doTxn(opCtx(), "test", cmdObj, &resultBuilder));
- auto result = resultBuilder.obj();
- auto status = getStatusFromDoTxnResult(result);
- ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, status);
- checkTxnTable();
-}
-
-TEST_F(DoTxnTest, AtomicDoTxnInsertWithUuidIntoCollectionWithUuid) {
- NamespaceString nss("test.t");
-
- auto uuid = UUID::gen();
-
- CollectionOptions collectionOptions;
- collectionOptions.uuid = uuid;
- ASSERT_OK(_storage->createCollection(opCtx(), nss, collectionOptions));
-
- auto documentToInsert = BSON("_id" << 0);
- auto cmdObj = makeDoTxnWithInsertOperation(nss, uuid, documentToInsert);
- auto expectedCmdObj = makeApplyOpsWithInsertOperation(nss, uuid, documentToInsert);
- BSONObjBuilder resultBuilder;
- ASSERT_OK(doTxn(opCtx(), "test", cmdObj, &resultBuilder));
- ASSERT_EQ(expectedCmdObj.woCompare(_opObserver->applyOpsOplogEntry->getObject(),
- BSONObj(),
- BSONObj::ComparisonRules::kIgnoreFieldOrder |
- BSONObj::ComparisonRules::kConsiderFieldName),
- 0)
- << "expected: " << expectedCmdObj
- << " got: " << _opObserver->applyOpsOplogEntry->getObject();
- checkTxnTable();
-}
-
-TEST_F(DoTxnTest, AtomicDoTxnInsertWithUuidIntoCollectionWithOtherUuid) {
- NamespaceString nss("test.t");
-
- auto doTxnUuid = UUID::gen();
-
- // Collection has a different UUID.
- CollectionOptions collectionOptions;
- collectionOptions.uuid = UUID::gen();
- ASSERT_NOT_EQUALS(doTxnUuid, *collectionOptions.uuid);
- ASSERT_OK(_storage->createCollection(opCtx(), nss, collectionOptions));
-
- // The doTxn returns a NamespaceNotFound error because of the failed UUID lookup
- // even though a collection exists with the same namespace as the insert operation.
- auto documentToInsert = BSON("_id" << 0);
- auto cmdObj = makeDoTxnWithInsertOperation(nss, doTxnUuid, documentToInsert);
- BSONObjBuilder resultBuilder;
- ASSERT_EQUALS(ErrorCodes::UnknownError, doTxn(opCtx(), "test", cmdObj, &resultBuilder));
- auto result = resultBuilder.obj();
- auto status = getStatusFromDoTxnResult(result);
- ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, status);
- checkTxnTable();
-}
-
-TEST_F(DoTxnTest, AtomicDoTxnInsertWithoutUuidIntoCollectionWithUuid) {
- NamespaceString nss("test.t");
-
- auto uuid = UUID::gen();
-
- CollectionOptions collectionOptions;
- collectionOptions.uuid = uuid;
- ASSERT_OK(_storage->createCollection(opCtx(), nss, collectionOptions));
-
- auto documentToInsert = BSON("_id" << 0);
- auto cmdObj = makeDoTxnWithInsertOperation(nss, boost::none, documentToInsert);
- BSONObjBuilder resultBuilder;
- ASSERT_OK(doTxn(opCtx(), "test", cmdObj, &resultBuilder));
-
- // Insert operation provided by caller did not contain collection uuid but doTxn() should add
- // the uuid to the oplog entry.
- auto expectedCmdObj = makeApplyOpsWithInsertOperation(nss, uuid, documentToInsert);
- ASSERT_EQ(expectedCmdObj.woCompare(_opObserver->applyOpsOplogEntry->getObject(),
- BSONObj(),
- BSONObj::ComparisonRules::kIgnoreFieldOrder |
- BSONObj::ComparisonRules::kConsiderFieldName),
- 0)
- << "expected: " << expectedCmdObj
- << " got: " << _opObserver->applyOpsOplogEntry->getObject();
- checkTxnTable();
-}
-
-} // namespace
-} // namespace repl
-} // namespace mongo
diff --git a/src/mongo/db/transaction_validation.cpp b/src/mongo/db/transaction_validation.cpp
index d69b935e5bb..a2af2228612 100644
--- a/src/mongo/db/transaction_validation.cpp
+++ b/src/mongo/db/transaction_validation.cpp
@@ -61,7 +61,7 @@ void validateWriteConcernForTransaction(const WriteConcernOptions& wcResult, Str
"writeConcern is not allowed within a multi-statement transaction",
wcResult.usedDefault || cmdName == "commitTransaction" ||
cmdName == "coordinateCommitTransaction" || cmdName == "abortTransaction" ||
- cmdName == "prepareTransaction" || cmdName == "doTxn");
+ cmdName == "prepareTransaction");
}
bool shouldCommandSkipSessionCheckout(StringData cmdName) {