summaryrefslogtreecommitdiff
path: root/src/mongo/s/chunk_map_test.cpp
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-14 14:26:38 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-14 14:26:38 -0300
commit294bc6ecabf14c09c9bc8644704921dcf97cb44e (patch)
tree279b1e0bab53901a1647ac63c1c724f0f789a663 /src/mongo/s/chunk_map_test.cpp
parent70be7c27a251621187a1de533462ae2bb1e3bd39 (diff)
parent1e917fd798aa25b7066d4b414b51184f13d5a092 (diff)
Update upstream source from tag 'upstream/6.0.10'debian/6.0.10-1
Update to upstream version '6.0.10' with Debian dir 2d176fa254eee97b139f712fec5709641335a8c3
Diffstat (limited to 'src/mongo/s/chunk_map_test.cpp')
-rw-r--r--src/mongo/s/chunk_map_test.cpp455
1 files changed, 434 insertions, 21 deletions
diff --git a/src/mongo/s/chunk_map_test.cpp b/src/mongo/s/chunk_map_test.cpp
index 6514fc00745..96980c009f7 100644
--- a/src/mongo/s/chunk_map_test.cpp
+++ b/src/mongo/s/chunk_map_test.cpp
@@ -27,18 +27,66 @@
* it in the license file.
*/
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kTest
+
#include "mongo/platform/basic.h"
+#include "mongo/logv2/log.h"
+#include "mongo/platform/random.h"
#include "mongo/s/chunk_manager.h"
+#include "mongo/s/chunk_writes_tracker.h"
+#include "mongo/s/chunks_test_util.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
+using chunks_test_util::assertEqualChunkInfo;
+using chunks_test_util::calculateCollVersion;
+using chunks_test_util::calculateIntermediateShardKey;
+using chunks_test_util::genChunkVector;
+using chunks_test_util::genRandomSplitPoints;
+using chunks_test_util::performRandomChunkOperations;
+
namespace {
-const NamespaceString kNss("TestDB", "TestColl");
+PseudoRandom _random{SecureRandom().nextInt64()};
+
const ShardId kThisShard("testShard");
+ShardVersionMap getShardVersionMap(const ChunkMap& chunkMap) {
+ return chunkMap.getShardVersionsMap();
+}
+
+std::map<ShardId, ChunkVersion> calculateShardVersions(
+ const std::vector<std::shared_ptr<ChunkInfo>>& chunkVector) {
+ std::map<ShardId, ChunkVersion> svMap;
+ for (const auto& chunk : chunkVector) {
+ auto mapIt = svMap.find(chunk->getShardId());
+ if (mapIt == svMap.end()) {
+ svMap.emplace(chunk->getShardId(), chunk->getLastmod());
+ continue;
+ }
+ if (mapIt->second.isOlderThan(chunk->getLastmod())) {
+ mapIt->second = chunk->getLastmod();
+ }
+ }
+ return svMap;
+}
+
+std::vector<std::shared_ptr<ChunkInfo>> toChunkInfoPtrVector(
+ const std::vector<ChunkType>& chunkTypes, bool initializeWriteTrackerRandom = true) {
+ std::vector<std::shared_ptr<ChunkInfo>> chunkPtrs;
+ chunkPtrs.reserve(chunkTypes.size());
+ for (const auto& chunkType : chunkTypes) {
+ auto chunkInfoPtr = std::make_shared<ChunkInfo>(chunkType);
+ if (initializeWriteTrackerRandom) {
+ chunkInfoPtr->getWritesTracker()->addBytesWritten(_random.nextInt64(30));
+ }
+ chunkPtrs.push_back(std::move(chunkInfoPtr));
+ }
+ return chunkPtrs;
+}
+
class ChunkMapTest : public unittest::Test {
public:
const KeyPattern& getShardKeyPattern() const {
@@ -49,16 +97,36 @@ public:
return _uuid;
}
+ const OID& collEpoch() const {
+ return _epoch;
+ }
+
+ const Timestamp& collTimestamp() const {
+ return _collTimestamp;
+ }
+
+ ChunkMap makeChunkMap(const std::vector<std::shared_ptr<ChunkInfo>>& chunks) const {
+ const auto chunkBucketSize =
+ static_cast<size_t>(_random.nextInt64(chunks.size() * 1.2) + 1);
+ LOGV2(7162701, "Creating new chunk map", "chunkBucketSize"_attr = chunkBucketSize);
+ return ChunkMap{collEpoch(), collTimestamp(), chunkBucketSize}.createMerged(chunks);
+ }
+
+ std::vector<ChunkType> genRandomChunkVector(size_t maxNumChunks = 30,
+ size_t minNumChunks = 1) const {
+ return chunks_test_util::genRandomChunkVector(
+ _uuid, _epoch, _collTimestamp, maxNumChunks, minNumChunks);
+ }
+
private:
- KeyPattern _shardKeyPattern{BSON("a" << 1)};
+ KeyPattern _shardKeyPattern{chunks_test_util::kShardKeyPattern};
const UUID _uuid = UUID::gen();
+ const OID _epoch{OID::gen()};
+ const Timestamp _collTimestamp{1, 1};
};
-} // namespace
-
TEST_F(ChunkMapTest, TestAddChunk) {
- const OID epoch = OID::gen();
- ChunkVersion version{1, 0, epoch, Timestamp(1, 1)};
+ ChunkVersion version{1, 0, collEpoch(), collTimestamp()};
auto chunk = std::make_shared<ChunkInfo>(
ChunkType{uuid(),
@@ -66,18 +134,258 @@ TEST_F(ChunkMapTest, TestAddChunk) {
version,
kThisShard});
- ChunkMap chunkMap{epoch, Timestamp(1, 1)};
- auto newChunkMap = chunkMap.createMerged({chunk});
+ auto newChunkMap = makeChunkMap({chunk});
ASSERT_EQ(newChunkMap.size(), 1);
}
+TEST_F(ChunkMapTest, ConstructChunkMapRandom) {
+ auto chunkVector = toChunkInfoPtrVector(genRandomChunkVector());
+
+ const auto expectedShardVersions = calculateShardVersions(chunkVector);
+ const auto expectedCollVersion = calculateCollVersion(expectedShardVersions);
+
+ const auto chunkMap = makeChunkMap(chunkVector);
+
+ // Check that it contains all the chunks
+ ASSERT_EQ(chunkVector.size(), chunkMap.size());
+ // Check collection version
+ ASSERT_EQ(expectedCollVersion, chunkMap.getVersion());
+
+ size_t i = 0;
+ chunkMap.forEach([&](const auto& chunkPtr) {
+ const auto& expectedChunkPtr = chunkVector[i++];
+ assertEqualChunkInfo(*expectedChunkPtr, *chunkPtr);
+ return true;
+ });
+
+ // Validate all shard versions
+ const auto shardVersions = getShardVersionMap(chunkMap);
+ ASSERT_EQ(expectedShardVersions.size(), shardVersions.size());
+ for (const auto& mapIt : shardVersions) {
+ ASSERT_EQ(expectedShardVersions.at(mapIt.first), mapIt.second.shardVersion);
+ }
+
+ // Check that vectors are balanced in size
+ auto maxVectorSize = static_cast<size_t>(std::lround(chunkMap.getMaxChunkVectorSize() * 1.5));
+ auto minVectorSize = std::min(
+ chunkMap.size(), static_cast<size_t>(std::lround(chunkMap.getMaxChunkVectorSize() / 2)));
+
+ for (const auto& [maxKeyString, chunkVectorPtr] : chunkMap.getChunkVectorMap()) {
+ ASSERT_GTE(chunkVectorPtr->size(), minVectorSize);
+ ASSERT_LTE(chunkVectorPtr->size(), maxVectorSize);
+ }
+}
+
+TEST_F(ChunkMapTest, ConstructChunkMapRandomAllChunksSameVersion) {
+ auto chunkVector = genRandomChunkVector();
+ auto commonVersion = chunkVector.front().getVersion();
+
+ // Set same version on all chunks
+ for (auto& chunk : chunkVector) {
+ chunk.setVersion(commonVersion);
+ }
+
+ auto chunkInfoVector = toChunkInfoPtrVector(chunkVector);
+ const auto expectedShardVersions = calculateShardVersions(chunkInfoVector);
+ const auto expectedCollVersion = calculateCollVersion(expectedShardVersions);
+
+ ASSERT_EQ(commonVersion, expectedCollVersion);
+
+ const auto chunkMap = makeChunkMap(chunkInfoVector);
+
+ // Check that it contains all the chunks
+ ASSERT_EQ(chunkInfoVector.size(), chunkMap.size());
+ // Check collection version
+ ASSERT_EQ(expectedCollVersion, chunkMap.getVersion());
+
+ size_t i = 0;
+ chunkMap.forEach([&](const auto& chunkPtr) {
+ const auto& expectedChunkPtr = chunkInfoVector[i++];
+ assertEqualChunkInfo(*expectedChunkPtr, *chunkPtr);
+ return true;
+ });
+
+ // Validate all shard versions
+ const auto shardVersions = getShardVersionMap(chunkMap);
+ ASSERT_EQ(expectedShardVersions.size(), shardVersions.size());
+ for (const auto& mapIt : shardVersions) {
+ ASSERT_EQ(expectedShardVersions.at(mapIt.first), mapIt.second.shardVersion);
+ }
+}
+
+/*
+ * Check that constucting a ChunkMap with chunks that have mismatching timestamp fails.
+ */
+TEST_F(ChunkMapTest, ConstructChunkMapMismatchingTimestamp) {
+ auto chunkVector = toChunkInfoPtrVector(genRandomChunkVector());
+
+ // Set a different epoch in one of the chunks
+ const Timestamp wrongTimestamp{Date_t::now()};
+ ASSERT_NE(wrongTimestamp, collTimestamp());
+ const auto wrongChunkIdx = _random.nextInt32(chunkVector.size());
+ const auto oldChunk = chunkVector.at(wrongChunkIdx);
+ const auto oldVersion = oldChunk->getLastmod();
+ const ChunkVersion wrongVersion{
+ oldVersion.majorVersion(), oldVersion.minorVersion(), collEpoch(), wrongTimestamp};
+ chunkVector[wrongChunkIdx] = std::make_shared<ChunkInfo>(
+ ChunkType{uuid(), oldChunk->getRange(), wrongVersion, oldChunk->getShardId()});
+
+ ASSERT_THROWS_CODE(
+ makeChunkMap(chunkVector), AssertionException, ErrorCodes::ConflictingOperationInProgress);
+}
+
+TEST_F(ChunkMapTest, UpdateMapNotLeaveSmallVectors) {
+ const ChunkVersion initialVersion{1, 0, collEpoch(), collTimestamp()};
+ auto chunkVector = toChunkInfoPtrVector(
+ genChunkVector(uuid(), genRandomSplitPoints(8), initialVersion, 1 /*numShards*/));
+
+ const auto chunkBucketSize = 4;
+ LOGV2(7162703, "Constructing new chunk map", "chunkBucketSize"_attr = chunkBucketSize);
+ const auto initialChunkMap =
+ ChunkMap(collEpoch(), collTimestamp(), chunkBucketSize).createMerged(chunkVector);
+
+ // Check that it contains all the chunks
+ ASSERT_EQ(chunkVector.size(), initialChunkMap.size());
+
+ auto mergedVersion = initialChunkMap.getVersion();
+ mergedVersion.incMinor();
+
+ auto mergedChunk = std::make_shared<ChunkInfo>(ChunkType{
+ uuid(),
+ ChunkRange{chunkVector[4]->getRange().getMin(), chunkVector.back()->getRange().getMax()},
+ mergedVersion,
+ kThisShard});
+ const auto chunkMap = initialChunkMap.createMerged({mergedChunk});
+
+ // Check that vectors are balanced in size
+ auto maxVectorSize = std::lround(chunkMap.getMaxChunkVectorSize() * 1.5);
+ auto minVectorSize = std::min(
+ chunkMap.size(), static_cast<size_t>(std::lround(chunkMap.getMaxChunkVectorSize() / 2)));
+
+ for (const auto& [maxKeyString, chunkVectorPtr] : chunkMap.getChunkVectorMap()) {
+ ASSERT_GTE(chunkVectorPtr->size(), minVectorSize);
+ ASSERT_LTE(chunkVectorPtr->size(), maxVectorSize);
+ }
+}
+
+/*
+ * Check that updating a ChunkMap with chunks that have mismatching timestamp fails.
+ */
+TEST_F(ChunkMapTest, UpdateChunkMapMismatchingTimestamp) {
+ auto chunkVector = toChunkInfoPtrVector(genRandomChunkVector());
+
+ auto chunkMap = makeChunkMap(chunkVector);
+ auto collVersion = chunkMap.getVersion();
+
+ // Set a different epoch in one of the chunks
+ const Timestamp wrongTimestamp{Date_t::now()};
+ const auto wrongChunkIdx = _random.nextInt32(chunkVector.size());
+ const auto oldChunk = chunkVector.at(wrongChunkIdx);
+ const ChunkVersion wrongVersion{
+ collVersion.majorVersion(), collVersion.minorVersion(), collEpoch(), wrongTimestamp};
+ auto updateChunk = std::make_shared<ChunkInfo>(
+ ChunkType{uuid(), oldChunk->getRange(), wrongVersion, oldChunk->getShardId()});
+
+ ASSERT_THROWS_CODE(chunkMap.createMerged({updateChunk}),
+ AssertionException,
+ ErrorCodes::ConflictingOperationInProgress);
+}
+
+/*
+ * Check that updating a ChunkMap with chunks that have lower version fails.
+ */
+TEST_F(ChunkMapTest, UpdateChunkMapLowerVersion) {
+ auto chunkVector = toChunkInfoPtrVector(genRandomChunkVector());
+
+ auto chunkMap = makeChunkMap(chunkVector);
+
+ const auto wrongChunkIdx = _random.nextInt32(chunkVector.size());
+ const auto oldChunk = chunkVector.at(wrongChunkIdx);
+ const ChunkVersion wrongVersion{0, 1, collEpoch(), collTimestamp()};
+ auto updateChunk = std::make_shared<ChunkInfo>(
+ ChunkType{uuid(), oldChunk->getRange(), wrongVersion, oldChunk->getShardId()});
+
+ ASSERT_THROWS_CODE(chunkMap.createMerged({updateChunk}), AssertionException, 626840);
+}
+/*
+ * Test update of ChunkMap with random chunk manipulation (splits/merges/moves);
+ */
+TEST_F(ChunkMapTest, UpdateChunkMapRandom) {
+ auto initialChunks = genRandomChunkVector();
+ auto initialChunksInfo = toChunkInfoPtrVector(initialChunks);
+
+ const auto initialChunkMap = makeChunkMap(initialChunksInfo);
+
+ const auto initialShardVersions = calculateShardVersions(initialChunksInfo);
+ const auto initialCollVersion = calculateCollVersion(initialShardVersions);
+
+ auto chunks = initialChunks;
+
+ const auto maxNumChunkOps = 2 * initialChunks.size();
+ const auto numChunkOps = _random.nextInt32(maxNumChunkOps);
+ performRandomChunkOperations(&chunks, numChunkOps);
+
+ auto chunksInfo = toChunkInfoPtrVector(initialChunks, false /* initializeWriteTrackerRandom */);
+
+ std::vector<std::shared_ptr<ChunkInfo>> updatedChunksInfo;
+ for (auto& chunkPtr : chunksInfo) {
+ // First overlapping chunk in the initial vector
+ const auto& overlapInitChunk =
+ **std::lower_bound(initialChunksInfo.begin(),
+ initialChunksInfo.end(),
+ ShardKeyPattern::toKeyString(chunkPtr->getRange().getMin()),
+ [](const auto& chunkInfo, const std::string& shardKeyString) {
+ return chunkInfo->getMaxKeyString() <= shardKeyString;
+ });
+ // The new chunks inherits the written bytes from the first overlapping old chunk
+ chunkPtr->getWritesTracker()->addBytesWritten(
+ overlapInitChunk.getWritesTracker()->getBytesWritten());
+
+ if (!chunkPtr->getLastmod().isOlderOrEqualThan(initialCollVersion)) {
+ updatedChunksInfo.push_back(std::make_shared<ChunkInfo>(ChunkType{
+ uuid(), chunkPtr->getRange(), chunkPtr->getLastmod(), chunkPtr->getShardId()}));
+ }
+ }
+
+ const auto expectedShardVersions = calculateShardVersions(chunksInfo);
+ const auto expectedCollVersion = calculateCollVersion(expectedShardVersions);
+ auto chunkMap = initialChunkMap.createMerged(updatedChunksInfo);
+
+ // Check that it contains all the chunks
+ ASSERT_EQ(chunksInfo.size(), chunkMap.size());
+ // Check collection version
+ ASSERT_EQ(expectedCollVersion, chunkMap.getVersion());
+
+ size_t i = 0;
+ chunkMap.forEach([&](const auto& chunkPtr) {
+ const auto& expectedChunkPtr = chunksInfo[i++];
+ assertEqualChunkInfo(*expectedChunkPtr, *chunkPtr);
+ return true;
+ });
+
+ // Validate all shard versions
+ const auto shardVersions = getShardVersionMap(chunkMap);
+ ASSERT_EQ(expectedShardVersions.size(), shardVersions.size());
+ for (const auto& mapIt : shardVersions) {
+ ASSERT_EQ(expectedShardVersions.at(mapIt.first), mapIt.second.shardVersion);
+ }
+
+ // Check that vectors are balanced in size
+ auto maxVectorSize = static_cast<size_t>(std::lround(chunkMap.getMaxChunkVectorSize() * 1.5));
+ auto minVectorSize = std::min(
+ chunkMap.size(), static_cast<size_t>(std::lround(chunkMap.getMaxChunkVectorSize() / 2)));
+
+ for (const auto& [maxKeyString, chunkVectorPtr] : chunkMap.getChunkVectorMap()) {
+ ASSERT_GTE(chunkVectorPtr->size(), minVectorSize);
+ ASSERT_LTE(chunkVectorPtr->size(), maxVectorSize);
+ }
+}
+
TEST_F(ChunkMapTest, TestEnumerateAllChunks) {
- const OID epoch = OID::gen();
- ChunkMap chunkMap{epoch, Timestamp(1, 1)};
- ChunkVersion version{1, 0, epoch, Timestamp(1, 1)};
+ ChunkVersion version{1, 0, collEpoch(), collTimestamp()};
- auto newChunkMap = chunkMap.createMerged(
+ auto newChunkMap = makeChunkMap(
{std::make_shared<ChunkInfo>(
ChunkType{uuid(),
ChunkRange{getShardKeyPattern().globalMin(), BSON("a" << 0)},
@@ -107,12 +415,11 @@ TEST_F(ChunkMapTest, TestEnumerateAllChunks) {
ASSERT_EQ(count, newChunkMap.size());
}
+
TEST_F(ChunkMapTest, TestIntersectingChunk) {
- const OID epoch = OID::gen();
- ChunkMap chunkMap{epoch, Timestamp(1, 1)};
- ChunkVersion version{1, 0, epoch, Timestamp(1, 1)};
+ ChunkVersion version{1, 0, collEpoch(), collTimestamp()};
- auto newChunkMap = chunkMap.createMerged(
+ auto newChunkMap = makeChunkMap(
{std::make_shared<ChunkInfo>(
ChunkType{uuid(),
ChunkRange{getShardKeyPattern().globalMin(), BSON("a" << 0)},
@@ -135,14 +442,33 @@ TEST_F(ChunkMapTest, TestIntersectingChunk) {
SimpleBSONObjComparator::kInstance.evaluate(intersectingChunk->getMin() == BSON("a" << 0)));
ASSERT(SimpleBSONObjComparator::kInstance.evaluate(intersectingChunk->getMax() ==
BSON("a" << 100)));
+
+ // findIntersectingChunks returns last chunk if invoked with MaxKey
+ intersectingChunk =
+ newChunkMap.findIntersectingChunk(BSON("a" << getShardKeyPattern().globalMax()));
+ ASSERT(SimpleBSONObjComparator::kInstance.evaluate(intersectingChunk->getMin() ==
+ BSON("a" << 100)));
+ ASSERT(SimpleBSONObjComparator::kInstance.evaluate(intersectingChunk->getMax() ==
+ getShardKeyPattern().globalMax()));
+}
+
+TEST_F(ChunkMapTest, TestIntersectingChunkRandom) {
+ auto chunks = toChunkInfoPtrVector(genRandomChunkVector());
+
+ const auto chunkMap = makeChunkMap(chunks);
+
+ auto targetChunkIt = chunks.begin() + _random.nextInt64(chunks.size());
+ auto intermediateKey = calculateIntermediateShardKey(
+ (*targetChunkIt)->getMin(), (*targetChunkIt)->getMax(), 0.2 /* minKeyProb */);
+
+ auto intersectingChunkPtr = chunkMap.findIntersectingChunk(intermediateKey);
+ assertEqualChunkInfo(**(targetChunkIt), *intersectingChunkPtr);
}
TEST_F(ChunkMapTest, TestEnumerateOverlappingChunks) {
- const OID epoch = OID::gen();
- ChunkMap chunkMap{epoch, Timestamp(1, 1)};
- ChunkVersion version{1, 0, epoch, Timestamp(1, 1)};
+ ChunkVersion version{1, 0, collEpoch(), collTimestamp()};
- auto newChunkMap = chunkMap.createMerged(
+ auto newChunkMap = makeChunkMap(
{std::make_shared<ChunkInfo>(
ChunkType{uuid(),
ChunkRange{getShardKeyPattern().globalMin(), BSON("a" << 0)},
@@ -160,14 +486,101 @@ TEST_F(ChunkMapTest, TestEnumerateOverlappingChunks) {
auto min = BSON("a" << -50);
auto max = BSON("a" << 150);
-
int count = 0;
newChunkMap.forEachOverlappingChunk(min, max, true, [&](const auto& chunk) {
count++;
return true;
});
+ ASSERT_EQ(count, 3);
+ min = BSON("a" << -50);
+ max = BSON("a" << getShardKeyPattern().globalMax());
+ count = 0;
+ newChunkMap.forEachOverlappingChunk(min, max, false, [&](const auto& chunk) {
+ count++;
+ return true;
+ });
ASSERT_EQ(count, 3);
+
+ min = BSON("a" << 50);
+ max = BSON("a" << 100);
+ count = 0;
+ newChunkMap.forEachOverlappingChunk(min, max, true, [&](const auto& chunk) {
+ count++;
+ return true;
+ });
+ ASSERT_EQ(count, 2);
+
+ min = BSON("a" << 50);
+ max = BSON("a" << 100);
+ count = 0;
+ newChunkMap.forEachOverlappingChunk(min, max, false, [&](const auto& chunk) {
+ count++;
+ return true;
+ });
+ ASSERT_EQ(count, 1);
+}
+
+TEST_F(ChunkMapTest, ForEachNoShardKey) {
+ auto chunks = toChunkInfoPtrVector(genRandomChunkVector());
+
+ const auto chunkMap = makeChunkMap(chunks);
+
+ auto lastChunkIdx = std::max(_random.nextInt64(chunks.size()), static_cast<int64_t>(1));
+
+ int i = 0;
+ chunkMap.forEach([&](const auto& chunkInfo) {
+ assertEqualChunkInfo(*chunks[i], *chunkInfo);
+ return ++i < lastChunkIdx;
+ });
+
+ ASSERT_EQ(i, lastChunkIdx);
+}
+
+TEST_F(ChunkMapTest, ForEachWithShardKey) {
+ auto chunks = toChunkInfoPtrVector(genRandomChunkVector());
+
+ const auto chunkMap = makeChunkMap(chunks);
+
+ auto firstChunkIdx = static_cast<size_t>(_random.nextInt64(chunks.size()));
+ const auto& firstChunk = chunks[firstChunkIdx];
+ auto skey = calculateIntermediateShardKey(
+ firstChunk->getMin(), firstChunk->getMax(), 0.2 /* minKeyProb */);
+
+ size_t i = firstChunkIdx;
+ auto lastChunkIdx = firstChunkIdx +
+ std::max(_random.nextInt64(chunks.size() - firstChunkIdx), static_cast<int64_t>(1));
+ chunkMap.forEach(
+ [&](const auto& chunkInfo) {
+ assertEqualChunkInfo(*chunks[i], *chunkInfo);
+ return ++i < lastChunkIdx;
+ },
+ skey);
+
+ ASSERT_EQ(i, lastChunkIdx);
}
+TEST_F(ChunkMapTest, TestEnumerateOverlappingChunksRandom) {
+ auto chunks = toChunkInfoPtrVector(genRandomChunkVector());
+
+ const auto chunkMap = makeChunkMap(chunks);
+
+ auto firstChunkIt = chunks.begin() + _random.nextInt64(chunks.size());
+ auto lastChunkIt = firstChunkIt + _random.nextInt64(std::distance(firstChunkIt, chunks.end()));
+
+ auto minBound = calculateIntermediateShardKey(
+ (*firstChunkIt)->getMin(), (*firstChunkIt)->getMax(), 0.2 /* minKeyProb */);
+ auto maxBound = calculateIntermediateShardKey(
+ (*lastChunkIt)->getMin(), (*lastChunkIt)->getMax(), 0.2 /* minKeyProb */);
+
+ auto it = firstChunkIt;
+ chunkMap.forEachOverlappingChunk(minBound, maxBound, true, [&](const auto& chunkInfoPtr) {
+ assertEqualChunkInfo(**(it++), *chunkInfoPtr);
+ return true;
+ });
+ ASSERT_EQ(0, std::distance(it, std::next(lastChunkIt)));
+}
+
+} // namespace
+
} // namespace mongo