diff options
author | Cheahuychou Mao <mao.cheahuychou@gmail.com> | 2021-10-28 14:31:44 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-10-29 00:50:10 +0000 |
commit | 4e04c860df75edf6b8bd1de205de0f379f67ea25 (patch) | |
tree | 88102b08ca43ce3fe9cbf40c24babac01eee5125 /src/mongo | |
parent | ea197fd5193fe034174584e60290dd90fe01d2b1 (diff) | |
download | mongo-4e04c860df75edf6b8bd1de205de0f379f67ea25.tar.gz |
SERVER-58756 Move ApplyOpsCommandInfo into a separate library
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/repl/SConscript | 14 | ||||
-rw-r--r-- | src/mongo/db/repl/apply_ops.cpp | 105 | ||||
-rw-r--r-- | src/mongo/db/repl/apply_ops.h | 51 | ||||
-rw-r--r-- | src/mongo/db/repl/apply_ops_command_info.cpp | 153 | ||||
-rw-r--r-- | src/mongo/db/repl/apply_ops_command_info.h | 95 |
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 |