diff options
author | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2022-04-11 12:21:14 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-04-11 15:18:20 +0000 |
commit | 481cdc722a1516d094743f58d2fbf61575668e3c (patch) | |
tree | ff8960633132ba7b30ffdeed63440a3ca5cb6480 /src/mongo | |
parent | 327a61632913d0f943ab0062611714b6552dbc0b (diff) | |
download | mongo-481cdc722a1516d094743f58d2fbf61575668e3c.tar.gz |
SERVER-65204 Add Timestamp (in addition to Epoch) on the complete split path
Diffstat (limited to 'src/mongo')
30 files changed, 177 insertions, 994 deletions
diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript index bf63a589862..892a2fee04c 100644 --- a/src/mongo/db/s/SConscript +++ b/src/mongo/db/s/SConscript @@ -268,6 +268,7 @@ env.Library( 'sharding_config_server_parameters.idl', 'sharding_ddl_util.cpp', 'sharding_util.cpp', + 'split_chunk_request_type.cpp', 'type_lockpings.cpp', 'type_locks.cpp', 'type_shard_identity.cpp', @@ -550,6 +551,7 @@ env.CppUnitTest( 'migration_util_test.cpp', 'namespace_metadata_change_notifications_test.cpp', 'op_observer_sharding_test.cpp', + 'operation_sharding_state_test.cpp', 'persistent_task_queue_test.cpp', 'range_deletion_util_test.cpp', 'resharding/resharding_agg_test.cpp', @@ -573,8 +575,8 @@ env.CppUnitTest( 'resharding/resharding_txn_cloner_test.cpp', 'session_catalog_migration_destination_test.cpp', 'session_catalog_migration_source_test.cpp', - 'shard_local_test.cpp', 'shard_key_index_util_test.cpp', + 'shard_local_test.cpp', 'shard_metadata_util_test.cpp', 'shard_server_catalog_cache_loader_test.cpp', 'sharding_data_transform_cumulative_metrics_test.cpp', @@ -582,6 +584,7 @@ env.CppUnitTest( 'sharding_initialization_mongod_test.cpp', 'sharding_initialization_op_observer_test.cpp', 'sharding_logging_test.cpp', + 'split_chunk_request_test.cpp', 'split_vector_test.cpp', 'start_chunk_clone_request_test.cpp', 'transaction_coordinator_catalog_test.cpp', @@ -592,7 +595,6 @@ env.CppUnitTest( 'transaction_coordinator_test.cpp', 'type_shard_collection_test.cpp', 'type_shard_identity_test.cpp', - 'operation_sharding_state_test.cpp', 'vector_clock_shard_server_test.cpp', ], LIBDEPS=[ diff --git a/src/mongo/db/s/balancer/balancer.cpp b/src/mongo/db/s/balancer/balancer.cpp index 423faed4c89..7b06396d010 100644 --- a/src/mongo/db/s/balancer/balancer.cpp +++ b/src/mongo/db/s/balancer/balancer.cpp @@ -29,8 +29,6 @@ #define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kSharding -#include "mongo/platform/basic.h" - #include "mongo/db/s/balancer/balancer.h" #include <algorithm> @@ -201,7 +199,6 @@ Status processManualMigrationOutcome(OperationContext* opCtx, return outcome; } - uint64_t getMaxChunkSizeBytes(OperationContext* opCtx, const CollectionType& coll) { const auto balancerConfig = Grid::get(opCtx)->getBalancerConfiguration(); uassertStatusOK(balancerConfig->refreshAndCheck(opCtx)); @@ -210,7 +207,7 @@ uint64_t getMaxChunkSizeBytes(OperationContext* opCtx, const CollectionType& col const int64_t getMaxChunkSizeMB(OperationContext* opCtx, const CollectionType& coll) { return getMaxChunkSizeBytes(opCtx, coll) / (1024 * 1024); -}; +} const auto _balancerDecoration = ServiceContext::declareDecoration<Balancer>(); @@ -885,6 +882,7 @@ Status Balancer::_splitChunksIfNeeded(OperationContext* opCtx) { splitInfo.nss, cm.getShardKeyPattern(), splitInfo.collectionVersion.epoch(), + splitInfo.collectionVersion.getTimestamp(), ChunkVersion::IGNORED() /*shardVersion*/, ChunkRange(splitInfo.minKey, splitInfo.maxKey), splitInfo.splitKeys); diff --git a/src/mongo/db/s/balancer/balancer_commands_scheduler_impl.cpp b/src/mongo/db/s/balancer/balancer_commands_scheduler_impl.cpp index c05eb954132..797d4c00bbc 100644 --- a/src/mongo/db/s/balancer/balancer_commands_scheduler_impl.cpp +++ b/src/mongo/db/s/balancer/balancer_commands_scheduler_impl.cpp @@ -160,6 +160,7 @@ const std::string SplitChunkCommandInfo::kKeyPattern = "keyPattern"; const std::string SplitChunkCommandInfo::kLowerBound = "min"; const std::string SplitChunkCommandInfo::kUpperBound = "max"; const std::string SplitChunkCommandInfo::kEpoch = "epoch"; +const std::string SplitChunkCommandInfo::kTimestamp = "timestamp"; const std::string SplitChunkCommandInfo::kSplitKeys = "splitKeys"; BalancerCommandsSchedulerImpl::BalancerCommandsSchedulerImpl() {} diff --git a/src/mongo/db/s/balancer/balancer_commands_scheduler_impl.h b/src/mongo/db/s/balancer/balancer_commands_scheduler_impl.h index 2139ce3f9d5..ba85ee9a6e3 100644 --- a/src/mongo/db/s/balancer/balancer_commands_scheduler_impl.h +++ b/src/mongo/db/s/balancer/balancer_commands_scheduler_impl.h @@ -393,6 +393,7 @@ public: .append(kShardName, getTarget().toString()) .append(kKeyPattern, _shardKeyPattern) .append(kEpoch, _version.epoch()) + .append(kTimestamp, _version.getTimestamp()) .append(kLowerBound, _lowerBoundKey) .append(kUpperBound, _upperBoundKey) .append(kSplitKeys, _splitPoints); @@ -412,6 +413,7 @@ private: static const std::string kLowerBound; static const std::string kUpperBound; static const std::string kEpoch; + static const std::string kTimestamp; static const std::string kSplitKeys; }; diff --git a/src/mongo/db/s/chunk_splitter.cpp b/src/mongo/db/s/chunk_splitter.cpp index 751e15141c1..d27798fdd44 100644 --- a/src/mongo/db/s/chunk_splitter.cpp +++ b/src/mongo/db/s/chunk_splitter.cpp @@ -29,8 +29,6 @@ #define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kSharding -#include "mongo/platform/basic.h" - #include "mongo/db/s/chunk_splitter.h" #include "mongo/client/dbclient_cursor.h" @@ -49,8 +47,8 @@ #include "mongo/s/catalog/type_chunk.h" #include "mongo/s/catalog_cache.h" #include "mongo/s/chunk_manager.h" -#include "mongo/s/config_server_client.h" #include "mongo/s/grid.h" +#include "mongo/s/request_types/balance_chunk_request_type.h" #include "mongo/s/shard_key_pattern.h" #include "mongo/s/shard_util.h" #include "mongo/util/assert_util.h" @@ -104,11 +102,24 @@ Status splitChunkAtMultiplePoints(OperationContext* opCtx, std::move(splitPoints), shardId.toString(), collectionVersion.epoch(), + collectionVersion.getTimestamp(), true /* fromChunkSplitter */) .getStatus() .withContext("split failed"); } +void rebalanceChunk(OperationContext* opCtx, const NamespaceString& nss, const ChunkType& chunk) { + auto shardRegistry = Grid::get(opCtx)->shardRegistry(); + auto shard = shardRegistry->getConfigShard(); + auto response = uassertStatusOK(shard->runCommandWithFixedRetryAttempts( + opCtx, + ReadPreferenceSetting{ReadPreference::PrimaryOnly}, + "admin", + BalanceChunkRequest::serializeToRebalanceCommandForConfig(nss, chunk), + Shard::RetryPolicy::kNotIdempotent)); + uassertStatusOK(response.commandStatus); +} + /** * Attempts to move the chunk specified by minKey away from its current shard. */ @@ -130,7 +141,7 @@ void moveChunk(OperationContext* opCtx, const NamespaceString& nss, const BSONOb chunkToMove.setMax(suggestedChunk.getMax()); chunkToMove.setVersion(suggestedChunk.getLastmod()); - uassertStatusOK(configsvr_client::rebalanceChunk(opCtx, nss, chunkToMove)); + rebalanceChunk(opCtx, nss, chunkToMove); } /** diff --git a/src/mongo/db/s/config/configsvr_split_chunk_command.cpp b/src/mongo/db/s/config/configsvr_split_chunk_command.cpp index 336198ab5b0..831f9ca6eb5 100644 --- a/src/mongo/db/s/config/configsvr_split_chunk_command.cpp +++ b/src/mongo/db/s/config/configsvr_split_chunk_command.cpp @@ -39,8 +39,8 @@ #include "mongo/db/operation_context.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/db/s/config/sharding_catalog_manager.h" +#include "mongo/db/s/split_chunk_request_type.h" #include "mongo/s/grid.h" -#include "mongo/s/request_types/split_chunk_request_type.h" #include "mongo/util/str.h" namespace mongo { @@ -122,6 +122,7 @@ public: opCtx, parsedRequest.getNamespace(), parsedRequest.getEpoch(), + parsedRequest.getTimestamp(), parsedRequest.getChunkRange(), parsedRequest.getSplitPoints(), parsedRequest.getShardName(), diff --git a/src/mongo/db/s/config/sharding_catalog_manager.h b/src/mongo/db/s/config/sharding_catalog_manager.h index 08a1c9143dc..0cb11bfc7dd 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager.h +++ b/src/mongo/db/s/config/sharding_catalog_manager.h @@ -249,6 +249,7 @@ public: StatusWith<BSONObj> commitChunkSplit(OperationContext* opCtx, const NamespaceString& nss, const OID& requestEpoch, + const boost::optional<Timestamp>& requestTimestamp, const ChunkRange& range, const std::vector<BSONObj>& splitPoints, const std::string& shardName, diff --git a/src/mongo/db/s/config/sharding_catalog_manager_chunk_operations.cpp b/src/mongo/db/s/config/sharding_catalog_manager_chunk_operations.cpp index d2b7fe9f9f5..2843dae8e4d 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_chunk_operations.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_chunk_operations.cpp @@ -565,6 +565,7 @@ StatusWith<BSONObj> ShardingCatalogManager::commitChunkSplit( OperationContext* opCtx, const NamespaceString& nss, const OID& requestEpoch, + const boost::optional<Timestamp>& requestTimestamp, const ChunkRange& range, const std::vector<BSONObj>& splitPoints, const std::string& shardName, @@ -605,7 +606,8 @@ StatusWith<BSONObj> ShardingCatalogManager::commitChunkSplit( auto collVersion = swCollVersion.getValue(); // Return an error if collection epoch does not match epoch of request. - if (coll.getEpoch() != requestEpoch) { + if (coll.getEpoch() != requestEpoch || + (requestTimestamp && coll.getTimestamp() != requestTimestamp)) { return {ErrorCodes::StaleEpoch, str::stream() << "splitChunk cannot split chunk " << range.toString() << ". Epoch of collection '" << nss.ns() << "' has changed." @@ -1794,6 +1796,7 @@ void ShardingCatalogManager::splitOrMarkJumbo(OperationContext* opCtx, nss, cm.getShardKeyPattern(), cm.getVersion().epoch(), + cm.getVersion().getTimestamp(), ChunkVersion::IGNORED() /*shardVersion*/, ChunkRange(chunk.getMin(), chunk.getMax()), splitPoints)); diff --git a/src/mongo/db/s/config/sharding_catalog_manager_split_chunk_test.cpp b/src/mongo/db/s/config/sharding_catalog_manager_split_chunk_test.cpp index d6aadf88c49..cd7f46ebfda 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_split_chunk_test.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_split_chunk_test.cpp @@ -27,8 +27,6 @@ * it in the license file. */ -#include "mongo/platform/basic.h" - #include "mongo/client/read_preference.h" #include "mongo/db/dbdirectclient.h" #include "mongo/db/namespace_string.h" @@ -86,6 +84,7 @@ TEST_F(SplitChunkTest, SplitExistingChunkCorrectlyShouldSucceed) { ->commitChunkSplit(operationContext(), nss, collEpoch, + collTimestamp, ChunkRange(chunkMin, chunkMax), splitPoints, "shard0000", @@ -170,6 +169,7 @@ TEST_F(SplitChunkTest, MultipleSplitsOnExistingChunkShouldSucceed) { ->commitChunkSplit(operationContext(), nss, collEpoch, + collTimestamp, ChunkRange(chunkMin, chunkMax), splitPoints, "shard0000", @@ -266,6 +266,7 @@ TEST_F(SplitChunkTest, NewSplitShouldClaimHighestVersion) { ->commitChunkSplit(operationContext(), nss, collEpoch, + collTimestamp, ChunkRange(chunkMin, chunkMax), splitPoints, "shard0000", @@ -326,6 +327,7 @@ TEST_F(SplitChunkTest, PreConditionFailErrors) { ->commitChunkSplit(operationContext(), nss, collEpoch, + collTimestamp, ChunkRange(chunkMin, BSON("a" << 7)), splitPoints, "shard0000", @@ -360,6 +362,7 @@ TEST_F(SplitChunkTest, NonExisingNamespaceErrors) { ->commitChunkSplit(operationContext(), NamespaceString("TestDB.NonExistingColl"), collEpoch, + Timestamp{50, 0}, ChunkRange(chunkMin, chunkMax), splitPoints, "shard0000", @@ -395,6 +398,7 @@ TEST_F(SplitChunkTest, NonMatchingEpochsOfChunkAndRequestErrors) { ->commitChunkSplit(operationContext(), nss, OID::gen(), + Timestamp{50, 0}, ChunkRange(chunkMin, chunkMax), splitPoints, "shard0000", @@ -430,6 +434,7 @@ TEST_F(SplitChunkTest, SplitPointsOutOfOrderShouldFail) { ->commitChunkSplit(operationContext(), nss, collEpoch, + collTimestamp, ChunkRange(chunkMin, chunkMax), splitPoints, "shard0000", @@ -464,6 +469,7 @@ TEST_F(SplitChunkTest, SplitPointsOutOfRangeAtMinShouldFail) { ->commitChunkSplit(operationContext(), nss, collEpoch, + collTimestamp, ChunkRange(chunkMin, chunkMax), splitPoints, "shard0000", @@ -499,6 +505,7 @@ TEST_F(SplitChunkTest, SplitPointsOutOfRangeAtMaxShouldFail) { ->commitChunkSplit(operationContext(), nss, collEpoch, + collTimestamp, ChunkRange(chunkMin, chunkMax), splitPoints, "shard0000", @@ -530,6 +537,7 @@ TEST_F(SplitChunkTest, SplitPointsWithDollarPrefixShouldFail) { ->commitChunkSplit(operationContext(), nss, collEpoch, + collTimestamp, ChunkRange(chunkMin, chunkMax), {BSON("a" << BSON("$minKey" << 1))}, "shard0000", @@ -538,6 +546,7 @@ TEST_F(SplitChunkTest, SplitPointsWithDollarPrefixShouldFail) { ->commitChunkSplit(operationContext(), nss, collEpoch, + collTimestamp, ChunkRange(chunkMin, chunkMax), {BSON("a" << BSON("$maxKey" << 1))}, "shard0000", @@ -550,13 +559,14 @@ TEST_F(SplitChunkTest, SplitPointsWithDollarPrefixShouldFail) { TEST_F(SplitChunkTest, CantCommitSplitFromChunkSplitterDuringDefragmentation) { const auto& nss = _nss2; const auto collEpoch = OID::gen(); + const Timestamp collTimestamp{1, 0}; const auto collUuid = UUID::gen(); ChunkType chunk; chunk.setName(OID::gen()); chunk.setCollectionUUID(collUuid); - auto version = ChunkVersion(1, 0, collEpoch, Timestamp(42)); + auto version = ChunkVersion(1, 0, collEpoch, collTimestamp); chunk.setVersion(version); chunk.setShard(ShardId(_shardName)); @@ -587,6 +597,7 @@ TEST_F(SplitChunkTest, CantCommitSplitFromChunkSplitterDuringDefragmentation) { ->commitChunkSplit(operationContext(), nss, collEpoch, + collTimestamp, ChunkRange(chunkMin, chunkMax), splitPoints, "shard0000", @@ -599,6 +610,7 @@ TEST_F(SplitChunkTest, CantCommitSplitFromChunkSplitterDuringDefragmentation) { ->commitChunkSplit(operationContext(), nss, collEpoch, + collTimestamp, ChunkRange(chunkMin, chunkMax), splitPoints, "shard0000", diff --git a/src/mongo/db/s/shardsvr_split_chunk_command.cpp b/src/mongo/db/s/shardsvr_split_chunk_command.cpp index 5476c28e787..dea40abcb4e 100644 --- a/src/mongo/db/s/shardsvr_split_chunk_command.cpp +++ b/src/mongo/db/s/shardsvr_split_chunk_command.cpp @@ -165,6 +165,13 @@ public: OID expectedCollectionEpoch; uassertStatusOK(bsonExtractOIDField(cmdObj, "epoch", &expectedCollectionEpoch)); + boost::optional<Timestamp> expectedCollectionTimestamp; + if (cmdObj["timestamp"]) { + expectedCollectionTimestamp.emplace(); + uassertStatusOK(bsonExtractTimestampField( + cmdObj, "timestamp", expectedCollectionTimestamp.get_ptr())); + } + bool fromChunkSplitter = [&]() { bool field = false; Status status = bsonExtractBooleanField(cmdObj, "fromChunkSplitter", &field); @@ -178,6 +185,7 @@ public: std::move(splitKeys), shardName, expectedCollectionEpoch, + expectedCollectionTimestamp, fromChunkSplitter)); // Otherwise, we want to check whether or not top-chunk optimization should be performed. If diff --git a/src/mongo/db/s/split_chunk.cpp b/src/mongo/db/s/split_chunk.cpp index a97efd9be55..6ac4cf2620a 100644 --- a/src/mongo/db/s/split_chunk.cpp +++ b/src/mongo/db/s/split_chunk.cpp @@ -47,11 +47,11 @@ #include "mongo/db/s/shard_filtering_metadata_refresh.h" #include "mongo/db/s/shard_key_index_util.h" #include "mongo/db/s/sharding_state.h" +#include "mongo/db/s/split_chunk_request_type.h" #include "mongo/logv2/log.h" #include "mongo/s/catalog/type_chunk.h" #include "mongo/s/client/shard_registry.h" #include "mongo/s/grid.h" -#include "mongo/s/request_types/split_chunk_request_type.h" namespace mongo { namespace { @@ -96,6 +96,7 @@ bool checkIfSingleDoc(OperationContext* opCtx, bool checkMetadataForSuccessfulSplitChunk(OperationContext* opCtx, const NamespaceString& nss, const OID& epoch, + const boost::optional<Timestamp>& expectedTimestamp, const ChunkRange& chunkRange, const std::vector<BSONObj>& splitPoints) { AutoGetCollection autoColl(opCtx, nss, MODE_IS); @@ -104,7 +105,9 @@ bool checkMetadataForSuccessfulSplitChunk(OperationContext* opCtx, uassert(ErrorCodes::StaleEpoch, str::stream() << "Collection " << nss.ns() << " changed since split start", - metadataAfterSplit && metadataAfterSplit->getShardVersion().epoch() == epoch); + metadataAfterSplit && metadataAfterSplit->getShardVersion().epoch() == epoch && + (!expectedTimestamp || + metadataAfterSplit->getShardVersion().getTimestamp() == expectedTimestamp)); ChunkType nextChunk; for (auto it = splitPoints.begin(); it != splitPoints.end(); ++it) { @@ -128,14 +131,16 @@ bool checkMetadataForSuccessfulSplitChunk(OperationContext* opCtx, } // namespace -StatusWith<boost::optional<ChunkRange>> splitChunk(OperationContext* opCtx, - const NamespaceString& nss, - const BSONObj& keyPatternObj, - const ChunkRange& chunkRange, - std::vector<BSONObj>&& splitPoints, - const std::string& shardName, - const OID& expectedCollectionEpoch, - const bool fromChunkSplitter) { +StatusWith<boost::optional<ChunkRange>> splitChunk( + OperationContext* opCtx, + const NamespaceString& nss, + const BSONObj& keyPatternObj, + const ChunkRange& chunkRange, + std::vector<BSONObj>&& splitPoints, + const std::string& shardName, + const OID& expectedCollectionEpoch, + const boost::optional<Timestamp>& expectedCollectionTimestamp, + const bool fromChunkSplitter) { auto scopedSplitOrMergeChunk(uassertStatusOK( ActiveMigrationsRegistry::get(opCtx).registerSplitOrMergeChunk(opCtx, nss, chunkRange))); @@ -159,6 +164,7 @@ StatusWith<boost::optional<ChunkRange>> splitChunk(OperationContext* opCtx, auto request = SplitChunkRequest(nss, shardName, expectedCollectionEpoch, + expectedCollectionTimestamp, chunkRange, std::move(splitPoints), fromChunkSplitter); @@ -250,8 +256,12 @@ StatusWith<boost::optional<ChunkRange>> splitChunk(OperationContext* opCtx, // getting the response from the first call to _configsvrCommitChunkSplit, but it actually // succeeds, thus the automatic retry fails with a precondition violation, for example. if (!commandStatus.isOK() || !writeConcernStatus.isOK()) { - if (checkMetadataForSuccessfulSplitChunk( - opCtx, nss, expectedCollectionEpoch, chunkRange, request.getSplitPoints())) { + if (checkMetadataForSuccessfulSplitChunk(opCtx, + nss, + expectedCollectionEpoch, + expectedCollectionTimestamp, + chunkRange, + request.getSplitPoints())) { // Split was committed. } else if (!commandStatus.isOK()) { return commandStatus; diff --git a/src/mongo/db/s/split_chunk.h b/src/mongo/db/s/split_chunk.h index d8787d358eb..2f98f203523 100644 --- a/src/mongo/db/s/split_chunk.h +++ b/src/mongo/db/s/split_chunk.h @@ -33,16 +33,16 @@ #include <string> #include <vector> +#include "mongo/base/status_with.h" +#include "mongo/bson/oid.h" +#include "mongo/bson/timestamp.h" + namespace mongo { class BSONObj; class ChunkRange; -class KeyPattern; class NamespaceString; -class OID; class OperationContext; -template <typename T> -class StatusWith; /** * Attempts to split a chunk with the specified parameters. If the split fails, then the StatusWith @@ -55,12 +55,15 @@ class StatusWith; * range for the top chunk. Note that this ChunkRange is boost::optional, meaning that if top-chunk * optimization is not performed, boost::none will be returned inside of the StatusWith instead. */ -StatusWith<boost::optional<ChunkRange>> splitChunk(OperationContext* opCtx, - const NamespaceString& nss, - const BSONObj& keyPatternObj, - const ChunkRange& chunkRange, - std::vector<BSONObj>&& splitPoints, - const std::string& shardName, - const OID& expectedCollectionEpoch, - bool fromChunkSplitter = false); +StatusWith<boost::optional<ChunkRange>> splitChunk( + OperationContext* opCtx, + const NamespaceString& nss, + const BSONObj& keyPatternObj, + const ChunkRange& chunkRange, + std::vector<BSONObj>&& splitPoints, + const std::string& shardName, + const OID& expectedCollectionEpoch, + const boost::optional<Timestamp>& expectedCollectionTimestamp, + bool fromChunkSplitter = false); + } // namespace mongo diff --git a/src/mongo/s/request_types/split_chunk_request_test.cpp b/src/mongo/db/s/split_chunk_request_test.cpp index d5262fd88b5..00179486ca6 100644 --- a/src/mongo/s/request_types/split_chunk_request_test.cpp +++ b/src/mongo/db/s/split_chunk_request_test.cpp @@ -27,15 +27,11 @@ * it in the license file. */ -#include "mongo/platform/basic.h" - #include "mongo/bson/bsonobjbuilder.h" -#include "mongo/s/request_types/split_chunk_request_type.h" - +#include "mongo/db/s/split_chunk_request_type.h" #include "mongo/unittest/unittest.h" namespace mongo { - namespace { using unittest::assertGet; diff --git a/src/mongo/s/request_types/split_chunk_request_type.cpp b/src/mongo/db/s/split_chunk_request_type.cpp index 82c654f5685..951e4d91e7a 100644 --- a/src/mongo/s/request_types/split_chunk_request_type.cpp +++ b/src/mongo/db/s/split_chunk_request_type.cpp @@ -27,44 +27,41 @@ * it in the license file. */ -#include "mongo/platform/basic.h" - -#include "mongo/s/request_types/split_chunk_request_type.h" +#include "mongo/db/s/split_chunk_request_type.h" #include "mongo/bson/bsonobjbuilder.h" #include "mongo/bson/util/bson_extract.h" #include "mongo/db/write_concern_options.h" namespace mongo { - -using std::string; -using std::vector; - namespace { const char kConfigsvrSplitChunk[] = "_configsvrCommitChunkSplit"; const char kCollEpoch[] = "collEpoch"; +const char kCollTimestamp[] = "collTimestamp"; const char kSplitPoints[] = "splitPoints"; const char kShardName[] = "shard"; const char kFromChunkSplitter[] = "fromChunkSplitter"; -} // unnamed namespace +} // namespace SplitChunkRequest::SplitChunkRequest(NamespaceString nss, - string shardName, + std::string shardName, OID epoch, + boost::optional<Timestamp> timestamp, ChunkRange chunkRange, - vector<BSONObj> splitPoints, + std::vector<BSONObj> splitPoints, bool fromChunkSplitter) : _nss(std::move(nss)), _epoch(std::move(epoch)), + _timestamp(std::move(timestamp)), _chunkRange(std::move(chunkRange)), _splitPoints(std::move(splitPoints)), _shardName(std::move(shardName)), _fromChunkSplitter(fromChunkSplitter) {} StatusWith<SplitChunkRequest> SplitChunkRequest::parseFromConfigCommand(const BSONObj& cmdObj) { - string ns; + std::string ns; auto parseNamespaceStatus = bsonExtractStringField(cmdObj, kConfigsvrSplitChunk, &ns); if (!parseNamespaceStatus.isOK()) { @@ -78,13 +75,24 @@ StatusWith<SplitChunkRequest> SplitChunkRequest::parseFromConfigCommand(const BS return parseEpochStatus; } + boost::optional<Timestamp> timestamp; + if (cmdObj[kCollTimestamp]) { + timestamp.emplace(); + auto parseTimestampStatus = + bsonExtractTimestampField(cmdObj, kCollTimestamp, timestamp.get_ptr()); + + if (!parseTimestampStatus.isOK()) { + return parseTimestampStatus; + } + } + auto chunkRangeStatus = ChunkRange::fromBSON(cmdObj); if (!chunkRangeStatus.isOK()) { return chunkRangeStatus.getStatus(); } - vector<BSONObj> splitPoints; + std::vector<BSONObj> splitPoints; { BSONElement splitPointsElem; auto splitPointsElemStatus = @@ -99,7 +107,7 @@ StatusWith<SplitChunkRequest> SplitChunkRequest::parseFromConfigCommand(const BS } } - string shardName; + std::string shardName; auto parseShardNameStatus = bsonExtractStringField(cmdObj, kShardName, &shardName); if (!parseShardNameStatus.isOK()) { @@ -115,6 +123,7 @@ StatusWith<SplitChunkRequest> SplitChunkRequest::parseFromConfigCommand(const BS auto request = SplitChunkRequest(NamespaceString(ns), std::move(shardName), std::move(epoch), + std::move(timestamp), std::move(chunkRangeStatus.getValue()), std::move(splitPoints), fromChunkSplitter); @@ -164,11 +173,11 @@ const ChunkRange& SplitChunkRequest::getChunkRange() const { return _chunkRange; } -const vector<BSONObj>& SplitChunkRequest::getSplitPoints() const { +const std::vector<BSONObj>& SplitChunkRequest::getSplitPoints() const { return _splitPoints; } -const string& SplitChunkRequest::getShardName() const { +const std::string& SplitChunkRequest::getShardName() const { return _shardName; } diff --git a/src/mongo/s/request_types/split_chunk_request_type.h b/src/mongo/db/s/split_chunk_request_type.h index 4dfa9c87ca7..52cedce7b5c 100644 --- a/src/mongo/s/request_types/split_chunk_request_type.h +++ b/src/mongo/db/s/split_chunk_request_type.h @@ -48,6 +48,7 @@ public: SplitChunkRequest(NamespaceString nss, std::string shardName, OID epoch, + boost::optional<Timestamp> timestamp, ChunkRange chunkRange, std::vector<BSONObj> splitPoints, bool fromChunkSplitter); @@ -83,6 +84,9 @@ public: const NamespaceString& getNamespace() const; const OID& getEpoch() const; + const auto& getTimestamp() const { + return _timestamp; + } const ChunkRange& getChunkRange() const; const std::vector<BSONObj>& getSplitPoints() const; const std::string& getShardName() const; @@ -97,6 +101,7 @@ private: NamespaceString _nss; OID _epoch; + boost::optional<Timestamp> _timestamp; ChunkRange _chunkRange; std::vector<BSONObj> _splitPoints; std::string _shardName; diff --git a/src/mongo/db/s/split_chunk_test.cpp b/src/mongo/db/s/split_chunk_test.cpp deleted file mode 100644 index 98ef66f4021..00000000000 --- a/src/mongo/db/s/split_chunk_test.cpp +++ /dev/null @@ -1,698 +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_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest - -#include "mongo/platform/basic.h" - -#include <boost/optional.hpp> - -#include "mongo/db/json.h" -#include "mongo/db/s/shard_server_test_fixture.h" -#include "mongo/db/s/sharding_initialization_mongod.h" -#include "mongo/db/s/split_chunk.h" -#include "mongo/db/server_options.h" -#include "mongo/executor/remote_command_request.h" -#include "mongo/executor/remote_command_response.h" -#include "mongo/executor/task_executor.h" -#include "mongo/logv2/log.h" -#include "mongo/s/catalog/type_chunk.h" -#include "mongo/s/catalog/type_collection.h" -#include "mongo/s/catalog/type_database_gen.h" -#include "mongo/s/catalog/type_shard.h" -#include "mongo/s/catalog_cache_loader.h" -#include "mongo/s/client/shard_registry.h" -#include "mongo/s/grid.h" -#include "mongo/s/write_ops/batched_command_request.h" -#include "mongo/s/write_ops/batched_command_response.h" - -namespace mongo { -namespace { - -using executor::RemoteCommandRequest; -using executor::RemoteCommandResponse; - -class SplitChunkTest : public ShardServerTestFixture { -public: - void setUp() override { - ShardServerTestFixture::setUp(); - - ShardingState::get(operationContext())->setInitialized(_shardId, OID::gen()); - CatalogCacheLoader::get(getServiceContext()).initializeReplicaSetRole(true); - - // Instantiate names. - _epoch = OID::gen(); - - _shardId = ShardId("shardId"); - _nss = NamespaceString(StringData("dbName"), StringData("collName")); - - // Set up the databases collection - _db.setName("dbName"); - _db.setPrimary(_shardId.toString()); - _db.setSharded(true); - ASSERT_OK(_db.validate()); - - // Set up the collections collection - _coll.setNss(_nss); - _coll.setEpoch(_epoch); - _coll.setUpdatedAt(Date_t::fromMillisSinceEpoch(ChunkVersion(1, 3, _epoch).toLong())); - _coll.setKeyPattern(BSON("_id" << 1)); - _coll.setUique(false); - ASSERT_OK(_coll.validate()); - - // Set up the shard - _shard.setName(_shardId.toString()); - _shard.setHost("TestHost1"); - ASSERT_OK(_shard.validate()); - - setUpChunkRanges(); - setUpChunkVersions(); - } - - /** - * Returns the mock response that correspond with particular requests. For example, dbResponse - * returns a response for when a find-databases request occurs. - * - * commitChunkSplitResponse : responds with { "ok" : 1 } or { "ok" : 0 } - * dbResponse : responds with vector containing _db.toBSON() - * collResponse : responds with vector containing _coll.toBSON() - * shardResponse : responds with vector containing _shard.toBSON() - * chunkResponse : responds with vector containing all every chunk.toConfigBSON() - * emptyResponse : responds with empty vector - */ - void commitChunkSplitResponse(bool isOk); - void dbResponse(); - void collResponse(); - void shardResponse(); - void chunkResponse(); - void emptyResponse(); - -protected: - /** - * Helper functions to return vectors of basic chunk ranges, chunk versions to - * be used by some of the tests. - */ - void setUpChunkRanges(); - void setUpChunkVersions(); - - OID _epoch; - - NamespaceString _nss; - ShardId _shardId; - - DatabaseType _db; - CollectionType _coll; - ShardType _shard; - - std::vector<ChunkRange> _chunkRanges; - std::vector<ChunkVersion> _chunkVersions; -}; - -void SplitChunkTest::setUpChunkRanges() { - BSONObjBuilder minKeyBuilder; - BSONObjBuilder maxKeyBuilder; - minKeyBuilder.appendMinKey("foo"); - maxKeyBuilder.appendMaxKey("foo"); - - const BSONObj key1 = minKeyBuilder.obj(); - const BSONObj key2 = BSON("foo" << 0); - const BSONObj key3 = BSON("foo" << 1024); - const BSONObj key4 = maxKeyBuilder.obj(); - - _chunkRanges.push_back(ChunkRange(key1, key2)); - _chunkRanges.push_back(ChunkRange(key2, key3)); - _chunkRanges.push_back(ChunkRange(key3, key4)); -} - -void SplitChunkTest::setUpChunkVersions() { - _chunkVersions = { - ChunkVersion(1, 1, _epoch), ChunkVersion(1, 2, _epoch), ChunkVersion(1, 3, _epoch)}; -} - -void SplitChunkTest::commitChunkSplitResponse(bool isOk) { - onCommand([&](const RemoteCommandRequest& request) { - return isOk ? BSON("ok" << 1) : BSON("ok" << 0); - }); -} - -void SplitChunkTest::dbResponse() { - onFindCommand( - [&](const RemoteCommandRequest& request) { return std::vector<BSONObj>{_db.toBSON()}; }); -} - -void SplitChunkTest::collResponse() { - onFindCommand( - [&](const RemoteCommandRequest& request) { return std::vector<BSONObj>{_coll.toBSON()}; }); -} - -void SplitChunkTest::shardResponse() { - onFindCommand( - [&](const RemoteCommandRequest& request) { return std::vector<BSONObj>{_shard.toBSON()}; }); -} - -void SplitChunkTest::chunkResponse() { - onFindCommand([&](const RemoteCommandRequest& request) { - std::vector<BSONObj> response; - for (unsigned long i = 0; i < _chunkRanges.size(); ++i) { - ChunkType chunk(_nss, _chunkRanges[i], _chunkVersions[i], _shardId); - response.push_back(chunk.toConfigBSON()); - } - return response; - }); -} - -void SplitChunkTest::emptyResponse() { - onFindCommand([&](const RemoteCommandRequest& request) { return std::vector<BSONObj>(); }); -} - -TEST_F(SplitChunkTest, HashedKeyPatternNumberLongSplitKeys) { - BSONObj keyPatternObj = BSON("foo" - << "hashed"); - _coll.setKeyPattern(BSON("_id" - << "hashed")); - - // Build a vector of valid split keys, which are values of NumberLong types. - std::vector<BSONObj> validSplitKeys; - for (long long i = 256; i <= 1024; i += 256) { - validSplitKeys.push_back(BSON("foo" << i)); - } - - // Call the splitChunk function asynchronously on a different thread, so that we do not block, - // and so we can construct the mock responses to requests made by splitChunk below. - auto future = launchAsync([&] { - auto statusWithOptionalChunkRange = splitChunk(operationContext(), - _nss, - keyPatternObj, - _chunkRanges[1], - validSplitKeys, - _shardId.toString(), - _epoch); - ASSERT_OK(statusWithOptionalChunkRange.getStatus()); - }); - - // Here, we mock responses to the requests made by the splitChunk operation. The requests first - // do a find on the databases, then a find on all collections in the database we are looking - // for. Next, filter by the specific collection, and find the relevant chunks and shards. - dbResponse(); - collResponse(); - collResponse(); - chunkResponse(); - shardResponse(); - - // Because we provided valid split points, the config server should respond with { "ok" : 1 }. - commitChunkSplitResponse(true); - - // Finally, we find the original collection, and then find the relevant chunks. - collResponse(); - chunkResponse(); - - future.default_timed_get(); -} - -TEST_F(SplitChunkTest, HashedKeyPatternIntegerSplitKeys) { - BSONObj keyPatternObj = BSON("foo" - << "hashed"); - _coll.setKeyPattern(BSON("_id" - << "hashed")); - - // Build a vector of valid split keys, which contains values that may not necessarily be able - // to be converted to NumberLong types. - std::vector<BSONObj> invalidSplitKeys{ - BSON("foo" << -1), BSON("foo" << 0), BSON("foo" << 1), BSON("foo" << 42)}; - - // Call the splitChunk function asynchronously on a different thread, so that we do not block, - // and so we can construct the mock responses to requests made by splitChunk below. - auto future = launchAsync([&] { - auto statusWithOptionalChunkRange = splitChunk(operationContext(), - _nss, - keyPatternObj, - _chunkRanges[1], - invalidSplitKeys, - _shardId.toString(), - _epoch); - ASSERT_EQUALS(ErrorCodes::CannotSplit, statusWithOptionalChunkRange.getStatus()); - }); - - // Here, we mock responses to the requests made by the splitChunk operation. The requests first - // do a find on the databases, then a find on all collections in the database we are looking - // for. Next, filter by the specific collection, and find the relevant chunks and shards. - dbResponse(); - collResponse(); - collResponse(); - chunkResponse(); - shardResponse(); - - future.default_timed_get(); -} - -TEST_F(SplitChunkTest, HashedKeyPatternDoubleSplitKeys) { - BSONObj keyPatternObj = BSON("foo" - << "hashed"); - _coll.setKeyPattern(BSON("_id" - << "hashed")); - - // Build a vector of valid split keys, which contains values that may not necessarily be able - // to be converted to NumberLong types. - std::vector<BSONObj> invalidSplitKeys{ - BSON("foo" << 47.21230129), BSON("foo" << 1.0), BSON("foo" << 0.0), BSON("foo" << -0.001)}; - - // Call the splitChunk function asynchronously on a different thread, so that we do not block, - // and so we can construct the mock responses to requests made by splitChunk below. - auto future = launchAsync([&] { - auto statusWithOptionalChunkRange = splitChunk(operationContext(), - _nss, - keyPatternObj, - _chunkRanges[1], - invalidSplitKeys, - _shardId.toString(), - _epoch); - ASSERT_EQUALS(ErrorCodes::CannotSplit, statusWithOptionalChunkRange.getStatus()); - }); - - // Here, we mock responses to the requests made by the splitChunk operation. The requests first - // do a find on the databases, then a find on all collections in the database we are looking - // for. Next, filter by the specific collection, and find the relevant chunks and shards. - dbResponse(); - collResponse(); - collResponse(); - chunkResponse(); - shardResponse(); - - future.default_timed_get(); -} - -TEST_F(SplitChunkTest, HashedKeyPatternStringSplitKeys) { - BSONObj keyPatternObj = BSON("foo" - << "hashed"); - _coll.setKeyPattern(BSON("_id" - << "hashed")); - - // Build a vector of valid split keys, which contains values that may not necessarily be able - // to be converted to NumberLong types. - std::vector<BSONObj> invalidSplitKeys{BSON("foo" - << "@&(9@*88+_241(/.*@8uuDU@(9];a;s;]3"), - BSON("foo" - << "string"), - BSON("foo" - << "14.13289"), - BSON("foo" - << "")}; - - // Call the splitChunk function asynchronously on a different thread, so that we do not block, - // and so we can construct the mock responses to requests made by splitChunk below. - auto future = launchAsync([&] { - auto statusWithOptionalChunkRange = splitChunk(operationContext(), - _nss, - keyPatternObj, - _chunkRanges[1], - invalidSplitKeys, - _shardId.toString(), - _epoch); - ASSERT_EQUALS(ErrorCodes::CannotSplit, statusWithOptionalChunkRange.getStatus()); - }); - - // Here, we mock responses to the requests made by the splitChunk operation. The requests first - // do a find on the databases, then a find on all collections in the database we are looking - // for. Next, filter by the specific collection, and find the relevant chunks and shards. - dbResponse(); - collResponse(); - collResponse(); - chunkResponse(); - shardResponse(); - - future.default_timed_get(); -} - -TEST_F(SplitChunkTest, ValidRangeKeyPatternSplitKeys) { - BSONObj keyPatternObj = BSON("foo" << 1); - - // Build a vector of valid split keys, which contains values that may not necessarily be able - // be converted to NumberLong types. However, this does not matter since we are not using a - // hashed shard key pattern. - std::vector<BSONObj> validSplitKeys{BSON("foo" << 20), - BSON("foo" << 512), - BSON("foo" - << "hello"), - BSON("foo" - << ""), - BSON("foo" << 3.1415926535)}; - - // Call the splitChunk function asynchronously on a different thread, so that we do not block, - // and so we can construct the mock responses to requests made by splitChunk below. - auto future = launchAsync([&] { - auto statusWithOptionalChunkRange = splitChunk(operationContext(), - _nss, - keyPatternObj, - _chunkRanges[1], - validSplitKeys, - _shardId.toString(), - _epoch); - ASSERT_OK(statusWithOptionalChunkRange.getStatus()); - }); - - // Here, we mock responses to the requests made by the splitChunk operation. The requests first - // do a find on the databases, then a find on all collections in the database we are looking - // for. Next, filter by the specific collection, and find the relevant chunks and shards. - dbResponse(); - collResponse(); - collResponse(); - chunkResponse(); - shardResponse(); - - // Because we provided valid split points, the config server should respond with { "ok" : 1 }. - commitChunkSplitResponse(true); - - // Finally, we find the original collection, and then find the relevant chunks. - collResponse(); - chunkResponse(); - - future.default_timed_get(); -} - -TEST_F(SplitChunkTest, SplitChunkWithNoErrors) { - BSONObj keyPatternObj = BSON("foo" << 1); - - // Build a vector of split keys. Note that we start at {"foo" : 256} and end at {"foo" : 768}, - // neither of which are boundary points. - std::vector<BSONObj> splitKeys; - for (int i = 256; i < 1024; i += 256) { - splitKeys.push_back(BSON("foo" << i)); - } - - // Call the splitChunk function asynchronously on a different thread, so that we do not block, - // and so we can construct the mock responses to requests made by splitChunk below. - auto future = launchAsync([&] { - auto statusWithOptionalChunkRange = splitChunk(operationContext(), - _nss, - keyPatternObj, - _chunkRanges[1], - splitKeys, - _shardId.toString(), - _epoch); - ASSERT_OK(statusWithOptionalChunkRange.getStatus()); - }); - - // Here, we mock responses to the requests made by the splitChunk operation. The requests first - // do a find on the databases, then a find on all collections in the database we are looking - // for. Next, filter by the specific collection, and find the relevant chunks and shards. - dbResponse(); - collResponse(); - collResponse(); - chunkResponse(); - shardResponse(); - - // Mock an OK response to the request to the config server regarding the chunk split, but first - // check the request parameters. - onCommand([&](const RemoteCommandRequest& request) { - ASSERT_EQ(HostAndPort("dummy", 123), request.target); - ASSERT_BSONOBJ_EQ(request.cmdObj["min"].Obj(), BSON("foo" << 0)); - ASSERT_BSONOBJ_EQ(request.cmdObj["max"].Obj(), BSON("foo" << 1024)); - - // Check that the split points in the request are the same as the split keys that were - // initially passed to the splitChunk function. - std::vector<BSONElement> splitPoints = request.cmdObj["splitPoints"].Array(); - ASSERT_EQ(splitKeys.size(), splitPoints.size()); - int i = 0; - for (auto e : splitPoints) { - ASSERT(e.Obj().woCompare(splitKeys[i]) == 0); - i++; - } - - return BSON("ok" << 1); - }); - - // Finally, we find the original collection, and then find the relevant chunks. - collResponse(); - chunkResponse(); - - future.default_timed_get(); -} - -TEST_F(SplitChunkTest, AttemptSplitWithConfigsvrError) { - BSONObj keyPatternObj = BSON("foo" << 1); - - // Build a vector of split keys. Note that we start at {"foo" : 0} and end at {"foo" : 1024}, - // both of which are boundary points. - std::vector<BSONObj> splitKeys; - for (int i = 0; i <= 1024; i += 256) { - splitKeys.push_back(BSON("foo" << i)); - } - - // Call the splitChunk function asynchronously on a different thread, so that we do not block, - // and so we can construct the mock responses to requests made by splitChunk below. - auto future = launchAsync([&] { - auto statusWithOptionalChunkRange = splitChunk(operationContext(), - _nss, - keyPatternObj, - _chunkRanges[1], - splitKeys, - _shardId.toString(), - _epoch); - ASSERT_NOT_OK(statusWithOptionalChunkRange.getStatus()); - }); - - // Here, we mock responses to the requests made by the splitChunk operation. The requests first - // do a find on the databases, then a find on all collections in the database we are looking - // for. Next, filter by the specific collection, and find the relevant chunks and shards. - dbResponse(); - collResponse(); - collResponse(); - chunkResponse(); - shardResponse(); - - // Because we provided invalid split points, the config server should respond with { "ok" : 0 }. - commitChunkSplitResponse(false); - - // Finally, we find the original collection, and then find the relevant chunks. - collResponse(); - chunkResponse(); - - future.default_timed_get(); -} - -TEST_F(SplitChunkTest, AttemptSplitOnNoDatabases) { - BSONObj keyPatternObj = BSON("foo" << 1); - - // Build a vector of split keys. Note that we start at {"foo" : 256} and end at {"foo" : 768}, - // neither of which are boundary points. - std::vector<BSONObj> splitKeys; - for (int i = 256; i < 1024; i += 256) { - splitKeys.push_back(BSON("foo" << i)); - } - - // Call the splitChunk function asynchronously on a different thread, so that we do not block, - // and so we can construct the mock responses to requests made by splitChunk below. - auto future = launchAsync([&] { - auto statusWithOptionalChunkRange = splitChunk(operationContext(), - _nss, - keyPatternObj, - _chunkRanges[1], - splitKeys, - _shardId.toString(), - _epoch); - ASSERT_NOT_OK(statusWithOptionalChunkRange.getStatus()); - }); - - // Here, we mock responses to the requests made by the splitChunk operation. We give an empty - // response to the request that finds the database, along with the request that finds all - // collections in the database. - emptyResponse(); - emptyResponse(); - - future.default_timed_get(); -} - -TEST_F(SplitChunkTest, AttemptSplitOnNoCollections) { - BSONObj keyPatternObj = BSON("foo" << 1); - - // Build a vector of split keys. Note that we start at {"foo" : 256} and end at {"foo" : 768}, - // neither of which are boundary points. - std::vector<BSONObj> splitKeys; - for (int i = 256; i < 1024; i += 256) { - splitKeys.push_back(BSON("foo" << i)); - } - - // Call the splitChunk function asynchronously on a different thread, so that we do not block, - // and so we can construct the mock responses to requests made by splitChunk below. - auto future = launchAsync([&] { - auto statusWithOptionalChunkRange = splitChunk(operationContext(), - _nss, - keyPatternObj, - _chunkRanges[1], - splitKeys, - _shardId.toString(), - _epoch); - ASSERT_NOT_OK(statusWithOptionalChunkRange.getStatus()); - }); - - // Here, we mock responses to the requests made by the splitChunk operation. We first respond - // to the request finding the databases. - dbResponse(); - - // Next, we give an empty response to the request for finding collections in the database, - // followed by a response to the request for relevant shards. - emptyResponse(); - shardResponse(); - - future.default_timed_get(); -} - -TEST_F(SplitChunkTest, AttemptSplitOnNoChunks) { - BSONObj keyPatternObj = BSON("foo" << 1); - - // Build a vector of split keys. Note that we start at {"foo" : 256} and end at {"foo" : 768}, - // neither of which are boundary points. - std::vector<BSONObj> splitKeys; - for (int i = 256; i < 1024; i += 256) { - splitKeys.push_back(BSON("foo" << i)); - } - - // Call the splitChunk function asynchronously on a different thread, so that we do not block, - // and so we can construct the mock responses to requests made by splitChunk below. - auto future = launchAsync([&] { - auto statusWithOptionalChunkRange = splitChunk(operationContext(), - _nss, - keyPatternObj, - _chunkRanges[1], - splitKeys, - _shardId.toString(), - _epoch); - ASSERT_NOT_OK(statusWithOptionalChunkRange.getStatus()); - }); - - // Here, we mock responses to the requests made by the splitChunk operation. The requests first - // do a find on the databases, then a find on all collections in the database we are looking - // for. - dbResponse(); - collResponse(); - - // We attempt to find the relevant chunks three times. For each of these times, we will respond - // with the relevant collection, but no chunks. - collResponse(); - emptyResponse(); - collResponse(); - emptyResponse(); - collResponse(); - emptyResponse(); - - future.default_timed_get(); -} - -TEST_F(SplitChunkTest, NoCollectionAfterSplit) { - BSONObj keyPatternObj = BSON("foo" << 1); - - // Build a vector of split keys. Note that we start at {"foo" : 256} and end at {"foo" : 768}, - // neither of which are boundary points. - std::vector<BSONObj> splitKeys; - for (int i = 256; i < 1024; i += 256) { - splitKeys.push_back(BSON("foo" << i)); - } - - // Call the splitChunk function asynchronously on a different thread, so that we do not block, - // and so we can construct the mock responses to requests made by splitChunk below. - auto future = launchAsync([&] { - auto statusWithOptionalChunkRange = splitChunk(operationContext(), - _nss, - keyPatternObj, - _chunkRanges[1], - splitKeys, - _shardId.toString(), - _epoch); - ASSERT_OK(statusWithOptionalChunkRange.getStatus()); - }); - - // Here, we mock responses to the requests made by the splitChunk operation. The requests first - // do a find on the databases, then a find on all collections in the database we are looking - // for. Next, filter by the specific collection, and find the relevant chunks and shards. - dbResponse(); - collResponse(); - collResponse(); - chunkResponse(); - shardResponse(); - - // Here, we mock a successful response from the config server, which denotes that the split was - // successful on the config server's end. - commitChunkSplitResponse(true); - - // Finally, give an empty response to a request regarding a find on the original collection. - emptyResponse(); - - future.default_timed_get(); -} - -TEST_F(SplitChunkTest, NoChunksAfterSplit) { - BSONObj keyPatternObj = BSON("foo" << 1); - - // Build a vector of split keys. Note that we start at {"foo" : 256} and end at {"foo" : 768}, - // neither of which are boundary points. - std::vector<BSONObj> splitKeys; - for (int i = 256; i < 1024; i += 256) { - splitKeys.push_back(BSON("foo" << i)); - } - - // Call the splitChunk function asynchronously on a different thread, so that we do not block, - // and so we can construct the mock responses to requests made by splitChunk below. - auto future = launchAsync([&] { - auto statusWithOptionalChunkRange = splitChunk(operationContext(), - _nss, - keyPatternObj, - _chunkRanges[1], - splitKeys, - _shardId.toString(), - _epoch); - ASSERT_NOT_OK(statusWithOptionalChunkRange.getStatus()); - }); - - // Here, we mock responses to the requests made by the splitChunk operation. The requests first - // do a find on the databases, then a find on all collections in the database we are looking - // for. Next, filter by the specific collection, and find the relevant chunks and shards. - dbResponse(); - collResponse(); - collResponse(); - chunkResponse(); - shardResponse(); - - // Here, we mock a successful response from the config server, which denotes that the split was - // successful on the config server's end. - commitChunkSplitResponse(true); - - // We attempt to find the relevant chunks three times. For each of these times, we will respond - // with the relevant collection, but no chunks. - collResponse(); - emptyResponse(); - collResponse(); - emptyResponse(); - collResponse(); - emptyResponse(); - - future.default_timed_get(); -} - -} // namespace -} // namespace mongo diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript index cc5e33d5311..32bb92585ee 100644 --- a/src/mongo/s/SConscript +++ b/src/mongo/s/SConscript @@ -213,7 +213,6 @@ env.Library( 'request_types/set_allow_migrations.idl', 'request_types/set_shard_version_request.cpp', 'request_types/sharded_ddl_commands.idl', - 'request_types/split_chunk_request_type.cpp', 'request_types/update_zone_key_range_request_type.cpp', 'request_types/wait_for_fail_point.idl', 'resharding/common_types.idl', @@ -325,7 +324,6 @@ env.Library( 'catalog_cache_loader.cpp', 'cluster_identity_loader.cpp', 'config_server_catalog_cache_loader.cpp', - 'config_server_client.cpp', 'shard_util.cpp', ], LIBDEPS=[ @@ -639,7 +637,6 @@ env.CppUnitTest( 'request_types/move_chunk_request_test.cpp', 'request_types/remove_shard_from_zone_request_test.cpp', 'request_types/set_shard_version_request_test.cpp', - 'request_types/split_chunk_request_test.cpp', 'request_types/update_zone_key_range_request_test.cpp', 'routing_table_history_test.cpp', 'sessions_collection_sharded_test.cpp', diff --git a/src/mongo/s/chunk_version.cpp b/src/mongo/s/chunk_version.cpp index e162bed1a72..b94d7a8f0b5 100644 --- a/src/mongo/s/chunk_version.cpp +++ b/src/mongo/s/chunk_version.cpp @@ -27,12 +27,10 @@ * it in the license file. */ -#include "mongo/platform/basic.h" - #include "mongo/s/chunk_version.h" + #include "mongo/s/chunk_version_gen.h" #include "mongo/s/pm2583_feature_flags_gen.h" - #include "mongo/util/str.h" namespace mongo { diff --git a/src/mongo/s/chunk_version_test.cpp b/src/mongo/s/chunk_version_test.cpp index d61c99de813..3962588b52b 100644 --- a/src/mongo/s/chunk_version_test.cpp +++ b/src/mongo/s/chunk_version_test.cpp @@ -27,19 +27,16 @@ * it in the license file. */ -#include "mongo/platform/basic.h" - #include <limits> #include "mongo/s/chunk_version.h" #include "mongo/s/chunk_version_gen.h" - #include "mongo/unittest/unittest.h" namespace mongo { namespace { -TEST(ChunkVersionParsing, Parsing60Format) { +TEST(ChunkVersionTest, Parsing60Format) { const Timestamp majorMinor = Timestamp(Seconds(1), 2); const OID epoch = OID::gen(); const Timestamp timestamp(42); @@ -70,7 +67,7 @@ TEST(ChunkVersionParsing, Parsing60Format) { ASSERT_EQ(timestamp, futureFormatChunkVersion2.getTimestamp()); } -TEST(ChunkVersionParsing, ToFromBSONRoundtrip) { +TEST(ChunkVersionTest, ToFromBSONRoundtrip) { ChunkVersion version(1, 2, OID::gen(), Timestamp(42)); const auto roundTripVersion = ChunkVersion::fromBSONPositionalOrNewerFormat([&] { BSONObjBuilder builder; @@ -81,7 +78,7 @@ TEST(ChunkVersionParsing, ToFromBSONRoundtrip) { ASSERT_EQ(version, roundTripVersion); } -TEST(ChunkVersionParsing, ToFromBSONLegacyRoundtrip) { +TEST(ChunkVersionTest, ToFromBSONLegacyRoundtrip) { ChunkVersion version(1, 2, OID::gen(), Timestamp(42)); const auto roundTripVersion = ChunkVersion::fromBSONLegacyOrNewerFormat( [&] { @@ -94,7 +91,7 @@ TEST(ChunkVersionParsing, ToFromBSONLegacyRoundtrip) { ASSERT_EQ(version, roundTripVersion); } -TEST(ChunkVersionParsing, FromBSONMissingTimestamp) { +TEST(ChunkVersionTest, FromBSONMissingTimestamp) { ASSERT_THROWS_CODE(ChunkVersion::fromBSONPositionalOrNewerFormat( BSON("testVersionField" << BSON_ARRAY( Timestamp(Seconds(2), 3) << OID::gen()))["testVersionField"]), @@ -102,7 +99,7 @@ TEST(ChunkVersionParsing, FromBSONMissingTimestamp) { ErrorCodes::TypeMismatch); } -TEST(ChunkVersionParsing, FromBSON) { +TEST(ChunkVersionTest, FromBSON) { const OID oid = OID::gen(); const Timestamp timestamp(42); ChunkVersion chunkVersionComplete = ChunkVersion::fromBSONPositionalOrNewerFormat( @@ -116,7 +113,7 @@ TEST(ChunkVersionParsing, FromBSON) { ASSERT_EQ(timestamp, chunkVersionComplete.getTimestamp()); } -TEST(ChunkVersionParsing, FromBSONMissingEpoch) { +TEST(ChunkVersionTest, FromBSONMissingEpoch) { ASSERT_THROWS_CODE( ChunkVersion::fromBSONPositionalOrNewerFormat( BSON("testVersionField" << BSON_ARRAY(Timestamp(Seconds(2), 3)))["testVersionField"]), @@ -124,14 +121,14 @@ TEST(ChunkVersionParsing, FromBSONMissingEpoch) { ErrorCodes::TypeMismatch); } -TEST(ChunkVersionParsing, FromBSONMissingMajorAndMinor) { +TEST(ChunkVersionTest, FromBSONMissingMajorAndMinor) { ASSERT_THROWS_CODE(ChunkVersion::fromBSONPositionalOrNewerFormat( BSON("testVersionField" << BSON_ARRAY(OID::gen()))["testVersionField"]), DBException, ErrorCodes::TypeMismatch); } -TEST(ChunkVersionParsing, FromBSONLegacy_WithTimestamp_WithEpoch) { +TEST(ChunkVersionTest, FromBSONLegacy_WithTimestamp_WithEpoch) { const OID oid = OID::gen(); ChunkVersion chunkVersionComplete = ChunkVersion::fromBSONLegacyOrNewerFormat( BSON("lastmod" << Timestamp(Seconds(2), 3) << "lastmodEpoch" << oid << "lastmodTimestamp" @@ -143,7 +140,7 @@ TEST(ChunkVersionParsing, FromBSONLegacy_WithTimestamp_WithEpoch) { ASSERT_EQ(3u, chunkVersionComplete.minorVersion()); } -TEST(ChunkVersionParsing, FromBSONLegacy_NoTimestamp_WithUnshardedEpoch) { +TEST(ChunkVersionTest, FromBSONLegacy_NoTimestamp_WithUnshardedEpoch) { ChunkVersion chunkVersion = ChunkVersion::fromBSONLegacyOrNewerFormat( BSON("lastmod" << Timestamp() << "lastmodEpoch" << ChunkVersion::UNSHARDED().epoch()), "lastmod"); @@ -153,7 +150,7 @@ TEST(ChunkVersionParsing, FromBSONLegacy_NoTimestamp_WithUnshardedEpoch) { ASSERT_EQ(0u, chunkVersion.minorVersion()); } -TEST(ChunkVersionParsing, FromBSONLegacy_NoTimestamp_WithIgnoredEpoch) { +TEST(ChunkVersionTest, FromBSONLegacy_NoTimestamp_WithIgnoredEpoch) { ChunkVersion chunkVersion = ChunkVersion::fromBSONLegacyOrNewerFormat( BSON("lastmod" << Timestamp() << "lastmodEpoch" << ChunkVersion::IGNORED().epoch()), "lastmod"); @@ -163,14 +160,14 @@ TEST(ChunkVersionParsing, FromBSONLegacy_NoTimestamp_WithIgnoredEpoch) { ASSERT_EQ(0u, chunkVersion.minorVersion()); } -TEST(ChunkVersionParsing, FromBSONLegacy_NoTimestamp_WithShardedEpoch_Throws) { +TEST(ChunkVersionTest, FromBSONLegacy_NoTimestamp_WithShardedEpoch_Throws) { ASSERT_THROWS( ChunkVersion::fromBSONLegacyOrNewerFormat( BSON("lastmod" << Timestamp(Seconds(3), 4) << "lastmodEpoch" << OID::gen()), "lastmod"), DBException); } -TEST(ChunkVersionParsing, FromBSONLegacy_WithTimestamp_NoEpoch_Throws) { +TEST(ChunkVersionTest, FromBSONLegacy_WithTimestamp_NoEpoch_Throws) { ASSERT_THROWS( ChunkVersion::fromBSONLegacyOrNewerFormat( BSON("lastmod" << Timestamp(Seconds(3), 4) << "lastmodTimestamp" << Timestamp(42)), @@ -178,7 +175,7 @@ TEST(ChunkVersionParsing, FromBSONLegacy_WithTimestamp_NoEpoch_Throws) { DBException); } -TEST(ChunkVersionParsing, FromBSONLegacy_NoTimestamp_NoEpoch_Throws) { +TEST(ChunkVersionTest, FromBSONLegacy_NoTimestamp_NoEpoch_Throws) { ChunkVersion chunkVersion = ChunkVersion::fromBSONLegacyOrNewerFormat( BSON("lastmod" << Timestamp(Seconds(3), 4)), "lastmod"); ASSERT_EQ(Timestamp(), chunkVersion.getTimestamp()); @@ -187,7 +184,7 @@ TEST(ChunkVersionParsing, FromBSONLegacy_NoTimestamp_NoEpoch_Throws) { ASSERT_EQ(4u, chunkVersion.minorVersion()); } -TEST(ChunkVersionComparison, EqualityOperators) { +TEST(ChunkVersionTest, EqualityOperators) { OID epoch = OID::gen(); Timestamp timestamp = Timestamp(1); @@ -200,7 +197,7 @@ TEST(ChunkVersionComparison, EqualityOperators) { ASSERT_NE(ChunkVersion(4, 2, epoch, timestamp), ChunkVersion(4, 1, epoch, timestamp)); } -TEST(ChunkVersionComparison, OlderThan) { +TEST(ChunkVersionTest, OlderThan) { OID epoch = OID::gen(); Timestamp timestamp(1); Timestamp newerTimestamp(2); @@ -220,7 +217,7 @@ TEST(ChunkVersionComparison, OlderThan) { ASSERT(!ChunkVersion(3, 1, epoch, timestamp).isOlderThan(ChunkVersion::UNSHARDED())); } -TEST(ChunkVersionConstruction, CreateWithLargeValues) { +TEST(ChunkVersionTest, CreateWithLargeValues) { const uint32_t majorVersion = std::numeric_limits<uint32_t>::max(); const uint32_t minorVersion = std::numeric_limits<uint32_t>::max(); const auto epoch = OID::gen(); @@ -232,7 +229,7 @@ TEST(ChunkVersionConstruction, CreateWithLargeValues) { ASSERT_EQ(Timestamp(1, 1), version.getTimestamp()); } -TEST(ChunkVersionManipulation, ThrowsErrorIfOverflowIsAttemptedForMajorVersion) { +TEST(ChunkVersionTest, ThrowsErrorIfOverflowIsAttemptedForMajorVersion) { const uint32_t majorVersion = std::numeric_limits<uint32_t>::max(); const uint32_t minorVersion = 0; const auto epoch = OID::gen(); @@ -245,7 +242,7 @@ TEST(ChunkVersionManipulation, ThrowsErrorIfOverflowIsAttemptedForMajorVersion) ASSERT_THROWS_CODE(version.incMajor(), DBException, 31180); } -TEST(ChunkVersionManipulation, ThrowsErrorIfOverflowIsAttemptedForMinorVersion) { +TEST(ChunkVersionTest, ThrowsErrorIfOverflowIsAttemptedForMinorVersion) { const uint32_t majorVersion = 0; const uint32_t minorVersion = std::numeric_limits<uint32_t>::max(); const auto epoch = OID::gen(); diff --git a/src/mongo/s/commands/cluster_move_chunk_cmd.cpp b/src/mongo/s/commands/cluster_move_chunk_cmd.cpp index c0ce46ebc49..ad1ce135972 100644 --- a/src/mongo/s/commands/cluster_move_chunk_cmd.cpp +++ b/src/mongo/s/commands/cluster_move_chunk_cmd.cpp @@ -43,7 +43,6 @@ #include "mongo/s/catalog_cache.h" #include "mongo/s/client/shard_registry.h" #include "mongo/s/cluster_commands_helpers.h" -#include "mongo/s/config_server_client.h" #include "mongo/s/grid.h" #include "mongo/s/request_types/migration_secondary_throttle_options.h" #include "mongo/s/request_types/move_range_request_gen.h" diff --git a/src/mongo/s/commands/cluster_split_cmd.cpp b/src/mongo/s/commands/cluster_split_cmd.cpp index 69d25edd3ad..7693d3ccf22 100644 --- a/src/mongo/s/commands/cluster_split_cmd.cpp +++ b/src/mongo/s/commands/cluster_split_cmd.cpp @@ -270,6 +270,7 @@ public: nss, cm.getShardKeyPattern(), cm.getVersion().epoch(), + cm.getVersion().getTimestamp(), cm.getVersion(chunk->getShardId()) /* shardVersion */, ChunkRange(chunk->getMin(), chunk->getMax()), {splitPoint})); diff --git a/src/mongo/s/config_server_client.cpp b/src/mongo/s/config_server_client.cpp deleted file mode 100644 index 015f63452e3..00000000000 --- a/src/mongo/s/config_server_client.cpp +++ /dev/null @@ -1,64 +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/s/config_server_client.h" - -#include "mongo/client/read_preference.h" -#include "mongo/s/client/shard_registry.h" -#include "mongo/s/grid.h" -#include "mongo/s/request_types/balance_chunk_request_type.h" - -namespace mongo { -namespace configsvr_client { -namespace { - -const ReadPreferenceSetting kPrimaryOnlyReadPreference{ReadPreference::PrimaryOnly}; - -} // namespace - -Status rebalanceChunk(OperationContext* opCtx, const NamespaceString& nss, const ChunkType& chunk) { - auto shardRegistry = Grid::get(opCtx)->shardRegistry(); - auto shard = shardRegistry->getConfigShard(); - auto cmdResponseStatus = shard->runCommandWithFixedRetryAttempts( - opCtx, - kPrimaryOnlyReadPreference, - "admin", - BalanceChunkRequest::serializeToRebalanceCommandForConfig(nss, chunk), - Shard::RetryPolicy::kNotIdempotent); - if (!cmdResponseStatus.isOK()) { - return cmdResponseStatus.getStatus(); - } - - return cmdResponseStatus.getValue().commandStatus; -} - -} // namespace configsvr_client -} // namespace mongo diff --git a/src/mongo/s/config_server_client.h b/src/mongo/s/config_server_client.h deleted file mode 100644 index 4143f6c6260..00000000000 --- a/src/mongo/s/config_server_client.h +++ /dev/null @@ -1,54 +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. - */ - -#pragma once - -#include "mongo/s/client/shard.h" - -namespace mongo { - -class ChunkType; -class MigrationSecondaryThrottleOptions; -class OperationContext; -class Status; - -/** - * This namespace contains all commands, which can be executed against the config server in order - * exercise control over the state of the sharded cluster. - */ -namespace configsvr_client { - -/** - * Requests the balancer to move the specified chunk off of its current shard to a shard, considered - * more appropriate under the balancing policy which is currently in effect. - */ -Status rebalanceChunk(OperationContext* opCtx, const NamespaceString& nss, const ChunkType& chunk); - -} // namespace configsvr_client -} // namespace mongo diff --git a/src/mongo/s/request_types/balance_chunk_request_type.cpp b/src/mongo/s/request_types/balance_chunk_request_type.cpp index 0bc3ad0ea42..278947708d3 100644 --- a/src/mongo/s/request_types/balance_chunk_request_type.cpp +++ b/src/mongo/s/request_types/balance_chunk_request_type.cpp @@ -34,7 +34,6 @@ #include "mongo/bson/bsonobjbuilder.h" #include "mongo/bson/util/bson_extract.h" #include "mongo/db/write_concern_options.h" -#include "mongo/s/request_types/balance_chunk_request_type.h" namespace mongo { namespace { @@ -143,36 +142,6 @@ StatusWith<BalanceChunkRequest> BalanceChunkRequest::parseFromConfigCommand(cons return request; } -BSONObj BalanceChunkRequest::serializeToMoveCommandForConfig( - const NamespaceString& nss, - const ChunkType& chunk, - const ShardId& newShardId, - const MigrationSecondaryThrottleOptions& secondaryThrottle, - bool waitForDelete, - bool forceJumbo) { - invariant(chunk.validate()); - - BSONObjBuilder cmdBuilder; - cmdBuilder.append(kConfigSvrMoveChunk, 1); - cmdBuilder.append(kNS, nss.ns()); - cmdBuilder.appendElements(chunk.toConfigBSON()); - // ChunkType::toConfigBSON() no longer adds the epoch - cmdBuilder.append(ChunkType::lastmod() + "Epoch", chunk.getVersion().epoch()); - cmdBuilder.append(ChunkType::lastmod() + "Timestamp", chunk.getVersion().getTimestamp()); - cmdBuilder.append(kToShardId, newShardId.toString()); - { - BSONObjBuilder secondaryThrottleBuilder(cmdBuilder.subobjStart(kSecondaryThrottle)); - secondaryThrottle.append(&secondaryThrottleBuilder); - secondaryThrottleBuilder.doneFast(); - } - cmdBuilder.append(kWaitForDelete, waitForDelete); - cmdBuilder.append(kForceJumbo, forceJumbo); - cmdBuilder.append(WriteConcernOptions::kWriteConcernField, - kMajorityWriteConcernNoTimeout.toBSON()); - - return cmdBuilder.obj(); -} - BSONObj BalanceChunkRequest::serializeToRebalanceCommandForConfig(const NamespaceString& nss, const ChunkType& chunk) { invariant(chunk.validate()); diff --git a/src/mongo/s/request_types/balance_chunk_request_type.h b/src/mongo/s/request_types/balance_chunk_request_type.h index 1386e15fcc4..0a139219441 100644 --- a/src/mongo/s/request_types/balance_chunk_request_type.h +++ b/src/mongo/s/request_types/balance_chunk_request_type.h @@ -58,18 +58,6 @@ public: bool requireUUID = true); /** - * Produces a BSON object for the variant of the command, which requests the balancer to move a - * chunk to a user-specified shard. - */ - static BSONObj serializeToMoveCommandForConfig( - const NamespaceString& nss, - const ChunkType& chunk, - const ShardId& newShardId, - const MigrationSecondaryThrottleOptions& secondaryThrottle, - bool waitForDelete, - bool forceJumbo); - - /** * Produces a BSON object for the variant of the command, which requests the balancer to pick a * better location for a chunk. */ diff --git a/src/mongo/s/shard_util.cpp b/src/mongo/s/shard_util.cpp index 5deba9b40eb..ae5e1aa9a1b 100644 --- a/src/mongo/s/shard_util.cpp +++ b/src/mongo/s/shard_util.cpp @@ -197,6 +197,7 @@ StatusWith<boost::optional<ChunkRange>> splitChunkAtMultiplePoints( const NamespaceString& nss, const ShardKeyPattern& shardKeyPattern, const OID& epoch, + const Timestamp& timestamp, ChunkVersion shardVersion, const ChunkRange& chunkRange, const std::vector<BSONObj>& splitPoints) { @@ -237,6 +238,7 @@ StatusWith<boost::optional<ChunkRange>> splitChunkAtMultiplePoints( cmd.append("from", shardId.toString()); cmd.append("keyPattern", shardKeyPattern.toBSON()); cmd.append("epoch", epoch); + cmd.append("timestamp", timestamp); shardVersion.serializeToBSON(ChunkVersion::kShardVersionField, &cmd); chunkRange.append(&cmd); diff --git a/src/mongo/s/shard_util.h b/src/mongo/s/shard_util.h index d89d78ca93b..997dcc1e4c9 100644 --- a/src/mongo/s/shard_util.h +++ b/src/mongo/s/shard_util.h @@ -112,6 +112,7 @@ StatusWith<boost::optional<ChunkRange>> splitChunkAtMultiplePoints( const NamespaceString& nss, const ShardKeyPattern& shardKeyPattern, const OID& epoch, + const Timestamp& timestamp, ChunkVersion shardVersion, const ChunkRange& chunkRange, const std::vector<BSONObj>& splitPoints); diff --git a/src/mongo/s/stale_exception.cpp b/src/mongo/s/stale_exception.cpp index 9f201a157b3..ac026a45ea2 100644 --- a/src/mongo/s/stale_exception.cpp +++ b/src/mongo/s/stale_exception.cpp @@ -53,17 +53,12 @@ void StaleConfigInfo::serialize(BSONObjBuilder* bob) const { } std::shared_ptr<const ErrorExtraInfo> StaleConfigInfo::parse(const BSONObj& obj) { - return std::make_shared<StaleConfigInfo>(parseFromCommandError(obj)); -} - -StaleConfigInfo StaleConfigInfo::parseFromCommandError(const BSONObj& obj) { const auto shardId = obj["shardId"].String(); - invariant(shardId != ""); + uassert(ErrorCodes::NoSuchKey, "The shardId field is missing", !shardId.empty()); auto extractOptionalChunkVersion = [&obj](StringData field) -> boost::optional<ChunkVersion> { try { - return boost::make_optional<ChunkVersion>( - ChunkVersion::fromBSONLegacyOrNewerFormat(obj, field)); + return ChunkVersion::fromBSONLegacyOrNewerFormat(obj, field); } catch (const DBException& ex) { auto status = ex.toStatus(); if (status != ErrorCodes::NoSuchKey) { @@ -73,10 +68,19 @@ StaleConfigInfo StaleConfigInfo::parseFromCommandError(const BSONObj& obj) { return boost::none; }; - return StaleConfigInfo(NamespaceString(obj["ns"].String()), - ChunkVersion::fromBSONLegacyOrNewerFormat(obj, "vReceived"), - extractOptionalChunkVersion("vWanted"), - ShardId(shardId)); + return std::make_shared<StaleConfigInfo>( + NamespaceString(obj["ns"].String()), + ChunkVersion::fromBSONLegacyOrNewerFormat(obj, "vReceived"), + extractOptionalChunkVersion("vWanted"), + ShardId(shardId)); +} + +void StaleEpochInfo::serialize(BSONObjBuilder* bob) const { + bob->append("ns", _nss.ns()); +} + +std::shared_ptr<const ErrorExtraInfo> StaleEpochInfo::parse(const BSONObj& obj) { + return std::make_shared<StaleEpochInfo>(NamespaceString(obj["ns"].String())); } void StaleDbRoutingVersion::serialize(BSONObjBuilder* bob) const { @@ -88,13 +92,10 @@ void StaleDbRoutingVersion::serialize(BSONObjBuilder* bob) const { } std::shared_ptr<const ErrorExtraInfo> StaleDbRoutingVersion::parse(const BSONObj& obj) { - return std::make_shared<StaleDbRoutingVersion>(parseFromCommandError(obj)); -} - -StaleDbRoutingVersion StaleDbRoutingVersion::parseFromCommandError(const BSONObj& obj) { - return StaleDbRoutingVersion(obj["db"].String(), - DatabaseVersion(obj["vReceived"].Obj()), - !obj["vWanted"].eoo() ? DatabaseVersion(obj["vWanted"].Obj()) + return std::make_shared<StaleDbRoutingVersion>(obj["db"].String(), + DatabaseVersion(obj["vReceived"].Obj()), + !obj["vWanted"].eoo() + ? DatabaseVersion(obj["vWanted"].Obj()) : boost::optional<DatabaseVersion>{}); } diff --git a/src/mongo/s/stale_exception.h b/src/mongo/s/stale_exception.h index d67e3415731..d82b18bab52 100644 --- a/src/mongo/s/stale_exception.h +++ b/src/mongo/s/stale_exception.h @@ -74,7 +74,6 @@ public: void serialize(BSONObjBuilder* bob) const; static std::shared_ptr<const ErrorExtraInfo> parse(const BSONObj& obj); - static StaleConfigInfo parseFromCommandError(const BSONObj& obj); protected: NamespaceString _nss; @@ -96,17 +95,8 @@ public: return _nss; } - void serialize(BSONObjBuilder* bob) const { - bob->append("ns", _nss.ns()); - } - - static std::shared_ptr<const ErrorExtraInfo> parse(const BSONObj& obj) { - return std::make_shared<StaleEpochInfo>(parseFromCommandError(obj)); - } - - static StaleEpochInfo parseFromCommandError(const BSONObj& obj) { - return StaleEpochInfo(NamespaceString(obj["ns"].String())); - } + void serialize(BSONObjBuilder* bob) const; + static std::shared_ptr<const ErrorExtraInfo> parse(const BSONObj& obj); private: NamespaceString _nss; @@ -146,7 +136,6 @@ public: void serialize(BSONObjBuilder* bob) const override; static std::shared_ptr<const ErrorExtraInfo> parse(const BSONObj&); - static StaleDbRoutingVersion parseFromCommandError(const BSONObj& commandError); private: std::string _db; diff --git a/src/mongo/s/stale_exception_test.cpp b/src/mongo/s/stale_exception_test.cpp index 0c0c23d32f1..57cb2b89062 100644 --- a/src/mongo/s/stale_exception_test.cpp +++ b/src/mongo/s/stale_exception_test.cpp @@ -27,23 +27,16 @@ * it in the license file. */ - -#include "mongo/platform/basic.h" - #include "mongo/s/shard_id.h" -#include "mongo/s/sharding_router_test_fixture.h" #include "mongo/s/stale_exception.h" -#include "mongo/util/assert_util.h" +#include "mongo/unittest/unittest.h" namespace mongo { - namespace { -using StaleExceptionTest = ShardingTestFixture; - const NamespaceString kNss("test.nss"); -TEST_F(StaleExceptionTest, StaleConfigInfoSerializationTest) { +TEST(StaleExceptionTest, StaleConfigInfoSerializationTest) { const ShardId kShardId("SHARD_ID"); StaleConfigInfo info(kNss, ChunkVersion::UNSHARDED(), ChunkVersion::UNSHARDED(), kShardId); @@ -53,15 +46,16 @@ TEST_F(StaleExceptionTest, StaleConfigInfoSerializationTest) { info.serialize(&bob); // Deserialize - auto deserializedInfo = StaleConfigInfo::parseFromCommandError(bob.obj()); + auto deserializedInfo = + std::static_pointer_cast<const StaleConfigInfo>(StaleConfigInfo::parse(bob.obj())); - ASSERT_EQUALS(deserializedInfo.getNss(), kNss); - ASSERT_EQUALS(deserializedInfo.getVersionReceived(), ChunkVersion::UNSHARDED()); - ASSERT_EQUALS(*deserializedInfo.getVersionWanted(), ChunkVersion::UNSHARDED()); - ASSERT_EQUALS(deserializedInfo.getShardId(), kShardId); + ASSERT_EQUALS(deserializedInfo->getNss(), kNss); + ASSERT_EQUALS(deserializedInfo->getVersionReceived(), ChunkVersion::UNSHARDED()); + ASSERT_EQUALS(*deserializedInfo->getVersionWanted(), ChunkVersion::UNSHARDED()); + ASSERT_EQUALS(deserializedInfo->getShardId(), kShardId); } -TEST_F(StaleExceptionTest, StaleEpochInfoSerializationTest) { +TEST(StaleExceptionTest, StaleEpochInfoSerializationTest) { StaleEpochInfo info(kNss); // Serialize @@ -69,9 +63,10 @@ TEST_F(StaleExceptionTest, StaleEpochInfoSerializationTest) { info.serialize(&bob); // Deserialize - auto deserializedInfo = StaleEpochInfo::parseFromCommandError(bob.obj()); + auto deserializedInfo = + std::static_pointer_cast<const StaleEpochInfo>(StaleEpochInfo::parse(bob.obj())); - ASSERT_EQUALS(deserializedInfo.getNss(), kNss); + ASSERT_EQUALS(deserializedInfo->getNss(), kNss); } } // namespace |