diff options
-rw-r--r-- | src/mongo/base/error_codes.err | 1 | ||||
-rw-r--r-- | src/mongo/db/s/chunk_move_write_concern_options.cpp | 58 | ||||
-rw-r--r-- | src/mongo/db/s/chunk_move_write_concern_options.h | 43 | ||||
-rw-r--r-- | src/mongo/db/s/cleanup_orphaned_cmd.cpp | 44 | ||||
-rw-r--r-- | src/mongo/db/s/move_chunk_command.cpp | 20 | ||||
-rw-r--r-- | src/mongo/db/write_concern_options.cpp | 62 | ||||
-rw-r--r-- | src/mongo/db/write_concern_options.h | 29 | ||||
-rw-r--r-- | src/mongo/s/SConscript | 15 | ||||
-rw-r--r-- | src/mongo/s/chunk_diff_test.cpp | 5 | ||||
-rw-r--r-- | src/mongo/s/chunk_version.h | 1 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_move_chunk_cmd.cpp | 18 | ||||
-rw-r--r-- | src/mongo/s/d_migrate.cpp | 8 | ||||
-rw-r--r-- | src/mongo/s/migration_secondary_throttle_options.cpp | 147 | ||||
-rw-r--r-- | src/mongo/s/migration_secondary_throttle_options.h | 127 | ||||
-rw-r--r-- | src/mongo/s/migration_secondary_throttle_options_test.cpp | 137 |
15 files changed, 502 insertions, 213 deletions
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err index 43fdefa1c50..d541dfa42d5 100644 --- a/src/mongo/base/error_codes.err +++ b/src/mongo/base/error_codes.err @@ -98,7 +98,6 @@ error_code("NotSecondary", 95) error_code("OperationFailed", 96) error_code("NoProjectionFound", 97) error_code("DBPathInUse", 98) -error_code("WriteConcernNotDefined", 99) error_code("CannotSatisfyWriteConcern", 100) error_code("OutdatedClient", 101) error_code("IncompatibleAuditMetadata", 102) diff --git a/src/mongo/db/s/chunk_move_write_concern_options.cpp b/src/mongo/db/s/chunk_move_write_concern_options.cpp index c417ac0e6f1..4005e50106c 100644 --- a/src/mongo/db/s/chunk_move_write_concern_options.cpp +++ b/src/mongo/db/s/chunk_move_write_concern_options.cpp @@ -33,74 +33,72 @@ #include "mongo/db/s/chunk_move_write_concern_options.h" #include "mongo/base/status_with.h" -#include "mongo/bson/util/bson_extract.h" #include "mongo/db/repl/replication_coordinator_global.h" +#include "mongo/s/migration_secondary_throttle_options.h" #include "mongo/util/log.h" namespace mongo { namespace { -const int kDefaultWriteTimeoutForMigrationMs = 60 * 1000; -const WriteConcernOptions DefaultWriteConcernForMigration(2, - WriteConcernOptions::SyncMode::NONE, - kDefaultWriteTimeoutForMigrationMs); +const Seconds kDefaultWriteTimeoutForMigration(60); +const WriteConcernOptions kDefaultWriteConcernForMigration(2, + WriteConcernOptions::SyncMode::NONE, + kDefaultWriteTimeoutForMigration); +const WriteConcernOptions kWriteConcernLocal(1, + WriteConcernOptions::SyncMode::NONE, + WriteConcernOptions::kNoTimeout); WriteConcernOptions getDefaultWriteConcernForMigration() { repl::ReplicationCoordinator* replCoordinator = repl::getGlobalReplicationCoordinator(); if (replCoordinator->getReplicationMode() == mongo::repl::ReplicationCoordinator::modeReplSet) { Status status = - replCoordinator->checkIfWriteConcernCanBeSatisfied(DefaultWriteConcernForMigration); + replCoordinator->checkIfWriteConcernCanBeSatisfied(kDefaultWriteConcernForMigration); if (status.isOK()) { - return DefaultWriteConcernForMigration; + return kDefaultWriteConcernForMigration; } } - return WriteConcernOptions(1, WriteConcernOptions::SyncMode::NONE, 0); + return kWriteConcernLocal; } } // namespace -ChunkMoveWriteConcernOptions::ChunkMoveWriteConcernOptions(BSONObj secThrottleObj, - WriteConcernOptions writeConcernOptions) - : _secThrottleObj(std::move(secThrottleObj)), - _writeConcernOptions(std::move(writeConcernOptions)) {} +StatusWith<WriteConcernOptions> ChunkMoveWriteConcernOptions::getEffectiveWriteConcern( + const MigrationSecondaryThrottleOptions& options) { + if (options.getSecondaryThrottle() == MigrationSecondaryThrottleOptions::kOff) { + return kWriteConcernLocal; + } -StatusWith<ChunkMoveWriteConcernOptions> ChunkMoveWriteConcernOptions::initFromCommand( - const BSONObj& obj) { - BSONObj secThrottleObj; - WriteConcernOptions writeConcernOptions; + WriteConcernOptions writeConcern; - Status status = writeConcernOptions.parseSecondaryThrottle(obj, &secThrottleObj); - if (!status.isOK()) { - if (status.code() != ErrorCodes::WriteConcernNotDefined) { - return status; - } + if (options.isWriteConcernSpecified()) { + writeConcern = options.getWriteConcern(); - writeConcernOptions = getDefaultWriteConcernForMigration(); - } else { repl::ReplicationCoordinator* replCoordinator = repl::getGlobalReplicationCoordinator(); if (replCoordinator->getReplicationMode() == repl::ReplicationCoordinator::modeMasterSlave && - writeConcernOptions.shouldWaitForOtherNodes()) { + writeConcern.shouldWaitForOtherNodes()) { warning() << "moveChunk cannot check if secondary throttle setting " - << writeConcernOptions.toBSON() + << writeConcern.toBSON() << " can be enforced in a master slave configuration"; } - Status status = replCoordinator->checkIfWriteConcernCanBeSatisfied(writeConcernOptions); + Status status = replCoordinator->checkIfWriteConcernCanBeSatisfied(writeConcern); if (!status.isOK() && status != ErrorCodes::NoReplicationEnabled) { return status; } + } else { + writeConcern = getDefaultWriteConcernForMigration(); } - if (writeConcernOptions.shouldWaitForOtherNodes() && - writeConcernOptions.wTimeout == WriteConcernOptions::kNoTimeout) { + if (writeConcern.shouldWaitForOtherNodes() && + writeConcern.wTimeout == WriteConcernOptions::kNoTimeout) { // Don't allow no timeout - writeConcernOptions.wTimeout = kDefaultWriteTimeoutForMigrationMs; + writeConcern.wTimeout = durationCount<Milliseconds>(kDefaultWriteTimeoutForMigration); } - return ChunkMoveWriteConcernOptions(secThrottleObj, writeConcernOptions); + return writeConcern; } } // namespace mongo diff --git a/src/mongo/db/s/chunk_move_write_concern_options.h b/src/mongo/db/s/chunk_move_write_concern_options.h index 7258e093198..1ff445d9894 100644 --- a/src/mongo/db/s/chunk_move_write_concern_options.h +++ b/src/mongo/db/s/chunk_move_write_concern_options.h @@ -32,8 +32,7 @@ namespace mongo { -class BSONObj; -class BSONObjBuilder; +class MigrationSecondaryThrottleOptions; template <typename T> class StatusWith; @@ -44,31 +43,23 @@ class StatusWith; class ChunkMoveWriteConcernOptions { public: /** - * Parses the chunk move options expecting the format, which mongod should be getting and if - * none is available, assigns defaults. + * Based on the type of the server (standalone or replica set) and the requested secondary + * throttle options returns what write concern should be used locally both for writing migrated + * documents and for performing range deletions. + * + * Returns a non-OK status if the requested write concern cannot be satisfied for some reason. + * + * These are the rules for determining the local write concern to be used: + * - secondaryThrottle is not specified (kDefault) or it is on (kOn), but there is no custom + * write concern: + * - if replication is enabled and there are 2 or more nodes - w:2, j:false, timeout:60000 + * - if replication is not enabled or less than 2 nodes - w:1, j:false, timeout:0 + * - secondaryThrottle is off (kOff): w:1, j:false, timeout:0 + * - secondaryThrottle is on (kOn) and there is custom write concern, use the custom write + * concern. */ - static StatusWith<ChunkMoveWriteConcernOptions> initFromCommand(const BSONObj& obj); - - /** - * Returns the throttle options to be used when committing migrated documents on the recipient - * shard's seconary. - */ - const BSONObj& getSecThrottle() const { - return _secThrottleObj; - } - - /** - * Returns the write concern options. - */ - const WriteConcernOptions& getWriteConcern() const { - return _writeConcernOptions; - } - -private: - ChunkMoveWriteConcernOptions(BSONObj secThrottleObj, WriteConcernOptions writeConcernOptions); - - BSONObj _secThrottleObj; - WriteConcernOptions _writeConcernOptions; + static StatusWith<WriteConcernOptions> getEffectiveWriteConcern( + const MigrationSecondaryThrottleOptions& options); }; } // namespace mongo diff --git a/src/mongo/db/s/cleanup_orphaned_cmd.cpp b/src/mongo/db/s/cleanup_orphaned_cmd.cpp index e3d36168a1f..0694335b79d 100644 --- a/src/mongo/db/s/cleanup_orphaned_cmd.cpp +++ b/src/mongo/db/s/cleanup_orphaned_cmd.cpp @@ -47,6 +47,8 @@ #include "mongo/db/service_context.h" #include "mongo/db/s/collection_metadata.h" #include "mongo/db/s/sharding_state.h" +#include "mongo/db/s/chunk_move_write_concern_options.h" +#include "mongo/s/migration_secondary_throttle_options.h" #include "mongo/util/log.h" namespace mongo { @@ -56,11 +58,6 @@ using str::stream; namespace { -const int kDefaultWTimeoutMs = 60 * 1000; -const WriteConcernOptions DefaultWriteConcern(WriteConcernOptions::kMajority, - WriteConcernOptions::SyncMode::UNSET, - kDefaultWTimeoutMs); - enum CleanupResult { CleanupResult_Done, CleanupResult_Continue, CleanupResult_Error }; /** @@ -228,37 +225,10 @@ public: return false; } - WriteConcernOptions writeConcern; - Status status = writeConcern.parseSecondaryThrottle(cmdObj, NULL); - - if (!status.isOK()) { - if (status.code() != ErrorCodes::WriteConcernNotDefined) { - return appendCommandStatus(result, status); - } - - writeConcern = DefaultWriteConcern; - } else { - repl::ReplicationCoordinator* replCoordinator = repl::getGlobalReplicationCoordinator(); - Status status = replCoordinator->checkIfWriteConcernCanBeSatisfied(writeConcern); - - if (replCoordinator->getReplicationMode() == - repl::ReplicationCoordinator::modeMasterSlave && - writeConcern.shouldWaitForOtherNodes()) { - warning() << "cleanupOrphaned cannot check if write concern setting " - << writeConcern.toBSON() - << " can be enforced in a master slave configuration"; - } - - if (!status.isOK() && status != ErrorCodes::NoReplicationEnabled) { - return appendCommandStatus(result, status); - } - } - - if (writeConcern.shouldWaitForOtherNodes() && - writeConcern.wTimeout == WriteConcernOptions::kNoTimeout) { - // Don't allow no timeout. - writeConcern.wTimeout = kDefaultWTimeoutMs; - } + const auto secondaryThrottle = + uassertStatusOK(MigrationSecondaryThrottleOptions::createFromCommand(cmdObj)); + const auto writeConcern = uassertStatusOK( + ChunkMoveWriteConcernOptions::getEffectiveWriteConcern(secondaryThrottle)); ShardingState* const shardingState = ShardingState::get(txn); @@ -269,7 +239,7 @@ public: } ChunkVersion shardVersion; - status = shardingState->refreshMetadataNow(txn, ns, &shardVersion); + Status status = shardingState->refreshMetadataNow(txn, ns, &shardVersion); if (!status.isOK()) { if (status.code() == ErrorCodes::RemoteChangeDetected) { warning() << "Shard version in transition detected while refreshing " diff --git a/src/mongo/db/s/move_chunk_command.cpp b/src/mongo/db/s/move_chunk_command.cpp index 79bf24b320c..9fdc8114f45 100644 --- a/src/mongo/db/s/move_chunk_command.cpp +++ b/src/mongo/db/s/move_chunk_command.cpp @@ -47,6 +47,7 @@ #include "mongo/s/client/shard_connection.h" #include "mongo/s/chunk_version.h" #include "mongo/s/grid.h" +#include "mongo/s/migration_secondary_throttle_options.h" #include "mongo/util/fail_point_service.h" #include "mongo/util/log.h" @@ -178,10 +179,10 @@ public: // Initialize our current shard name in the shard state if needed shardingState->setShardName(chunkMoveState.getFromShard()); - const auto moveWriteConcernOptions = - uassertStatusOK(ChunkMoveWriteConcernOptions::initFromCommand(cmdObj)); - const auto& secThrottleObj = moveWriteConcernOptions.getSecThrottle(); - const auto& writeConcern = moveWriteConcernOptions.getWriteConcern(); + const auto secondaryThrottle = + uassertStatusOK(MigrationSecondaryThrottleOptions::createFromCommand(cmdObj)); + const auto writeConcernForRangeDeleter = uassertStatusOK( + ChunkMoveWriteConcernOptions::getEffectiveWriteConcern(secondaryThrottle)); // Do inline deletion bool waitForDelete = cmdObj["waitForDelete"].trueValue(); @@ -267,8 +268,6 @@ public: return false; } - const bool isSecondaryThrottle(writeConcern.shouldWaitForOtherNodes()); - BSONObjBuilder recvChunkStartBuilder; recvChunkStartBuilder.append("_recvChunkStart", ns); migrationSessionId.append(&recvChunkStartBuilder); @@ -280,12 +279,7 @@ public: recvChunkStartBuilder.append("shardKeyPattern", shardKeyPattern); recvChunkStartBuilder.append("configServer", shardingState->getConfigServer(txn).toString()); - recvChunkStartBuilder.append("secondaryThrottle", isSecondaryThrottle); - - // Follow the same convention in moveChunk. - if (isSecondaryThrottle && !secThrottleObj.isEmpty()) { - recvChunkStartBuilder.append("writeConcern", secThrottleObj); - } + secondaryThrottle.append(&recvChunkStartBuilder); BSONObj res; @@ -481,7 +475,7 @@ public: chunkMoveState.getMinKey().getOwned(), chunkMoveState.getMaxKey().getOwned(), shardKeyPattern)); - deleterOptions.writeConcern = writeConcern; + deleterOptions.writeConcern = writeConcernForRangeDeleter; deleterOptions.waitForOpenCursors = true; deleterOptions.fromMigrate = true; deleterOptions.onlyRemoveOrphanedDocs = true; diff --git a/src/mongo/db/write_concern_options.cpp b/src/mongo/db/write_concern_options.cpp index 58af7b36a9d..d0f003fb383 100644 --- a/src/mongo/db/write_concern_options.cpp +++ b/src/mongo/db/write_concern_options.cpp @@ -46,12 +46,11 @@ namespace { */ enum WriteConcern { W_NONE = 0, W_NORMAL = 1 }; -const BSONField<bool> mongosSecondaryThrottleField("_secondaryThrottle", true); -const BSONField<bool> secondaryThrottleField("secondaryThrottle", true); -const BSONField<BSONObj> writeConcernField("writeConcern"); - } // namespace +const int WriteConcernOptions::kNoTimeout(0); +const int WriteConcernOptions::kNoWaiting(-1); + const char WriteConcernOptions::kMajority[] = "majority"; const BSONObj WriteConcernOptions::Default = BSONObj(); @@ -59,12 +58,14 @@ const BSONObj WriteConcernOptions::Acknowledged(BSON("w" << W_NORMAL)); const BSONObj WriteConcernOptions::Unacknowledged(BSON("w" << W_NONE)); const BSONObj WriteConcernOptions::Majority(BSON("w" << WriteConcernOptions::kMajority)); - WriteConcernOptions::WriteConcernOptions(int numNodes, SyncMode sync, int timeout) - : syncMode(sync), wNumNodes(numNodes), wTimeout(timeout) {} + : WriteConcernOptions(numNodes, sync, Milliseconds(timeout)) {} WriteConcernOptions::WriteConcernOptions(const std::string& mode, SyncMode sync, int timeout) - : syncMode(sync), wNumNodes(0), wMode(mode), wTimeout(timeout) {} + : WriteConcernOptions(mode, sync, Milliseconds(timeout)) {} + +WriteConcernOptions::WriteConcernOptions(int numNodes, SyncMode sync, Milliseconds timeout) + : syncMode(sync), wNumNodes(numNodes), wTimeout(durationCount<Milliseconds>(timeout)) {} WriteConcernOptions::WriteConcernOptions(const std::string& mode, SyncMode sync, @@ -118,53 +119,6 @@ Status WriteConcernOptions::parse(const BSONObj& obj) { return Status::OK(); } -Status WriteConcernOptions::parseSecondaryThrottle(const BSONObj& doc, - BSONObj* rawWriteConcernObj) { - string errMsg; - bool isSecondaryThrottle; - FieldParser::FieldState fieldState = - FieldParser::extract(doc, secondaryThrottleField, &isSecondaryThrottle, &errMsg); - if (fieldState == FieldParser::FIELD_INVALID) { - return Status(ErrorCodes::FailedToParse, errMsg); - } - - if (fieldState != FieldParser::FIELD_SET) { - fieldState = - FieldParser::extract(doc, mongosSecondaryThrottleField, &isSecondaryThrottle, &errMsg); - - if (fieldState == FieldParser::FIELD_INVALID) { - return Status(ErrorCodes::FailedToParse, errMsg); - } - } - - BSONObj dummyBSON; - if (!rawWriteConcernObj) { - rawWriteConcernObj = &dummyBSON; - } - - fieldState = FieldParser::extract(doc, writeConcernField, rawWriteConcernObj, &errMsg); - if (fieldState == FieldParser::FIELD_INVALID) { - return Status(ErrorCodes::FailedToParse, errMsg); - } - - if (!isSecondaryThrottle) { - if (!rawWriteConcernObj->isEmpty()) { - return Status(ErrorCodes::UnsupportedFormat, - "Cannot have write concern when secondary throttle is false"); - } - - wNumNodes = 1; - return Status::OK(); - } - - if (rawWriteConcernObj->isEmpty()) { - return Status(ErrorCodes::WriteConcernNotDefined, - "Secondary throttle is on, but write concern is not specified"); - } - - return parse(*rawWriteConcernObj); -} - BSONObj WriteConcernOptions::toBSON() const { BSONObjBuilder builder; diff --git a/src/mongo/db/write_concern_options.h b/src/mongo/db/write_concern_options.h index 5acc54e5294..91320b0d39a 100644 --- a/src/mongo/db/write_concern_options.h +++ b/src/mongo/db/write_concern_options.h @@ -39,8 +39,8 @@ struct WriteConcernOptions { public: enum class SyncMode { UNSET, NONE, FSYNC, JOURNAL }; - static const int kNoTimeout = 0; - static const int kNoWaiting = -1; + static const int kNoTimeout; + static const int kNoWaiting; static const BSONObj Default; static const BSONObj Acknowledged; @@ -58,6 +58,8 @@ public: WriteConcernOptions(int numNodes, SyncMode sync, int timeout); + WriteConcernOptions(int numNodes, SyncMode sync, Milliseconds timeout); + WriteConcernOptions(const std::string& mode, SyncMode sync, int timeout); WriteConcernOptions(const std::string& mode, SyncMode sync, Milliseconds timeout); @@ -65,26 +67,6 @@ public: Status parse(const BSONObj& obj); /** - * Extracts the write concern settings from the BSONObj. The BSON object should have - * the format: - * - * { - * ... - * secondaryThrottle: <bool>, // optional - * _secondaryThrottle: <bool>, // optional - * writeConcern: <BSONObj> // optional - * } - * - * Note: secondaryThrottle takes precedence over _secondaryThrottle. - * - * Also sets output parameter rawWriteConcernObj if the writeCocnern field exists. - * - * Returns OK if the parse was successful. Also returns ErrorCodes::WriteConcernNotDefined - * when secondary throttle is true but write concern was not specified. - */ - Status parseSecondaryThrottle(const BSONObj& doc, BSONObj* rawWriteConcernObj); - - /** * Return true if the server needs to wait for other secondary nodes to satisfy this * write concern setting. Errs on the false positive for non-empty wMode. */ @@ -117,4 +99,5 @@ public: // Timeout in milliseconds. int wTimeout; }; -} + +} // namespace mongo diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript index 65f8a92be57..50ef4b485b5 100644 --- a/src/mongo/s/SConscript +++ b/src/mongo/s/SConscript @@ -38,11 +38,13 @@ env.Library( source=[ 'chunk_diff.cpp', 'chunk_version.cpp', + 'migration_secondary_throttle_options.cpp', 'set_shard_version_request.cpp', ], LIBDEPS=[ 'catalog/catalog_types', '$BUILD_DIR/mongo/client/connection_string', + '$BUILD_DIR/mongo/db/common', '$BUILD_DIR/mongo/db/query/lite_parsed_query', '$BUILD_DIR/mongo/db/repl/optime', '$BUILD_DIR/mongo/rpc/metadata', @@ -92,23 +94,14 @@ env.CppUnitTest( ], LIBDEPS=[ 'common', - '$BUILD_DIR/mongo/db/service_context', ] ) env.CppUnitTest( - target='chunk_version_test', + target='sharding_request_types_test', source=[ 'chunk_version_test.cpp', - ], - LIBDEPS=[ - 'common', - ] -) - -env.CppUnitTest( - target='set_shard_version_request_test', - source=[ + 'migration_secondary_throttle_options_test.cpp', 'set_shard_version_request_test.cpp', ], LIBDEPS=[ diff --git a/src/mongo/s/chunk_diff_test.cpp b/src/mongo/s/chunk_diff_test.cpp index 7c6bab7e94d..0e7674929fe 100644 --- a/src/mongo/s/chunk_diff_test.cpp +++ b/src/mongo/s/chunk_diff_test.cpp @@ -111,7 +111,6 @@ protected: ~ChunkDiffUnitTest() = default; void runTest(bool isInverse) { - OperationContextNoop txn; int numShards = 10; int numInitialChunks = 5; @@ -177,7 +176,7 @@ protected: convertBSONArrayToChunkTypes(chunks, &chunksVector); // Validate initial load - differ->calculateConfigDiff(&txn, chunksVector); + differ->calculateConfigDiff(nullptr, chunksVector); validate(isInverse, chunksVector, ranges, maxVersion, maxShardVersions); // Generate a lot of diffs, and keep validating that updating from the diffs always gives us @@ -325,7 +324,7 @@ protected: std::vector<ChunkType> chunksVector; convertBSONArrayToChunkTypes(chunks, &chunksVector); - differ->calculateConfigDiff(&txn, chunksVector); + differ->calculateConfigDiff(nullptr, chunksVector); validate(isInverse, chunksVector, ranges, maxVersion, maxShardVersions); } diff --git a/src/mongo/s/chunk_version.h b/src/mongo/s/chunk_version.h index 007e05f7353..cdc266fd613 100644 --- a/src/mongo/s/chunk_version.h +++ b/src/mongo/s/chunk_version.h @@ -29,7 +29,6 @@ #pragma once #include "mongo/db/jsobj.h" -#include "mongo/s/optime_pair.h" namespace mongo { diff --git a/src/mongo/s/commands/cluster_move_chunk_cmd.cpp b/src/mongo/s/commands/cluster_move_chunk_cmd.cpp index a4b1efc62c6..62a08176f43 100644 --- a/src/mongo/s/commands/cluster_move_chunk_cmd.cpp +++ b/src/mongo/s/commands/cluster_move_chunk_cmd.cpp @@ -44,6 +44,7 @@ #include "mongo/s/client/shard_registry.h" #include "mongo/s/config.h" #include "mongo/s/grid.h" +#include "mongo/s/migration_secondary_throttle_options.h" #include "mongo/util/log.h" #include "mongo/util/timer.h" @@ -224,18 +225,13 @@ public: return false; } - unique_ptr<WriteConcernOptions> writeConcern(new WriteConcernOptions()); + const auto secondaryThrottle = + uassertStatusOK(MigrationSecondaryThrottleOptions::createFromCommand(cmdObj)); - Status status = writeConcern->parseSecondaryThrottle(cmdObj, NULL); - if (!status.isOK()) { - if (status.code() != ErrorCodes::WriteConcernNotDefined) { - errmsg = status.toString(); - return false; - } - - // Let the shard decide what write concern to use. - writeConcern.reset(); - } + const unique_ptr<WriteConcernOptions> writeConcern( + secondaryThrottle.isWriteConcernSpecified() + ? new WriteConcernOptions(secondaryThrottle.getWriteConcern()) + : nullptr); BSONObj res; if (!chunk->moveAndCommit(txn, diff --git a/src/mongo/s/d_migrate.cpp b/src/mongo/s/d_migrate.cpp index 552c853fbb7..e591735a525 100644 --- a/src/mongo/s/d_migrate.cpp +++ b/src/mongo/s/d_migrate.cpp @@ -50,6 +50,7 @@ #include "mongo/db/s/migration_impl.h" #include "mongo/db/s/sharding_state.h" #include "mongo/s/chunk_version.h" +#include "mongo/s/migration_secondary_throttle_options.h" #include "mongo/util/assert_util.h" #include "mongo/util/log.h" @@ -274,9 +275,10 @@ public: } // Process secondary throttle settings and assign defaults if necessary. - const auto moveWriteConcernOptions = - uassertStatusOK(ChunkMoveWriteConcernOptions::initFromCommand(cmdObj)); - const auto& writeConcern = moveWriteConcernOptions.getWriteConcern(); + const auto secondaryThrottle = + uassertStatusOK(MigrationSecondaryThrottleOptions::createFromCommand(cmdObj)); + const auto writeConcern = uassertStatusOK( + ChunkMoveWriteConcernOptions::getEffectiveWriteConcern(secondaryThrottle)); BSONObj shardKeyPattern = cmdObj["shardKeyPattern"].Obj().getOwned(); diff --git a/src/mongo/s/migration_secondary_throttle_options.cpp b/src/mongo/s/migration_secondary_throttle_options.cpp new file mode 100644 index 00000000000..870daf767d3 --- /dev/null +++ b/src/mongo/s/migration_secondary_throttle_options.cpp @@ -0,0 +1,147 @@ +/** + * Copyright (C) 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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/s/migration_secondary_throttle_options.h" + +#include "mongo/base/status_with.h" +#include "mongo/bson/util/bson_extract.h" +#include "mongo/db/write_concern_options.h" + +namespace mongo { +namespace { + +const char kSecondaryThrottleMongos[] = "_secondaryThrottle"; +const char kSecondaryThrottleMongod[] = "secondaryThrottle"; +const char kWriteConcern[] = "writeConcern"; + +} // namespace + +MigrationSecondaryThrottleOptions::MigrationSecondaryThrottleOptions( + SecondaryThrottleOption secondaryThrottle, boost::optional<BSONObj> writeConcernBSON) + : _secondaryThrottle(secondaryThrottle), _writeConcernBSON(std::move(writeConcernBSON)) {} + +MigrationSecondaryThrottleOptions MigrationSecondaryThrottleOptions::create( + SecondaryThrottleOption option) { + return MigrationSecondaryThrottleOptions(option, boost::none); +} + +MigrationSecondaryThrottleOptions MigrationSecondaryThrottleOptions::createWithWriteConcern( + const WriteConcernOptions& writeConcern) { + // Optimize on write concern, which makes no difference + if (writeConcern.wNumNodes <= 1 && writeConcern.wMode.empty()) { + return MigrationSecondaryThrottleOptions(kOff, boost::none); + } + + return MigrationSecondaryThrottleOptions(kOn, writeConcern.toBSON()); +} + +StatusWith<MigrationSecondaryThrottleOptions> MigrationSecondaryThrottleOptions::createFromCommand( + const BSONObj& obj) { + SecondaryThrottleOption secondaryThrottle; + boost::optional<BSONObj> writeConcernBSON; + + // Parse the two variants of the 'secondaryThrottle' option + { + bool isSecondaryThrottle; + + Status status = + bsonExtractBooleanField(obj, kSecondaryThrottleMongod, &isSecondaryThrottle); + if (status == ErrorCodes::NoSuchKey) { + status = bsonExtractBooleanField(obj, kSecondaryThrottleMongos, &isSecondaryThrottle); + } + + if (status == ErrorCodes::NoSuchKey) { + secondaryThrottle = kDefault; + } else if (status.isOK()) { + secondaryThrottle = (isSecondaryThrottle ? kOn : kOff); + } else { + return status; + } + } + + // Extract the requested 'writeConcern' option + { + BSONElement writeConcernElem; + Status status = bsonExtractField(obj, kWriteConcern, &writeConcernElem); + if (status == ErrorCodes::NoSuchKey) { + return MigrationSecondaryThrottleOptions(secondaryThrottle, boost::none); + } else if (!status.isOK()) { + return status; + } + + if (secondaryThrottle != kOn) { + return Status(ErrorCodes::UnsupportedFormat, + "Cannot specify write concern when secondaryThrottle is not set"); + } + + writeConcernBSON = writeConcernElem.Obj().getOwned(); + } + + invariant(writeConcernBSON.is_initialized()); + + // Make sure the write concern parses correctly + WriteConcernOptions writeConcern; + Status status = writeConcern.parse(*writeConcernBSON); + if (!status.isOK()) { + return status; + } + + return MigrationSecondaryThrottleOptions(secondaryThrottle, std::move(writeConcernBSON)); +} + +WriteConcernOptions MigrationSecondaryThrottleOptions::getWriteConcern() const { + invariant(_secondaryThrottle != kOff); + invariant(_writeConcernBSON); + + WriteConcernOptions writeConcern; + fassertStatusOK(34414, writeConcern.parse(*_writeConcernBSON)); + + return writeConcern; +} + +void MigrationSecondaryThrottleOptions::append(BSONObjBuilder* builder) const { + if (_secondaryThrottle == kDefault) { + return; + } + + builder->appendBool(kSecondaryThrottleMongod, _secondaryThrottle == kOn); + + if (_secondaryThrottle == kOn && _writeConcernBSON) { + builder->append(kWriteConcern, *_writeConcernBSON); + } +} + +BSONObj MigrationSecondaryThrottleOptions::toBSON() const { + BSONObjBuilder builder; + append(&builder); + return builder.obj(); +} + +} // namespace mongo diff --git a/src/mongo/s/migration_secondary_throttle_options.h b/src/mongo/s/migration_secondary_throttle_options.h new file mode 100644 index 00000000000..b4f4cbe066a --- /dev/null +++ b/src/mongo/s/migration_secondary_throttle_options.h @@ -0,0 +1,127 @@ +/** + * Copyright (C) 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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. + */ + +#pragma once + +#include <boost/optional.hpp> + +#include "mongo/bson/bsonobj.h" + +namespace mongo { + +class BSONObjBuilder; +template <typename T> +class StatusWith; +struct WriteConcernOptions; + +/** + * Returns the default write concern for migration cleanup on the donor shard and for cloning + * documents on the destination shard. + */ +class MigrationSecondaryThrottleOptions { +public: + enum SecondaryThrottleOption { + // The secondary throttle option is not set explicitly. Use the default for the service. + kDefault, + + // The secondary throttle option is explicitly disabled. + kOff, + + // The secondary throttle option is explicitly enabled and there potentially might be a + // write concern specified. If no write concern was specified, use the default. + kOn + }; + + /** + * Constructs an object with the specified secondary throttle option and no custom write + * concern. + */ + static MigrationSecondaryThrottleOptions create(SecondaryThrottleOption option); + + /** + * Constructs an object with the secondary throttle enabled and with the specified write + * concern. + */ + static MigrationSecondaryThrottleOptions createWithWriteConcern( + const WriteConcernOptions& writeConcern); + + /** + * Extracts the write concern settings from a BSON with the following format: + * + * { + * secondaryThrottle: <bool>, // optional + * _secondaryThrottle: <bool>, // optional + * writeConcern: <BSONObj> // optional + * } + * + * Note: secondaryThrottle takes precedence over _secondaryThrottle. If either of the two are + * missing, the secondaryThrottle enabled status defaults to true. + * + * Returns OK if the parse was successful. + */ + static StatusWith<MigrationSecondaryThrottleOptions> createFromCommand(const BSONObj& obj); + + /** + * Returns the selected secondary throttle option. + */ + SecondaryThrottleOption getSecondaryThrottle() const { + return _secondaryThrottle; + } + + /** + * Returns whether secondary throttle is enabled and write concern was requested. + */ + bool isWriteConcernSpecified() const { + return _writeConcernBSON.is_initialized(); + } + + /** + * Returns the custom write concern, which was requested. Must only be called if + * isWriteConcernSpecified returns true. + */ + WriteConcernOptions getWriteConcern() const; + + /** + * Returns a BSON representation of the current secondary throttle settongs. + */ + void append(BSONObjBuilder* builder) const; + BSONObj toBSON() const; + +private: + MigrationSecondaryThrottleOptions(SecondaryThrottleOption secondaryThrottle, + boost::optional<BSONObj> writeConcernBSON); + + // What is the state of the secondaryThrottle option (kDefault means that it has not been set) + SecondaryThrottleOption _secondaryThrottle; + + // Owned BSON object with the contents of the writeConcern. If this object is set, then + // secodaryThrottle must be true. + boost::optional<BSONObj> _writeConcernBSON; +}; + +} // namespace mongo diff --git a/src/mongo/s/migration_secondary_throttle_options_test.cpp b/src/mongo/s/migration_secondary_throttle_options_test.cpp new file mode 100644 index 00000000000..ea76fb28f06 --- /dev/null +++ b/src/mongo/s/migration_secondary_throttle_options_test.cpp @@ -0,0 +1,137 @@ +/** + * Copyright (C) 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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::kSharding + +#include "mongo/platform/basic.h" + +#include "mongo/bson/bsonmisc.h" +#include "mongo/db/write_concern_options.h" +#include "mongo/s/migration_secondary_throttle_options.h" +#include "mongo/unittest/unittest.h" +#include "mongo/util/log.h" + +namespace mongo { + +using unittest::assertGet; + +namespace { + +TEST(MigrationSecondaryThrottleOptions, CreateDefault) { + MigrationSecondaryThrottleOptions options = + MigrationSecondaryThrottleOptions::create(MigrationSecondaryThrottleOptions::kDefault); + ASSERT_EQ(MigrationSecondaryThrottleOptions::kDefault, options.getSecondaryThrottle()); + ASSERT(!options.isWriteConcernSpecified()); + ASSERT_EQUALS(BSONObj(), options.toBSON()); +} + +TEST(MigrationSecondaryThrottleOptions, CreateOn) { + MigrationSecondaryThrottleOptions options = + MigrationSecondaryThrottleOptions::create(MigrationSecondaryThrottleOptions::kOn); + ASSERT_EQ(MigrationSecondaryThrottleOptions::kOn, options.getSecondaryThrottle()); + ASSERT(!options.isWriteConcernSpecified()); + ASSERT_EQUALS(BSON("secondaryThrottle" << true), options.toBSON()); +} + +TEST(MigrationSecondaryThrottleOptions, CreateOff) { + MigrationSecondaryThrottleOptions options = + MigrationSecondaryThrottleOptions::create(MigrationSecondaryThrottleOptions::kOff); + ASSERT_EQ(MigrationSecondaryThrottleOptions::kOff, options.getSecondaryThrottle()); + ASSERT(!options.isWriteConcernSpecified()); + ASSERT_EQUALS(BSON("secondaryThrottle" << false), options.toBSON()); +} + +TEST(MigrationSecondaryThrottleOptions, NotSpecifiedInCommandBSON) { + MigrationSecondaryThrottleOptions options = assertGet( + MigrationSecondaryThrottleOptions::createFromCommand(BSON("someOtherField" << 1))); + ASSERT_EQ(MigrationSecondaryThrottleOptions::kDefault, options.getSecondaryThrottle()); + ASSERT_EQUALS(BSONObj(), options.toBSON()); +} + +TEST(MigrationSecondaryThrottleOptions, EnabledInCommandBSONWithoutWriteConcern) { + MigrationSecondaryThrottleOptions options = + assertGet(MigrationSecondaryThrottleOptions::createFromCommand( + BSON("someOtherField" << 1 << "secondaryThrottle" << true))); + ASSERT_EQ(MigrationSecondaryThrottleOptions::kOn, options.getSecondaryThrottle()); + ASSERT(!options.isWriteConcernSpecified()); +} + +TEST(MigrationSecondaryThrottleOptions, EnabledInCommandBSONWithSimpleWriteConcern) { + MigrationSecondaryThrottleOptions options = + assertGet(MigrationSecondaryThrottleOptions::createFromCommand( + BSON("someOtherField" << 1 << "secondaryThrottle" << true << "writeConcern" + << BSON("w" << 2)))); + ASSERT_EQ(MigrationSecondaryThrottleOptions::kOn, options.getSecondaryThrottle()); + ASSERT(options.isWriteConcernSpecified()); + + WriteConcernOptions writeConcern = options.getWriteConcern(); + ASSERT_EQ(2, writeConcern.wNumNodes); + ASSERT_EQ(static_cast<int>(WriteConcernOptions::SyncMode::UNSET), + static_cast<int>(writeConcern.syncMode)); + ASSERT_EQ(WriteConcernOptions::kNoTimeout, writeConcern.wTimeout); +} + +TEST(MigrationSecondaryThrottleOptions, EnabledInCommandBSONWithCompleteWriteConcern) { + MigrationSecondaryThrottleOptions options = + assertGet(MigrationSecondaryThrottleOptions::createFromCommand( + BSON("someOtherField" << 1 << "secondaryThrottle" << true << "writeConcern" + << BSON("w" << 3 << "j" << true)))); + ASSERT_EQ(MigrationSecondaryThrottleOptions::kOn, options.getSecondaryThrottle()); + ASSERT(options.isWriteConcernSpecified()); + + WriteConcernOptions writeConcern = options.getWriteConcern(); + ASSERT_EQ(3, writeConcern.wNumNodes); + ASSERT_EQ(static_cast<int>(WriteConcernOptions::SyncMode::JOURNAL), + static_cast<int>(writeConcern.syncMode)); + ASSERT_EQ(WriteConcernOptions::kNoTimeout, writeConcern.wTimeout); +} + +TEST(MigrationSecondaryThrottleOptions, DisabledInCommandBSON) { + MigrationSecondaryThrottleOptions options = + assertGet(MigrationSecondaryThrottleOptions::createFromCommand( + BSON("someOtherField" << 1 << "secondaryThrottle" << false))); + ASSERT_EQ(MigrationSecondaryThrottleOptions::kOff, options.getSecondaryThrottle()); +} + +TEST(MigrationSecondaryThrottleOptions, ParseFailsDisabledInCommandBSONWriteConcernSpecified) { + auto status = MigrationSecondaryThrottleOptions::createFromCommand( + BSON("someOtherField" << 1 << "secondaryThrottle" << false << "writeConcern" + << BSON("w" + << "majority"))); + ASSERT_EQ(ErrorCodes::UnsupportedFormat, status.getStatus().code()); +} + +TEST(MigrationSecondaryThrottleOptions, ParseFailsNotSpecifiedInCommandBSONWriteConcernSpecified) { + auto status = MigrationSecondaryThrottleOptions::createFromCommand( + BSON("someOtherField" << 1 << "writeConcern" << BSON("w" + << "majority"))); + ASSERT_EQ(ErrorCodes::UnsupportedFormat, status.getStatus().code()); +} + +} // namespace +} // namespace mongo |