diff options
author | Tommaso Tocci <tommaso.tocci@mongodb.com> | 2022-02-04 08:40:22 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-02-04 09:07:34 +0000 |
commit | e221ce7f62fc9fa22c11544525f49759eb003c43 (patch) | |
tree | 1d5b690fc8e0e78ce0b301023cd6a89d4c1a8d36 | |
parent | 9a2d69fafebf49dd07c7672b5df65f1b871afb64 (diff) | |
download | mongo-e221ce7f62fc9fa22c11544525f49759eb003c43.tar.gz |
SERVER-63239 Do not throw exception in AutoSplitVector on empty ranges
-rw-r--r-- | src/mongo/db/s/auto_split_vector.cpp | 92 | ||||
-rw-r--r-- | src/mongo/db/s/auto_split_vector_test.cpp | 32 |
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 */); |