summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTommaso Tocci <tommaso.tocci@mongodb.com>2022-02-04 08:40:22 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-02-04 09:07:34 +0000
commite221ce7f62fc9fa22c11544525f49759eb003c43 (patch)
tree1d5b690fc8e0e78ce0b301023cd6a89d4c1a8d36
parent9a2d69fafebf49dd07c7672b5df65f1b871afb64 (diff)
downloadmongo-e221ce7f62fc9fa22c11544525f49759eb003c43.tar.gz
SERVER-63239 Do not throw exception in AutoSplitVector on empty ranges
-rw-r--r--src/mongo/db/s/auto_split_vector.cpp92
-rw-r--r--src/mongo/db/s/auto_split_vector_test.cpp32
2 files changed, 63 insertions, 61 deletions
diff --git a/src/mongo/db/s/auto_split_vector.cpp b/src/mongo/db/s/auto_split_vector.cpp
index bdf3220dea5..a8f30e5742c 100644
--- a/src/mongo/db/s/auto_split_vector.cpp
+++ b/src/mongo/db/s/auto_split_vector.cpp
@@ -81,49 +81,6 @@ const std::tuple<BSONObj, BSONObj> getMinMaxExtendedBounds(
}
/*
- * Returns true if the final key in the range is the same as the first key, false otherwise.
- */
-bool maxKeyEqualToMinKey(OperationContext* opCtx,
- const CollectionPtr* collection,
- const IndexCatalog::ShardKeyIndex& shardKeyIdx,
- const BSONObj& minBound,
- const BSONObj& maxBound,
- const BSONObj& minKeyInChunk) {
- BSONObj maxKeyInChunk;
- {
- auto backwardIdxScanner =
- InternalPlanner::shardKeyIndexScan(opCtx,
- collection,
- shardKeyIdx,
- maxBound,
- minBound,
- BoundInclusion::kIncludeEndKeyOnly,
- PlanYieldPolicy::YieldPolicy::YIELD_AUTO,
- InternalPlanner::BACKWARD);
-
- PlanExecutor::ExecState state = backwardIdxScanner->getNext(&maxKeyInChunk, nullptr);
- uassert(ErrorCodes::OperationFailed,
- "can't open a cursor to find final key in range (desired range is possibly empty)",
- state == PlanExecutor::ADVANCED);
- }
-
- if (minKeyInChunk.woCompare(maxKeyInChunk) == 0) {
- // Range contains only documents with a single key value. So we cannot possibly find a
- // split point, and there is no need to scan any further.
- LOGV2_WARNING(
- 5865001,
- "Possible low cardinality key detected in range. Range contains only a single key.",
- "namespace"_attr = collection->get()->ns(),
- "minKey"_attr = redact(prettyKey(shardKeyIdx.keyPattern(), minBound)),
- "maxKey"_attr = redact(prettyKey(shardKeyIdx.keyPattern(), maxBound)),
- "key"_attr = redact(prettyKey(shardKeyIdx.keyPattern(), minKeyInChunk)));
- return true;
- }
-
- return false;
-}
-
-/*
* Reshuffle fields according to the shard key pattern.
*/
auto orderShardKeyFields(const BSONObj& keyPattern, BSONObj& key) {
@@ -149,7 +106,9 @@ std::vector<BSONObj> autoSplitVector(OperationContext* opCtx,
{
AutoGetCollection collection(opCtx, nss, MODE_IS);
- uassert(ErrorCodes::NamespaceNotFound, "ns not found", collection);
+ uassert(ErrorCodes::NamespaceNotFound,
+ str::stream() << "namespace " << nss << " does not exists",
+ collection);
// Get the size estimate for this namespace
const long long totalLocalCollDocuments = collection->numRecords(opCtx);
@@ -188,23 +147,46 @@ std::vector<BSONObj> autoSplitVector(OperationContext* opCtx,
{
PlanExecutor::ExecState state =
forwardIdxScanner->getNext(&minKeyInOriginalChunk, nullptr);
- uassert(ErrorCodes::OperationFailed,
- "can't open a cursor to scan the range (desired range is possibly empty)",
- state == PlanExecutor::ADVANCED);
+ if (state == PlanExecutor::IS_EOF) {
+ // Range is empty
+ return {};
+ }
+ }
+
+ BSONObj maxKeyInChunk;
+ {
+ auto backwardIdxScanner =
+ InternalPlanner::shardKeyIndexScan(opCtx,
+ &(*collection),
+ *shardKeyIdx,
+ maxKey,
+ minKey,
+ BoundInclusion::kIncludeEndKeyOnly,
+ PlanYieldPolicy::YieldPolicy::YIELD_AUTO,
+ InternalPlanner::BACKWARD);
+
+ PlanExecutor::ExecState state = backwardIdxScanner->getNext(&maxKeyInChunk, nullptr);
+ if (state == PlanExecutor::IS_EOF) {
+ // Range is empty
+ return {};
+ }
}
- // Return empty vector if chunk's min and max keys are the same.
- if (maxKeyEqualToMinKey(opCtx,
- &collection.getCollection(),
- *shardKeyIdx,
- minKey,
- maxKey,
- minKeyInOriginalChunk)) {
+ if (minKeyInOriginalChunk.woCompare(maxKeyInChunk) == 0) {
+ // Range contains only documents with a single key value. So we cannot possibly find a
+ // split point, and there is no need to scan any further.
+ LOGV2_WARNING(
+ 5865001,
+ "Possible low cardinality key detected in range. Range contains only a single key.",
+ "namespace"_attr = collection.getNss(),
+ "minKey"_attr = redact(prettyKey(keyPattern, minKey)),
+ "maxKey"_attr = redact(prettyKey(keyPattern, maxKey)),
+ "key"_attr = redact(prettyKey(shardKeyIdx->keyPattern(), minKeyInOriginalChunk)));
return {};
}
LOGV2(5865000,
- "Requested split points lookup for chunk",
+ "Requested split points lookup for range",
"namespace"_attr = nss,
"minKey"_attr = redact(prettyKey(keyPattern, minKey)),
"maxKey"_attr = redact(prettyKey(keyPattern, maxKey)));
diff --git a/src/mongo/db/s/auto_split_vector_test.cpp b/src/mongo/db/s/auto_split_vector_test.cpp
index d93a6065a85..45371606529 100644
--- a/src/mongo/db/s/auto_split_vector_test.cpp
+++ b/src/mongo/db/s/auto_split_vector_test.cpp
@@ -121,26 +121,46 @@ class AutoSplitVectorTest10MB : public AutoSplitVectorTest {
auto opCtx = operationContext();
- DBDirectClient client(opCtx);
- client.createIndex(kNss.ns(), BSON(kPattern << 1));
-
insertNDocsOf1MB(opCtx, 10 /* nDocs */);
+
+ DBDirectClient client(opCtx);
ASSERT_EQUALS(10, client.count(kNss));
}
};
// Throw exception upon calling autoSplitVector on dropped/unexisting collection
-TEST_F(AutoSplitVectorTest10MB, NoCollection) {
+TEST_F(AutoSplitVectorTest, NoCollection) {
ASSERT_THROWS_CODE(autoSplitVector(operationContext(),
NamespaceString("dummy", "collection"),
BSON(kPattern << 1) /* shard key pattern */,
- BSON(kPattern << 0) /* min */,
- BSON(kPattern << 100) /* max */,
+ BSON(kPattern << kMinBSONKey) /* min */,
+ BSON(kPattern << kMaxBSONKey) /* max */,
1 * 1024 * 1024 /* max chunk size in bytes*/),
DBException,
ErrorCodes::NamespaceNotFound);
}
+TEST_F(AutoSplitVectorTest, EmptyCollection) {
+ const auto splitKey = autoSplitVector(operationContext(),
+ kNss,
+ BSON(kPattern << 1) /* shard key pattern */,
+ BSON(kPattern << kMinBSONKey) /* min */,
+ BSON(kPattern << kMaxBSONKey) /* max */,
+ 1 * 1024 * 1024 /* max chunk size in bytes*/);
+ ASSERT_EQ(0, splitKey.size());
+}
+
+TEST_F(AutoSplitVectorTest10MB, EmptyRange) {
+ const auto splitKey = autoSplitVector(operationContext(),
+ kNss,
+ BSON(kPattern << 1) /* shard key pattern */,
+ BSON(kPattern << kMinBSONKey) /* min */,
+ BSON(kPattern << -10) /* max */,
+ 1 * 1024 * 1024 /* max chunk size in bytes*/);
+ ASSERT_EQ(0, splitKey.size());
+}
+
+
// No split points if estimated `data size < max chunk size`
TEST_F(AutoSplitVectorTest10MB, NoSplitIfDataLessThanMaxChunkSize) {
std::vector<BSONObj> splitKeys = autoSplit(operationContext(), 11 /* maxChunkSizeMB */);