summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCheahuychou Mao <mao.cheahuychou@gmail.com>2021-10-28 14:31:44 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-10-29 00:50:10 +0000
commit4e04c860df75edf6b8bd1de205de0f379f67ea25 (patch)
tree88102b08ca43ce3fe9cbf40c24babac01eee5125
parentea197fd5193fe034174584e60290dd90fe01d2b1 (diff)
downloadmongo-4e04c860df75edf6b8bd1de205de0f379f67ea25.tar.gz
SERVER-58756 Move ApplyOpsCommandInfo into a separate library
-rw-r--r--src/mongo/db/repl/SConscript14
-rw-r--r--src/mongo/db/repl/apply_ops.cpp105
-rw-r--r--src/mongo/db/repl/apply_ops.h51
-rw-r--r--src/mongo/db/repl/apply_ops_command_info.cpp153
-rw-r--r--src/mongo/db/repl/apply_ops_command_info.h95
5 files changed, 262 insertions, 156 deletions
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index ac2443f24b6..8a9bf6f5a58 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -25,15 +25,27 @@ env.Library(
)
env.Library(
+ target='apply_ops_command_info',
+ source=[
+ 'apply_ops_command_info.cpp',
+ 'apply_ops.idl',
+ ],
+ LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/base',
+ 'oplog_entry',
+ ]
+)
+
+env.Library(
target='oplog',
source=[
'apply_ops.cpp',
'oplog.cpp',
'oplog_entry_or_grouped_inserts.cpp',
'transaction_oplog_application.cpp',
- 'apply_ops.idl',
],
LIBDEPS=[
+ 'apply_ops_command_info',
'tenant_migration_decoration',
],
LIBDEPS_PRIVATE=[
diff --git a/src/mongo/db/repl/apply_ops.cpp b/src/mongo/db/repl/apply_ops.cpp
index 41b8c8fd858..6be1ed0b32b 100644
--- a/src/mongo/db/repl/apply_ops.cpp
+++ b/src/mongo/db/repl/apply_ops.cpp
@@ -69,34 +69,6 @@ namespace {
// If enabled, causes loop in _applyOps() to hang after applying current operation.
MONGO_FAIL_POINT_DEFINE(applyOpsPauseBetweenOperations);
-/**
- * Return true iff the applyOpsCmd can be executed in a single WriteUnitOfWork.
- */
-bool _parseAreOpsCrudOnly(const BSONObj& applyOpCmd) {
- for (const auto& elem : applyOpCmd.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 'n':
- case 'u':
- break;
- case 'i':
- break;
- // Fallthrough.
- default:
- return false;
- }
- }
-
- return true;
-}
-
Status _applyOps(OperationContext* opCtx,
const ApplyOpsCommandInfo& info,
repl::OplogApplication::Mode oplogApplicationMode,
@@ -347,38 +319,6 @@ Status _checkPrecondition(OperationContext* opCtx,
}
} // namespace
-// static
-ApplyOpsCommandInfo ApplyOpsCommandInfo::parse(const BSONObj& applyOpCmd) {
- try {
- return ApplyOpsCommandInfo(applyOpCmd);
- } catch (DBException& ex) {
- ex.addContext(str::stream() << "Failed to parse applyOps command: " << redact(applyOpCmd));
- throw;
- }
-}
-
-bool ApplyOpsCommandInfo::areOpsCrudOnly() const {
- return _areOpsCrudOnly;
-}
-
-bool ApplyOpsCommandInfo::isAtomic() const {
- return getAllowAtomic() && areOpsCrudOnly();
-}
-
-ApplyOpsCommandInfo::ApplyOpsCommandInfo(const BSONObj& applyOpCmd)
- : _areOpsCrudOnly(_parseAreOpsCrudOnly(applyOpCmd)) {
- parseProtected(IDLParserErrorContext("applyOps"), applyOpCmd);
-
- if (getPreCondition()) {
- uassert(ErrorCodes::InvalidOptions,
- "Cannot use preCondition with {allowAtomic: false}",
- getAllowAtomic());
- uassert(ErrorCodes::InvalidOptions,
- "Cannot use preCondition when operations include commands.",
- areOpsCrudOnly());
- }
-}
-
Status applyApplyOpsOplogEntry(OperationContext* opCtx,
const OplogEntry& entry,
repl::OplogApplication::Mode oplogApplicationMode) {
@@ -514,50 +454,5 @@ Status applyOps(OperationContext* opCtx,
return Status::OK();
}
-// static
-std::vector<OplogEntry> ApplyOps::extractOperations(const OplogEntry& applyOpsOplogEntry) {
- std::vector<OplogEntry> result;
- extractOperationsTo(applyOpsOplogEntry, applyOpsOplogEntry.getEntry().toBSON(), &result);
- return result;
-}
-
-// static
-void ApplyOps::extractOperationsTo(const OplogEntry& applyOpsOplogEntry,
- const BSONObj& topLevelDoc,
- std::vector<OplogEntry>* operations) {
- uassert(ErrorCodes::TypeMismatch,
- str::stream() << "ApplyOps::extractOperations(): not a command: "
- << redact(applyOpsOplogEntry.toBSONForLogging()),
- applyOpsOplogEntry.isCommand());
-
- uassert(ErrorCodes::CommandNotSupported,
- str::stream() << "ApplyOps::extractOperations(): not applyOps command: "
- << redact(applyOpsOplogEntry.toBSONForLogging()),
- OplogEntry::CommandType::kApplyOps == applyOpsOplogEntry.getCommandType());
-
- auto cmdObj = applyOpsOplogEntry.getOperationToApply();
- auto info = ApplyOpsCommandInfo::parse(cmdObj);
- auto operationDocs = info.getOperations();
- bool alwaysUpsert = info.getAlwaysUpsert() && !applyOpsOplogEntry.getTxnNumber();
-
- for (const auto& operationDoc : operationDocs) {
- // Make sure that the inner ops are not malformed or over-specified.
- ReplOperation::parse(IDLParserErrorContext("extractOperations"), operationDoc);
-
- BSONObjBuilder builder(operationDoc);
-
- // Oplog entries can have an oddly-named "b" field for "upsert". MongoDB stopped creating
- // such entries in 4.0, but we can use the "b" field for the extracted entry here.
- if (alwaysUpsert && !operationDoc.hasField("b")) {
- builder.append("b", true);
- }
-
- builder.appendElementsUnique(topLevelDoc);
- auto operation = builder.obj();
-
- operations->emplace_back(operation);
- }
-}
-
} // namespace repl
} // namespace mongo
diff --git a/src/mongo/db/repl/apply_ops.h b/src/mongo/db/repl/apply_ops.h
index 470692d8dfb..3887a8a869a 100644
--- a/src/mongo/db/repl/apply_ops.h
+++ b/src/mongo/db/repl/apply_ops.h
@@ -31,6 +31,7 @@
#include "mongo/base/status.h"
#include "mongo/bson/bsonobj.h"
+#include "mongo/db/repl/apply_ops_command_info.h"
#include "mongo/db/repl/apply_ops_gen.h"
#include "mongo/db/repl/multiapplier.h"
#include "mongo/db/repl/oplog.h"
@@ -41,56 +42,6 @@ class BSONObjBuilder;
class OperationContext;
namespace repl {
-class ApplyOps {
-public:
- static constexpr StringData kPreconditionFieldName = "preCondition"_sd;
- static constexpr StringData kOplogApplicationModeFieldName = "oplogApplicationMode"_sd;
-
- /**
- * Extracts CRUD operations from an atomic applyOps oplog entry.
- * Throws UserException on error.
- */
- static std::vector<OplogEntry> extractOperations(const OplogEntry& applyOpsOplogEntry);
-
- /**
- * This variant allows optimization for extracting multiple applyOps operations. The entry for
- * the non-DurableReplOperation fields of the extracted operation must be specified as
- * 'topLevelDoc', and need not be any of the applyOps operations. The 'topLevelDoc' entry's
- * 'ts' field will be used as the 'ts' field for each operation.
- */
- static void extractOperationsTo(const OplogEntry& applyOpsOplogEntry,
- const BSONObj& topLevelDoc,
- std::vector<OplogEntry>* operations);
-};
-
-/**
- * Holds information about an applyOps command object.
- */
-class ApplyOpsCommandInfo : public ApplyOpsCommandInfoBase {
-public:
- /**
- * Parses the object in the 'o' field of an applyOps command.
- * May throw UserException.
- */
- static ApplyOpsCommandInfo parse(const BSONObj& applyOpCmd);
-
- /**
- * Returns true if all operations described by this applyOps command are CRUD only.
- */
- bool areOpsCrudOnly() const;
-
- /**
- * Returns true if applyOps will try to process all operations in a single batch atomically.
- * Derived from getAllowAtomic() and areOpsCrudOnly().
- */
- bool isAtomic() const;
-
-private:
- explicit ApplyOpsCommandInfo(const BSONObj& applyOpCmd);
-
- const bool _areOpsCrudOnly;
-};
-
/**
* Applies ops contained in 'applyOpCmd' 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
diff --git a/src/mongo/db/repl/apply_ops_command_info.cpp b/src/mongo/db/repl/apply_ops_command_info.cpp
new file mode 100644
index 00000000000..57144240cdb
--- /dev/null
+++ b/src/mongo/db/repl/apply_ops_command_info.cpp
@@ -0,0 +1,153 @@
+/**
+ * Copyright (C) 2021-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_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kCommand
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/repl/apply_ops_command_info.h"
+
+#include "mongo/bson/util/bson_extract.h"
+#include "mongo/logv2/log.h"
+#include "mongo/logv2/redaction.h"
+
+namespace mongo {
+namespace repl {
+
+namespace {
+
+/**
+ * Return true iff the applyOpsCmd can be executed in a single WriteUnitOfWork.
+ */
+bool _parseAreOpsCrudOnly(const BSONObj& applyOpCmd) {
+ for (const auto& elem : applyOpCmd.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 'n':
+ case 'u':
+ break;
+ case 'i':
+ break;
+ // Fallthrough.
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+// static
+ApplyOpsCommandInfo ApplyOpsCommandInfo::parse(const BSONObj& applyOpCmd) {
+ try {
+ return ApplyOpsCommandInfo(applyOpCmd);
+ } catch (DBException& ex) {
+ ex.addContext(str::stream() << "Failed to parse applyOps command: " << redact(applyOpCmd));
+ throw;
+ }
+}
+
+bool ApplyOpsCommandInfo::areOpsCrudOnly() const {
+ return _areOpsCrudOnly;
+}
+
+bool ApplyOpsCommandInfo::isAtomic() const {
+ return getAllowAtomic() && areOpsCrudOnly();
+}
+
+ApplyOpsCommandInfo::ApplyOpsCommandInfo(const BSONObj& applyOpCmd)
+ : _areOpsCrudOnly(_parseAreOpsCrudOnly(applyOpCmd)) {
+ parseProtected(IDLParserErrorContext("applyOps"), applyOpCmd);
+
+ if (getPreCondition()) {
+ uassert(ErrorCodes::InvalidOptions,
+ "Cannot use preCondition with {allowAtomic: false}",
+ getAllowAtomic());
+ uassert(ErrorCodes::InvalidOptions,
+ "Cannot use preCondition when operations include commands.",
+ areOpsCrudOnly());
+ }
+}
+
+// static
+std::vector<OplogEntry> ApplyOps::extractOperations(const OplogEntry& applyOpsOplogEntry) {
+ std::vector<OplogEntry> result;
+ extractOperationsTo(applyOpsOplogEntry, applyOpsOplogEntry.getEntry().toBSON(), &result);
+ return result;
+}
+
+// static
+void ApplyOps::extractOperationsTo(const OplogEntry& applyOpsOplogEntry,
+ const BSONObj& topLevelDoc,
+ std::vector<OplogEntry>* operations) {
+ uassert(ErrorCodes::TypeMismatch,
+ str::stream() << "ApplyOps::extractOperations(): not a command: "
+ << redact(applyOpsOplogEntry.toBSONForLogging()),
+ applyOpsOplogEntry.isCommand());
+
+ uassert(ErrorCodes::CommandNotSupported,
+ str::stream() << "ApplyOps::extractOperations(): not applyOps command: "
+ << redact(applyOpsOplogEntry.toBSONForLogging()),
+ OplogEntry::CommandType::kApplyOps == applyOpsOplogEntry.getCommandType());
+
+ auto cmdObj = applyOpsOplogEntry.getOperationToApply();
+ auto info = ApplyOpsCommandInfo::parse(cmdObj);
+ auto operationDocs = info.getOperations();
+ bool alwaysUpsert = info.getAlwaysUpsert() && !applyOpsOplogEntry.getTxnNumber();
+
+ for (const auto& operationDoc : operationDocs) {
+ // Make sure that the inner ops are not malformed or over-specified.
+ ReplOperation::parse(IDLParserErrorContext("extractOperations"), operationDoc);
+
+ BSONObjBuilder builder(operationDoc);
+
+ // Oplog entries can have an oddly-named "b" field for "upsert". MongoDB stopped creating
+ // such entries in 4.0, but we can use the "b" field for the extracted entry here.
+ if (alwaysUpsert && !operationDoc.hasField("b")) {
+ builder.append("b", true);
+ }
+
+ builder.appendElementsUnique(topLevelDoc);
+ auto operation = builder.obj();
+
+ operations->emplace_back(operation);
+ }
+}
+
+} // namespace repl
+} // namespace mongo
diff --git a/src/mongo/db/repl/apply_ops_command_info.h b/src/mongo/db/repl/apply_ops_command_info.h
new file mode 100644
index 00000000000..0773a25231a
--- /dev/null
+++ b/src/mongo/db/repl/apply_ops_command_info.h
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2021-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 <vector>
+
+#include "mongo/base/status.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/db/repl/apply_ops_gen.h"
+#include "mongo/db/repl/multiapplier.h"
+#include "mongo/db/repl/oplog.h"
+#include "mongo/db/repl/oplog_entry.h"
+
+namespace mongo {
+class BSONObjBuilder;
+class OperationContext;
+
+namespace repl {
+class ApplyOps {
+public:
+ static constexpr StringData kPreconditionFieldName = "preCondition"_sd;
+ static constexpr StringData kOplogApplicationModeFieldName = "oplogApplicationMode"_sd;
+
+ /**
+ * Extracts CRUD operations from an atomic applyOps oplog entry.
+ * Throws UserException on error.
+ */
+ static std::vector<OplogEntry> extractOperations(const OplogEntry& applyOpsOplogEntry);
+
+ /**
+ * This variant allows optimization for extracting multiple applyOps operations. The entry for
+ * the non-DurableReplOperation fields of the extracted operation must be specified as
+ * 'topLevelDoc', and need not be any of the applyOps operations. The 'topLevelDoc' entry's
+ * 'ts' field will be used as the 'ts' field for each operation.
+ */
+ static void extractOperationsTo(const OplogEntry& applyOpsOplogEntry,
+ const BSONObj& topLevelDoc,
+ std::vector<OplogEntry>* operations);
+};
+
+/**
+ * Holds information about an applyOps command object.
+ */
+class ApplyOpsCommandInfo : public ApplyOpsCommandInfoBase {
+public:
+ /**
+ * Parses the object in the 'o' field of an applyOps command.
+ * May throw UserException.
+ */
+ static ApplyOpsCommandInfo parse(const BSONObj& applyOpCmd);
+
+ /**
+ * Returns true if all operations described by this applyOps command are CRUD only.
+ */
+ bool areOpsCrudOnly() const;
+
+ /**
+ * Returns true if applyOps will try to process all operations in a single batch atomically.
+ * Derived from getAllowAtomic() and areOpsCrudOnly().
+ */
+ bool isAtomic() const;
+
+private:
+ explicit ApplyOpsCommandInfo(const BSONObj& applyOpCmd);
+
+ const bool _areOpsCrudOnly;
+};
+
+} // namespace repl
+} // namespace mongo