summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Polato <paolo.polato@mongodb.com>2022-02-10 13:41:13 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-02-21 22:44:09 +0000
commitc7699bb6bded2948ba33063c77ef3adb8fa78095 (patch)
tree202554032aa256e42aa72080f41d496ee1c21a04
parent88148a11a78d5d36dfad440b749a180269ad2e8d (diff)
downloadmongo-c7699bb6bded2948ba33063c77ef3adb8fa78095.tar.gz
SERVER-63203 Do not fail chunk split if more than 8192 points are requested
(cherry picked from commit d018b7e964dafa5085152af03916bde0ac74f947) (cherry picked from commit 125477c2b843bd02f387b4ec183bba9d641d349c)
-rw-r--r--src/mongo/db/s/chunk_splitter.cpp24
-rw-r--r--src/mongo/db/s/create_collection_coordinator.cpp12
-rw-r--r--src/mongo/db/s/split_chunk.cpp38
-rw-r--r--src/mongo/db/s/split_chunk.h2
-rw-r--r--src/mongo/db/s/split_chunk_command.cpp9
-rw-r--r--src/mongo/s/shard_util.cpp23
-rw-r--r--src/mongo/s/shard_util.h2
7 files changed, 63 insertions, 47 deletions
diff --git a/src/mongo/db/s/chunk_splitter.cpp b/src/mongo/db/s/chunk_splitter.cpp
index 936ac5be823..3f7704a655e 100644
--- a/src/mongo/db/s/chunk_splitter.cpp
+++ b/src/mongo/db/s/chunk_splitter.cpp
@@ -52,6 +52,7 @@
#include "mongo/s/config_server_client.h"
#include "mongo/s/grid.h"
#include "mongo/s/shard_key_pattern.h"
+#include "mongo/s/shard_util.h"
#include "mongo/util/assert_util.h"
namespace mongo {
@@ -84,22 +85,23 @@ Status splitChunkAtMultiplePoints(OperationContext* opCtx,
const ShardKeyPattern& shardKeyPattern,
const ChunkVersion& collectionVersion,
const ChunkRange& chunkRange,
- const std::vector<BSONObj>& splitPoints) {
+ std::vector<BSONObj>&& splitPoints) {
invariant(!splitPoints.empty());
- const size_t kMaxSplitPoints = 8192;
-
- if (splitPoints.size() > kMaxSplitPoints) {
- return {ErrorCodes::BadValue,
- str::stream() << "Cannot split chunk in more than " << kMaxSplitPoints
- << " parts at a time."};
+ if (splitPoints.size() > shardutil::kMaxSplitPoints) {
+ LOGV2_WARNING(6320301,
+ "Unable to apply all the split points received. Only the first "
+ "kMaxSplitPoints will be processed",
+ "numSplitPointsReceived"_attr = splitPoints.size(),
+ "kMaxSplitPoints"_attr = shardutil::kMaxSplitPoints);
+ splitPoints.resize(shardutil::kMaxSplitPoints);
}
return splitChunk(opCtx,
nss,
shardKeyPattern.toBSON(),
chunkRange,
- splitPoints,
+ std::move(splitPoints),
shardId.toString(),
collectionVersion.epoch())
.getStatus()
@@ -386,13 +388,15 @@ void ChunkSplitter::_runAutosplit(std::shared_ptr<ChunkSplitStateDriver> chunkSp
}
}
+ auto numSplitPoints = splitPoints.size();
+
uassertStatusOK(splitChunkAtMultiplePoints(opCtx.get(),
chunk.getShardId(),
nss,
shardKeyPattern,
cm.getVersion(),
chunk.getRange(),
- splitPoints));
+ std::move(splitPoints)));
chunkSplitStateDriver->commitSplit();
const bool shouldBalance = isAutoBalanceEnabled(opCtx.get(), nss, balancerConfig);
@@ -403,7 +407,7 @@ void ChunkSplitter::_runAutosplit(std::shared_ptr<ChunkSplitStateDriver> chunkSp
"autosplitted chunk",
"namespace"_attr = nss,
"chunk"_attr = redact(chunk.toString()),
- "splitPoints"_attr = splitPoints.size(),
+ "splitPoints"_attr = numSplitPoints,
"maxChunkSizeBytes"_attr = maxChunkSizeBytes,
"extraInfo"_attr =
(topChunkMinKey.isEmpty() ? ""
diff --git a/src/mongo/db/s/create_collection_coordinator.cpp b/src/mongo/db/s/create_collection_coordinator.cpp
index 3698c42761e..e7357b4a828 100644
--- a/src/mongo/db/s/create_collection_coordinator.cpp
+++ b/src/mongo/db/s/create_collection_coordinator.cpp
@@ -641,18 +641,18 @@ void CreateCollectionCoordinator::_checkCommandArguments(OperationContext* opCtx
if (_doc.getNumInitialChunks()) {
// Ensure numInitialChunks is within valid bounds.
- // Cannot have more than 8192 initial chunks per shard. Setting a maximum of 1,000,000
- // chunks in total to limit the amount of memory this command consumes so there is less
- // danger of an OOM error.
+ // Cannot have more than kMaxSplitPoints initial chunks per shard. Setting a maximum of
+ // 1,000,000 chunks in total to limit the amount of memory this command consumes so there is
+ // less danger of an OOM error.
const int maxNumInitialChunksForShards =
- Grid::get(opCtx)->shardRegistry()->getNumShardsNoReload() * 8192;
+ Grid::get(opCtx)->shardRegistry()->getNumShardsNoReload() * shardutil::kMaxSplitPoints;
const int maxNumInitialChunksTotal = 1000 * 1000; // Arbitrary limit to memory consumption
int numChunks = _doc.getNumInitialChunks().value();
uassert(ErrorCodes::InvalidOptions,
str::stream() << "numInitialChunks cannot be more than either: "
- << maxNumInitialChunksForShards << ", 8192 * number of shards; or "
- << maxNumInitialChunksTotal,
+ << maxNumInitialChunksForShards << ", " << shardutil::kMaxSplitPoints
+ << " * number of shards; or " << maxNumInitialChunksTotal,
numChunks >= 0 && numChunks <= maxNumInitialChunksForShards &&
numChunks <= maxNumInitialChunksTotal);
}
diff --git a/src/mongo/db/s/split_chunk.cpp b/src/mongo/db/s/split_chunk.cpp
index 54185941ec2..fc1ec6f26f6 100644
--- a/src/mongo/db/s/split_chunk.cpp
+++ b/src/mongo/db/s/split_chunk.cpp
@@ -90,14 +90,14 @@ bool checkIfSingleDoc(OperationContext* opCtx,
/**
* Checks the collection's metadata for a successful split on the specified chunkRange using the
- * specified splitKeys. Returns false if the metadata's chunks don't match the new chunk
+ * specified split points. Returns false if the metadata's chunks don't match the new chunk
* boundaries exactly.
*/
bool checkMetadataForSuccessfulSplitChunk(OperationContext* opCtx,
const NamespaceString& nss,
const OID& epoch,
const ChunkRange& chunkRange,
- const std::vector<BSONObj>& splitKeys) {
+ const std::vector<BSONObj>& splitPoints) {
AutoGetCollection autoColl(opCtx, nss, MODE_IS);
const auto metadataAfterSplit =
CollectionShardingRuntime::get(opCtx, nss)->getCurrentMetadataIfKnown();
@@ -106,19 +106,19 @@ bool checkMetadataForSuccessfulSplitChunk(OperationContext* opCtx,
str::stream() << "Collection " << nss.ns() << " changed since split start",
metadataAfterSplit && metadataAfterSplit->getShardVersion().epoch() == epoch);
- auto newChunkBounds(splitKeys);
- auto startKey = chunkRange.getMin();
- newChunkBounds.push_back(chunkRange.getMax());
-
ChunkType nextChunk;
- for (const auto& endKey : newChunkBounds) {
+ for (auto it = splitPoints.begin(); it != splitPoints.end(); ++it) {
// Check that all new chunks fit the new chunk boundaries
- if (!metadataAfterSplit->getNextChunk(startKey, &nextChunk) ||
- nextChunk.getMax().woCompare(endKey)) {
+ const auto& currentChunkMinKey = it == splitPoints.begin() ? chunkRange.getMin() : *it;
+ if (!metadataAfterSplit->getNextChunk(currentChunkMinKey, &nextChunk) ||
+ nextChunk.getMax().woCompare(*it)) {
return false;
}
-
- startKey = endKey;
+ }
+ // Special check for the last chunk produced.
+ if (!metadataAfterSplit->getNextChunk(splitPoints.back(), &nextChunk) ||
+ nextChunk.getMax().woCompare(chunkRange.getMax())) {
+ return false;
}
return true;
@@ -130,7 +130,7 @@ StatusWith<boost::optional<ChunkRange>> splitChunk(OperationContext* opCtx,
const NamespaceString& nss,
const BSONObj& keyPatternObj,
const ChunkRange& chunkRange,
- const std::vector<BSONObj>& splitKeys,
+ std::vector<BSONObj>&& splitPoints,
const std::string& shardName,
const OID& expectedCollectionEpoch) {
auto scopedSplitOrMergeChunk(uassertStatusOK(
@@ -140,8 +140,8 @@ StatusWith<boost::optional<ChunkRange>> splitChunk(OperationContext* opCtx,
// data types.
const auto hashedField = ShardKeyPattern::extractHashedField(keyPatternObj);
if (hashedField) {
- for (BSONObj splitKey : splitKeys) {
- auto hashedSplitElement = splitKey[hashedField.fieldName()];
+ for (const auto& splitPoint : splitPoints) {
+ auto hashedSplitElement = splitPoint[hashedField.fieldName()];
if (!ShardKeyPattern::isValidHashedValue(hashedSplitElement)) {
return {ErrorCodes::CannotSplit,
str::stream() << "splitChunk cannot split chunk " << chunkRange.toString()
@@ -153,8 +153,8 @@ StatusWith<boost::optional<ChunkRange>> splitChunk(OperationContext* opCtx,
}
// Commit the split to the config server.
- auto request =
- SplitChunkRequest(nss, shardName, expectedCollectionEpoch, chunkRange, splitKeys);
+ auto request = SplitChunkRequest(
+ nss, shardName, expectedCollectionEpoch, chunkRange, std::move(splitPoints));
auto configCmdObj =
request.toConfigCommandBSON(ShardingCatalogClient::kMajorityWriteConcern.toBSON());
@@ -204,7 +204,7 @@ StatusWith<boost::optional<ChunkRange>> splitChunk(OperationContext* opCtx,
if (!commandStatus.isOK() || !writeConcernStatus.isOK()) {
if (checkMetadataForSuccessfulSplitChunk(
- opCtx, nss, expectedCollectionEpoch, chunkRange, splitKeys)) {
+ opCtx, nss, expectedCollectionEpoch, chunkRange, request.getSplitPoints())) {
// Split was committed.
} else if (!commandStatus.isOK()) {
return commandStatus;
@@ -231,12 +231,12 @@ StatusWith<boost::optional<ChunkRange>> splitChunk(OperationContext* opCtx,
}
auto backChunk = ChunkType();
- backChunk.setMin(splitKeys.back());
+ backChunk.setMin(request.getSplitPoints().back());
backChunk.setMax(chunkRange.getMax());
auto frontChunk = ChunkType();
frontChunk.setMin(chunkRange.getMin());
- frontChunk.setMax(splitKeys.front());
+ frontChunk.setMax(request.getSplitPoints().front());
KeyPattern shardKeyPattern(keyPatternObj);
if (shardKeyPattern.globalMax().woCompare(backChunk.getMax()) == 0 &&
diff --git a/src/mongo/db/s/split_chunk.h b/src/mongo/db/s/split_chunk.h
index 6f394d397e5..16a75f0b94f 100644
--- a/src/mongo/db/s/split_chunk.h
+++ b/src/mongo/db/s/split_chunk.h
@@ -59,7 +59,7 @@ StatusWith<boost::optional<ChunkRange>> splitChunk(OperationContext* opCtx,
const NamespaceString& nss,
const BSONObj& keyPatternObj,
const ChunkRange& chunkRange,
- const std::vector<BSONObj>& splitKeys,
+ std::vector<BSONObj>&& splitPoints,
const std::string& shardName,
const OID& expectedCollectionEpoch);
diff --git a/src/mongo/db/s/split_chunk_command.cpp b/src/mongo/db/s/split_chunk_command.cpp
index 9a17b009b2d..483a651a4f1 100644
--- a/src/mongo/db/s/split_chunk_command.cpp
+++ b/src/mongo/db/s/split_chunk_command.cpp
@@ -139,8 +139,13 @@ public:
OID expectedCollectionEpoch;
uassertStatusOK(bsonExtractOIDField(cmdObj, "epoch", &expectedCollectionEpoch));
- auto topChunk = uassertStatusOK(splitChunk(
- opCtx, nss, keyPatternObj, chunkRange, splitKeys, shardName, expectedCollectionEpoch));
+ auto topChunk = uassertStatusOK(splitChunk(opCtx,
+ nss,
+ keyPatternObj,
+ chunkRange,
+ std::move(splitKeys),
+ shardName,
+ expectedCollectionEpoch));
// Otherwise, we want to check whether or not top-chunk optimization should be performed. If
// yes, then we should have a ChunkRange that was returned. Regardless of whether it should
diff --git a/src/mongo/s/shard_util.cpp b/src/mongo/s/shard_util.cpp
index 869750458f1..0265e611ede 100644
--- a/src/mongo/s/shard_util.cpp
+++ b/src/mongo/s/shard_util.cpp
@@ -162,28 +162,33 @@ StatusWith<boost::optional<ChunkRange>> splitChunkAtMultiplePoints(
const std::vector<BSONObj>& splitPoints) {
invariant(!splitPoints.empty());
- const size_t kMaxSplitPoints = 8192;
+ auto splitPointsBeginIt = splitPoints.begin();
+ auto splitPointsEndIt = splitPoints.end();
if (splitPoints.size() > kMaxSplitPoints) {
- return {ErrorCodes::BadValue,
- str::stream() << "Cannot split chunk in more than " << kMaxSplitPoints
- << " parts at a time."};
+ LOGV2_WARNING(6320300,
+ "Unable to apply all the split points received. Only the first "
+ "kMaxSplitPoints will be processed",
+ "numSplitPointsReceived"_attr = splitPoints.size(),
+ "kMaxSplitPoints"_attr = kMaxSplitPoints);
+ splitPointsEndIt = std::next(splitPointsBeginIt, kMaxSplitPoints);
}
// Sanity check that we are not attempting to split at the boundaries of the chunk. This check
// is already performed at chunk split commit time, but we are performing it here for parity
// with old auto-split code, which might rely on it.
- if (SimpleBSONObjComparator::kInstance.evaluate(chunkRange.getMin() == splitPoints.front())) {
+ if (SimpleBSONObjComparator::kInstance.evaluate(chunkRange.getMin() == *splitPointsBeginIt)) {
const std::string msg(str::stream()
<< "not splitting chunk " << chunkRange.toString() << ", split point "
- << splitPoints.front() << " is exactly on chunk bounds");
+ << *splitPointsBeginIt << " is exactly on chunk bounds");
return {ErrorCodes::CannotSplit, msg};
}
- if (SimpleBSONObjComparator::kInstance.evaluate(chunkRange.getMax() == splitPoints.back())) {
+ const auto& lastSplitPoint = *std::prev(splitPointsEndIt);
+ if (SimpleBSONObjComparator::kInstance.evaluate(chunkRange.getMax() == lastSplitPoint)) {
const std::string msg(str::stream()
<< "not splitting chunk " << chunkRange.toString() << ", split point "
- << splitPoints.back() << " is exactly on chunk bounds");
+ << lastSplitPoint << " is exactly on chunk bounds");
return {ErrorCodes::CannotSplit, msg};
}
@@ -195,7 +200,7 @@ StatusWith<boost::optional<ChunkRange>> splitChunkAtMultiplePoints(
collectionVersion.appendWithField(
&cmd, ChunkVersion::kShardVersionField); // backwards compatibility with v3.4
chunkRange.append(&cmd);
- cmd.append("splitKeys", splitPoints);
+ cmd.append("splitKeys", splitPointsBeginIt, splitPointsEndIt);
BSONObj cmdObj = cmd.obj();
diff --git a/src/mongo/s/shard_util.h b/src/mongo/s/shard_util.h
index 36e023b134d..ed696455d36 100644
--- a/src/mongo/s/shard_util.h
+++ b/src/mongo/s/shard_util.h
@@ -51,6 +51,8 @@ class StatusWith;
*/
namespace shardutil {
+static constexpr size_t kMaxSplitPoints = 8192;
+
/**
* Executes the listDatabases command against the specified shard and obtains the total data
* size across all databases in bytes (essentially, the totalSize field).