summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTommaso Tocci <tommaso.tocci@mongodb.com>2023-10-19 18:19:44 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-10-19 18:45:48 +0000
commit302c19437ae65b7d459360e18c5ac5086f494989 (patch)
tree2557991738fc752f25ee79229f9935d04d1ac2db
parentba9fd9d0750658bbe859e136950a13b25f1fc4fb (diff)
SERVER-82325 Config server could invariant during balancer roundr5.0.22-rc1r5.0.22
-rw-r--r--src/mongo/db/s/balancer/balancer_chunk_selection_policy_impl.cpp29
-rw-r--r--src/mongo/db/s/balancer/balancer_policy.cpp450
-rw-r--r--src/mongo/db/s/balancer/balancer_policy.h58
-rw-r--r--src/mongo/db/s/balancer/balancer_policy_test.cpp510
-rw-r--r--src/mongo/s/chunk_manager.h17
5 files changed, 456 insertions, 608 deletions
diff --git a/src/mongo/db/s/balancer/balancer_chunk_selection_policy_impl.cpp b/src/mongo/db/s/balancer/balancer_chunk_selection_policy_impl.cpp
index 07427b21879..8371a27b950 100644
--- a/src/mongo/db/s/balancer/balancer_chunk_selection_policy_impl.cpp
+++ b/src/mongo/db/s/balancer/balancer_chunk_selection_policy_impl.cpp
@@ -47,9 +47,9 @@
#include "mongo/s/catalog/type_collection.h"
#include "mongo/s/catalog/type_tags.h"
#include "mongo/s/catalog_cache.h"
-#include "mongo/s/chunk_manager.h"
#include "mongo/s/grid.h"
#include "mongo/util/str.h"
+
namespace mongo {
using MigrateInfoVector = BalancerChunkSelectionPolicy::MigrateInfoVector;
@@ -90,6 +90,27 @@ StatusWith<DistributionStatus> createCollectionDistributionStatus(
const NamespaceString& nss,
const ShardStatisticsVector& allShards,
const ChunkManager& chunkMgr) {
+ ShardToChunksMap shardToChunksMap;
+
+ // Makes sure there is an entry in shardToChunksMap for every shard, so empty shards will also
+ // be accounted for
+ for (const auto& stat : allShards) {
+ shardToChunksMap[stat.shardId];
+ }
+
+ chunkMgr.forEachChunk([&](const auto& chunkEntry) {
+ ChunkType chunk;
+ chunk.setNS(nss);
+ chunk.setMin(chunkEntry.getMin());
+ chunk.setMax(chunkEntry.getMax());
+ chunk.setJumbo(chunkEntry.isJumbo());
+ chunk.setShard(chunkEntry.getShardId());
+ chunk.setVersion(chunkEntry.getLastmod());
+
+ shardToChunksMap[chunkEntry.getShardId()].push_back(chunk);
+
+ return true;
+ });
auto swZoneInfo =
createCollectionZoneInfo(opCtx, nss, chunkMgr.getShardKeyPattern().getKeyPattern());
@@ -97,7 +118,7 @@ StatusWith<DistributionStatus> createCollectionDistributionStatus(
return swZoneInfo.getStatus();
}
- return {DistributionStatus{nss, std::move(swZoneInfo.getValue()), chunkMgr}};
+ return {DistributionStatus{nss, std::move(shardToChunksMap), std::move(swZoneInfo.getValue())}};
}
/**
@@ -505,7 +526,7 @@ Status BalancerChunkSelectionPolicyImpl::checkMoveAllowed(OperationContext* opCt
}
return BalancerPolicy::isShardSuitableReceiver(*newShardIterator,
- distribution.getTagForRange(chunk.getRange()));
+ distribution.getTagForChunk(chunk));
}
StatusWith<SplitInfoVector> BalancerChunkSelectionPolicyImpl::_getSplitCandidatesForCollection(
@@ -565,7 +586,7 @@ StatusWith<MigrateInfoVector> BalancerChunkSelectionPolicyImpl::_getMigrateCandi
const DistributionStatus& distribution = collInfoStatus.getValue();
- for (const auto& tagRangeEntry : distribution.getZoneInfo().zoneRanges()) {
+ for (const auto& tagRangeEntry : distribution.tagRanges()) {
const auto& tagRange = tagRangeEntry.second;
const auto chunkAtZoneMin = cm.findIntersectingChunkWithSimpleCollation(tagRange.min);
diff --git a/src/mongo/db/s/balancer/balancer_policy.cpp b/src/mongo/db/s/balancer/balancer_policy.cpp
index 4e11ab57cb8..ec4041b9667 100644
--- a/src/mongo/db/s/balancer/balancer_policy.cpp
+++ b/src/mongo/db/s/balancer/balancer_policy.cpp
@@ -58,134 +58,65 @@ namespace {
// optimal average across all shards for a zone for a rebalancing migration to be initiated.
const size_t kDefaultImbalanceThreshold = 1;
-ChunkType makeChunkType(const NamespaceString& nss, const Chunk& chunk) {
- ChunkType ct{nss, chunk.getRange(), chunk.getLastmod(), chunk.getShardId()};
- ct.setJumbo(chunk.isJumbo());
- return ct;
-}
-
-/**
- * Return a vector of zones after they have been normalized according to the given chunk
- * configuration.
- *
- * If a zone covers only partially a chunk, boundaries of that zone will be shrank so that the
- * normalized zone won't overlap with that chunk. The boundaries of a normalized zone will never
- * fall in the middle of a chunk.
- *
- * Additionally the vector will contain also zones for the "NoZone",
- */
-std::vector<ZoneRange> normalizeZones(const ChunkManager& cm, const ZoneInfo& zoneInfo) {
- std::vector<ZoneRange> normalizedRanges;
-
- auto lastMax = cm.getShardKeyPattern().getKeyPattern().globalMin();
-
- for (const auto& [max, zoneRange] : zoneInfo.zoneRanges()) {
- const auto& minChunk = cm.findIntersectingChunkWithSimpleCollation(zoneRange.min);
- const auto gtMin =
- SimpleBSONObjComparator::kInstance.evaluate(zoneRange.min > minChunk.getMin());
- const auto& normalizedMin = gtMin ? minChunk.getMax() : zoneRange.min;
-
-
- const auto& maxChunk = cm.findIntersectingChunkWithSimpleCollation(zoneRange.max);
- const auto gtMax =
- SimpleBSONObjComparator::kInstance.evaluate(zoneRange.max > maxChunk.getMin()) &&
- SimpleBSONObjComparator::kInstance.evaluate(
- zoneRange.max != cm.getShardKeyPattern().getKeyPattern().globalMax());
- const auto& normalizedMax = gtMax ? maxChunk.getMin() : zoneRange.max;
-
-
- if (SimpleBSONObjComparator::kInstance.evaluate(normalizedMin == normalizedMax)) {
- // This zone does not fully contain any chunk thus we can ignore it
- continue;
- }
+} // namespace
- if (SimpleBSONObjComparator::kInstance.evaluate(normalizedMin != lastMax)) {
- // The zone is not contiguous with the previous one so we add a kNoZoneRange
- // does not fully contain any chunk so we will ignore it
- normalizedRanges.emplace_back(lastMax, normalizedMin, ZoneInfo::kNoZoneName);
- }
+DistributionStatus::DistributionStatus(NamespaceString nss,
+ ShardToChunksMap shardToChunksMap,
+ ZoneInfo zoneInfo)
+ : _nss(std::move(nss)),
+ _shardChunks(std::move(shardToChunksMap)),
+ _zoneInfo(std::move(zoneInfo)) {}
- normalizedRanges.emplace_back(normalizedMin, normalizedMax, zoneRange.zone);
- lastMax = normalizedMax;
- }
+size_t DistributionStatus::totalChunks() const {
+ size_t total = 0;
- const auto& globalMaxKey = cm.getShardKeyPattern().getKeyPattern().globalMax();
- if (SimpleBSONObjComparator::kInstance.evaluate(lastMax != globalMaxKey)) {
- normalizedRanges.emplace_back(lastMax, globalMaxKey, ZoneInfo::kNoZoneName);
+ for (const auto& shardChunk : _shardChunks) {
+ total += shardChunk.second.size();
}
- return normalizedRanges;
-}
-
-} // namespace
-DistributionStatus::DistributionStatus(NamespaceString nss,
- ZoneInfo zoneInfo,
- const ChunkManager& chunkMngr)
- : _nss(std::move(nss)), _zoneInfo(std::move(zoneInfo)), _chunkMngr(chunkMngr) {
-
- _normalizedZones = normalizeZones(_chunkMngr, _zoneInfo);
-
- for (const auto& zoneRange : _normalizedZones) {
- chunkMngr.forEachOverlappingChunk(
- zoneRange.min, zoneRange.max, false /* isMaxInclusive */, [&](const auto& chunkInfo) {
- _shardToZoneSizeMap[chunkInfo.getShardId()][zoneRange.zone]++;
- return true;
- });
- }
+ return total;
}
size_t DistributionStatus::totalChunksWithTag(const std::string& tag) const {
size_t total = 0;
- for (const auto& [_, zoneSizeMap] : _shardToZoneSizeMap) {
- const auto& zoneIt = zoneSizeMap.find(tag);
- if (zoneIt != zoneSizeMap.end()) {
- total += zoneIt->second;
- }
+
+ for (const auto& shardChunk : _shardChunks) {
+ total += numberOfChunksInShardWithTag(shardChunk.first, tag);
}
+
return total;
}
size_t DistributionStatus::numberOfChunksInShard(const ShardId& shardId) const {
- const auto shardZonesIt = _shardToZoneSizeMap.find(shardId);
- if (shardZonesIt == _shardToZoneSizeMap.end()) {
- return 0;
- }
- size_t total = 0;
- for (const auto& [_, numChunks] : shardZonesIt->second) {
- total += numChunks;
- }
- return total;
+ const auto& shardChunks = getChunks(shardId);
+ return shardChunks.size();
}
size_t DistributionStatus::numberOfChunksInShardWithTag(const ShardId& shardId,
const string& tag) const {
- const auto shardZonesIt = _shardToZoneSizeMap.find(shardId);
- if (shardZonesIt == _shardToZoneSizeMap.end()) {
- return 0;
- }
- const auto& shardTags = shardZonesIt->second;
+ const auto& shardChunks = getChunks(shardId);
- const auto& zoneIt = shardTags.find(tag);
- if (zoneIt == shardTags.end()) {
- return 0;
+ size_t total = 0;
+
+ for (const auto& chunk : shardChunks) {
+ if (tag == getTagForChunk(chunk)) {
+ total++;
+ }
}
- return zoneIt->second;
-}
-string DistributionStatus::getTagForRange(const ChunkRange& range) const {
- return _zoneInfo.getZoneForChunk(range);
+ return total;
}
-const StringMap<size_t>& DistributionStatus::getChunksPerTagMap(const ShardId& shardId) const {
- static const StringMap<size_t> emptyMap;
- const auto shardZonesIt = _shardToZoneSizeMap.find(shardId);
- if (shardZonesIt == _shardToZoneSizeMap.end()) {
- return emptyMap;
- }
- return shardZonesIt->second;
+const vector<ChunkType>& DistributionStatus::getChunks(const ShardId& shardId) const {
+ ShardToChunksMap::const_iterator i = _shardChunks.find(shardId);
+ invariant(i != _shardChunks.end());
+
+ return i->second;
}
-const string ZoneInfo::kNoZoneName = "";
+string DistributionStatus::getTagForChunk(const ChunkType& chunk) const {
+ return _zoneInfo.getZoneForChunk(chunk.getRange());
+}
ZoneInfo::ZoneInfo()
: _zoneRanges(SimpleBSONObjComparator::kInstance.makeBSONObjIndexedMap<ZoneRange>()) {}
@@ -237,11 +168,11 @@ string ZoneInfo::getZoneForChunk(const ChunkRange& chunk) const {
// We should never have a partial overlap with a chunk range. If it happens, treat it as if this
// chunk doesn't belong to a tag
if (minIntersect != maxIntersect) {
- return ZoneInfo::kNoZoneName;
+ return "";
}
if (minIntersect == _zoneRanges.end()) {
- return ZoneInfo::kNoZoneName;
+ return "";
}
const ZoneRange& intersectRange = minIntersect->second;
@@ -252,7 +183,7 @@ string ZoneInfo::getZoneForChunk(const ChunkRange& chunk) const {
return intersectRange.zone;
}
- return ZoneInfo::kNoZoneName;
+ return "";
}
void DistributionStatus::report(BSONObjBuilder* builder) const {
@@ -260,15 +191,15 @@ void DistributionStatus::report(BSONObjBuilder* builder) const {
// Report all shards
BSONArrayBuilder shardArr(builder->subarrayStart("shards"));
- for (const auto& [shardId, zoneSizeMap] : _shardToZoneSizeMap) {
+ for (const auto& shardChunk : _shardChunks) {
BSONObjBuilder shardEntry(shardArr.subobjStart());
- shardEntry.append("name", shardId.toString());
+ shardEntry.append("name", shardChunk.first.toString());
- BSONObjBuilder tagsObj(shardEntry.subobjStart("tags"));
- for (const auto& [tagName, numChunks] : zoneSizeMap) {
- tagsObj.appendNumber(tagName, static_cast<long long>(numChunks));
+ BSONArrayBuilder chunkArr(shardEntry.subarrayStart("chunks"));
+ for (const auto& chunk : shardChunk.second) {
+ chunkArr.append(chunk.toConfigBSON());
}
- tagsObj.doneFast();
+ chunkArr.doneFast();
shardEntry.doneFast();
}
@@ -311,7 +242,7 @@ Status BalancerPolicy::isShardSuitableReceiver(const ClusterStatistics::ShardSta
str::stream() << stat.shardId << " is currently draining."};
}
- if (chunkTag != ZoneInfo::kNoZoneName && !stat.shardTags.count(chunkTag)) {
+ if (!chunkTag.empty() && !stat.shardTags.count(chunkTag)) {
return {ErrorCodes::IllegalOperation,
str::stream() << stat.shardId << " is not in the correct zone " << chunkTag};
}
@@ -429,28 +360,10 @@ MigrateInfo chooseRandomMigration(const ShardStatisticsVector& shardStats,
"fromShardId"_attr = sourceShardId,
"toShardId"_attr = destShardId);
- const auto& randomChunk = [&] {
- const auto numChunksOnSourceShard = distribution.numberOfChunksInShard(sourceShardId);
- const auto rndChunkIdx = getRandomIndex(numChunksOnSourceShard);
- ChunkType rndChunk;
-
- int idx{0};
- distribution.getChunkManager().forEachChunk([&](const auto& chunk) {
- if (chunk.getShardId() == sourceShardId && idx++ == rndChunkIdx) {
- rndChunk = makeChunkType(distribution.nss(), chunk);
- rndChunk.setJumbo(chunk.isJumbo());
- return false;
- }
- return true;
- });
-
- invariant(rndChunk.getShard().isValid());
- return rndChunk;
- }();
-
+ const auto& chunks = distribution.getChunks(sourceShardId);
return {destShardId,
- randomChunk,
+ chunks[getRandomIndex(chunks.size())],
MoveChunkRequest::ForceJumbo::kDoNotForce,
MigrateInfo::chunksImbalance};
}
@@ -482,70 +395,57 @@ vector<MigrateInfo> BalancerPolicy::balance(const ShardStatisticsVector& shardSt
if (!availableShards->count(stat.shardId))
continue;
+ const vector<ChunkType>& chunks = distribution.getChunks(stat.shardId);
+
+ if (chunks.empty())
+ continue;
+
// Now we know we need to move to chunks off this shard, but only if permitted by the
// tags policy
unsigned numJumboChunks = 0;
- const auto& chunksPerTagMap = distribution.getChunksPerTagMap(stat.shardId);
- for (const auto& tagIt : chunksPerTagMap) {
- const auto& zoneName = tagIt.first;
- for (const auto& zoneRange : distribution.getNormalizedZones()) {
- if (zoneRange.zone != zoneName) {
- continue;
- }
+ // Since we have to move all chunks, lets just do in order
+ for (const auto& chunk : chunks) {
+ if (chunk.getJumbo()) {
+ numJumboChunks++;
+ continue;
+ }
- distribution.getChunkManager().forEachOverlappingChunk(
- zoneRange.min,
- zoneRange.max,
- false /* isMaxInclusive */,
- [&](const auto& chunk) {
- if (chunk.getShardId() != stat.shardId) {
- return true; // continue
- }
- if (chunk.isJumbo()) {
- numJumboChunks++;
- return true; // continue
- }
-
- const ShardId to = _getLeastLoadedReceiverShard(
- shardStats, distribution, zoneName, *availableShards);
- if (!to.isValid()) {
- if (migrations.empty()) {
- LOGV2_WARNING(
- 21889,
- "Chunk {chunk} is on a draining shard, but no appropriate "
- "recipient found",
- "Chunk is on a draining shard, but no appropriate "
- "recipient found",
- "chunk"_attr = redact(
- makeChunkType(distribution.nss(), chunk).toString()));
- }
- return true; // continue
- }
- invariant(to != stat.shardId);
-
- migrations.emplace_back(to,
- makeChunkType(distribution.nss(), chunk),
- MoveChunkRequest::ForceJumbo::kForceBalancer,
- MigrateInfo::drain);
- invariant(availableShards->erase(stat.shardId));
- invariant(availableShards->erase(to));
- return false; // break
- });
+ const string tag = distribution.getTagForChunk(chunk);
+ const ShardId to =
+ _getLeastLoadedReceiverShard(shardStats, distribution, tag, *availableShards);
+ if (!to.isValid()) {
if (migrations.empty()) {
- LOGV2_WARNING(21890,
- "Unable to find any chunk to move from draining shard "
- "{shardId}. numJumboChunks: {numJumboChunks}",
- "Unable to find any chunk to move from draining shard",
- "shardId"_attr = stat.shardId,
- "numJumboChunks"_attr = numJumboChunks);
- }
-
- if (availableShards->size() < 2) {
- return migrations;
+ LOGV2_WARNING(21889,
+ "Chunk {chunk} is on a draining shard, but no appropriate "
+ "recipient found",
+ "Chunk is on a draining shard, but no appropriate "
+ "recipient found",
+ "chunk"_attr = redact(chunk.toString()));
}
+ continue;
}
+
+ invariant(to != stat.shardId);
+ migrations.emplace_back(
+ to, chunk, MoveChunkRequest::ForceJumbo::kForceBalancer, MigrateInfo::drain);
+ invariant(availableShards->erase(stat.shardId));
+ invariant(availableShards->erase(to));
+ break;
+ }
+
+ if (migrations.empty()) {
+ LOGV2_WARNING(21890,
+ "Unable to find any chunk to move from draining shard "
+ "{shardId}. numJumboChunks: {numJumboChunks}",
+ "Unable to find any chunk to move from draining shard",
+ "shardId"_attr = stat.shardId,
+ "numJumboChunks"_attr = numJumboChunks);
+ }
+
+ if (availableShards->size() < 2) {
+ return migrations;
}
}
}
@@ -553,77 +453,57 @@ vector<MigrateInfo> BalancerPolicy::balance(const ShardStatisticsVector& shardSt
// 2) Check for chunks, which are on the wrong shard and must be moved off of it
if (!distribution.tags().empty()) {
for (const auto& stat : shardStats) {
-
if (!availableShards->count(stat.shardId))
continue;
- const auto& chunksPerTagMap = distribution.getChunksPerTagMap(stat.shardId);
- for (const auto& tagIt : chunksPerTagMap) {
- const auto& zoneName = tagIt.first;
+ const vector<ChunkType>& chunks = distribution.getChunks(stat.shardId);
- if (zoneName == ZoneInfo::kNoZoneName)
+ for (const auto& chunk : chunks) {
+ const string tag = distribution.getTagForChunk(chunk);
+
+ if (tag.empty())
continue;
- if (stat.shardTags.count(zoneName))
+ if (stat.shardTags.count(tag))
continue;
- for (const auto& zoneRange : distribution.getNormalizedZones()) {
- if (zoneRange.zone != zoneName) {
- continue;
- }
- distribution.getChunkManager().forEachOverlappingChunk(
- zoneRange.min,
- zoneRange.max,
- false /* isMaxInclusive */,
- [&](const auto& chunk) {
- if (chunk.getShardId() != stat.shardId) {
- return true; // continue
- }
- if (chunk.isJumbo()) {
- LOGV2_WARNING(
- 21891,
- "Chunk {chunk} violates zone {zone}, but it is jumbo and "
- "cannot be "
- "moved",
- "Chunk violates zone, but it is jumbo and cannot be moved",
- "chunk"_attr =
- redact(makeChunkType(distribution.nss(), chunk).toString()),
- "zone"_attr = redact(zoneName));
- return true; // continue
- }
-
- const ShardId to = _getLeastLoadedReceiverShard(
- shardStats, distribution, zoneName, *availableShards);
- if (!to.isValid()) {
- if (migrations.empty()) {
- LOGV2_WARNING(
- 21892,
- "Chunk {chunk} violates zone {zone}, but no appropriate "
- "recipient found",
- "Chunk violates zone, but no appropriate recipient found",
- "chunk"_attr = redact(
- makeChunkType(distribution.nss(), chunk).toString()),
- "zone"_attr = redact(zoneName));
- }
- return true; // continue
- }
- invariant(to != stat.shardId);
-
- migrations.emplace_back(
- to,
- makeChunkType(distribution.nss(), chunk),
- forceJumbo ? MoveChunkRequest::ForceJumbo::kForceBalancer
- : MoveChunkRequest::ForceJumbo::kDoNotForce,
- MigrateInfo::zoneViolation);
- invariant(availableShards->erase(stat.shardId));
- invariant(availableShards->erase(to));
- return false; // break
- });
+ if (chunk.getJumbo()) {
+ LOGV2_WARNING(
+ 21891,
+ "Chunk {chunk} violates zone {zone}, but it is jumbo and cannot be moved",
+ "Chunk violates zone, but it is jumbo and cannot be moved",
+ "chunk"_attr = redact(chunk.toString()),
+ "zone"_attr = redact(tag));
+ continue;
}
- if (availableShards->size() < 2) {
- return migrations;
+ const ShardId to =
+ _getLeastLoadedReceiverShard(shardStats, distribution, tag, *availableShards);
+ if (!to.isValid()) {
+ if (migrations.empty()) {
+ LOGV2_WARNING(21892,
+ "Chunk {chunk} violates zone {zone}, but no appropriate "
+ "recipient found",
+ "Chunk violates zone, but no appropriate recipient found",
+ "chunk"_attr = redact(chunk.toString()),
+ "zone"_attr = redact(tag));
+ }
+ continue;
}
+
+ invariant(to != stat.shardId);
+ migrations.emplace_back(to,
+ chunk,
+ forceJumbo ? MoveChunkRequest::ForceJumbo::kForceBalancer
+ : MoveChunkRequest::ForceJumbo::kDoNotForce,
+ MigrateInfo::zoneViolation);
+ invariant(availableShards->erase(stat.shardId));
+ invariant(availableShards->erase(to));
+ break;
+ }
+
+ if (availableShards->size() < 2) {
+ return migrations;
}
}
}
@@ -631,35 +511,24 @@ vector<MigrateInfo> BalancerPolicy::balance(const ShardStatisticsVector& shardSt
// 3) for each tag balance
vector<string> tagsPlusEmpty(distribution.tags().begin(), distribution.tags().end());
- tagsPlusEmpty.push_back(ZoneInfo::kNoZoneName);
+ tagsPlusEmpty.push_back("");
for (const auto& tag : tagsPlusEmpty) {
+ const size_t totalNumberOfChunksWithTag =
+ (tag.empty() ? distribution.totalChunks() : distribution.totalChunksWithTag(tag));
- const auto totalNumberOfChunksWithTag = [&] {
- if (tag == ZoneInfo::kNoZoneName) {
- return static_cast<size_t>(distribution.getChunkManager().numChunks());
- }
- return distribution.totalChunksWithTag(tag);
- }();
+ size_t totalNumberOfShardsWithTag = 0;
- const auto totalNumberOfShardsWithTag = [&] {
- if (tag == ZoneInfo::kNoZoneName) {
- return shardStats.size();
- }
-
- size_t numShardsWithTag{0};
- for (const auto& stat : shardStats) {
- if (stat.shardTags.count(tag)) {
- numShardsWithTag++;
- }
+ for (const auto& stat : shardStats) {
+ if (tag.empty() || stat.shardTags.count(tag)) {
+ totalNumberOfShardsWithTag++;
}
- return numShardsWithTag;
- }();
+ }
// Skip zones which have no shards assigned to them. This situation is not harmful, but
// should not be possible so warn the operator to correct it.
if (totalNumberOfShardsWithTag == 0) {
- if (tag != ZoneInfo::kNoZoneName) {
+ if (!tag.empty()) {
LOGV2_WARNING(
21893,
"Zone {zone} in collection {namespace} has no assigned shards and chunks "
@@ -696,7 +565,7 @@ boost::optional<MigrateInfo> BalancerPolicy::balanceSingleChunk(
const ChunkType& chunk,
const ShardStatisticsVector& shardStats,
const DistributionStatus& distribution) {
- const string tag = distribution.getTagForRange(chunk.getRange());
+ const string tag = distribution.getTagForChunk(chunk);
stdx::unordered_set<ShardId> availableShards;
std::transform(shardStats.begin(),
@@ -773,41 +642,26 @@ bool BalancerPolicy::_singleZoneBalance(const ShardStatisticsVector& shardStats,
if (imbalance < kDefaultImbalanceThreshold)
return false;
+ const vector<ChunkType>& chunks = distribution.getChunks(from);
+
unsigned numJumboChunks = 0;
- bool chunkFound = false;
- for (const auto& zoneRange : distribution.getNormalizedZones()) {
- if (zoneRange.zone != tag) {
+ for (const auto& chunk : chunks) {
+ if (distribution.getTagForChunk(chunk) != tag)
continue;
- }
-
- distribution.getChunkManager().forEachOverlappingChunk(
- zoneRange.min, zoneRange.max, false /* isMaxInclusive */, [&](const auto& chunk) {
- if (chunk.getShardId() != from) {
- return true; // continue
- }
- if (chunk.isJumbo()) {
- numJumboChunks++;
- return true; // continue
- }
-
- migrations->emplace_back(to,
- makeChunkType(distribution.nss(), chunk),
- forceJumbo,
- MigrateInfo::chunksImbalance);
- invariant(availableShards->erase(chunk.getShardId()));
- invariant(availableShards->erase(to));
- chunkFound = true;
- return false; // break
- });
-
- if (chunkFound) {
- return chunkFound;
+ if (chunk.getJumbo()) {
+ numJumboChunks++;
+ continue;
}
+
+ migrations->emplace_back(to, chunk, forceJumbo, MigrateInfo::chunksImbalance);
+ invariant(availableShards->erase(chunk.getShard()));
+ invariant(availableShards->erase(to));
+ return true;
}
- if (!chunkFound && numJumboChunks) {
+ if (numJumboChunks) {
LOGV2_WARNING(
21894,
"Shard: {shardId}, collection: {namespace} has only jumbo chunks for "
@@ -819,7 +673,7 @@ bool BalancerPolicy::_singleZoneBalance(const ShardStatisticsVector& shardStats,
"numJumboChunks"_attr = numJumboChunks);
}
- return chunkFound;
+ return false;
}
ZoneRange::ZoneRange(const BSONObj& a_min, const BSONObj& a_max, const std::string& _zone)
diff --git a/src/mongo/db/s/balancer/balancer_policy.h b/src/mongo/db/s/balancer/balancer_policy.h
index 46a4adf6a14..b8403c3881f 100644
--- a/src/mongo/db/s/balancer/balancer_policy.h
+++ b/src/mongo/db/s/balancer/balancer_policy.h
@@ -37,12 +37,12 @@
#include "mongo/db/namespace_string.h"
#include "mongo/db/s/balancer/cluster_statistics.h"
#include "mongo/s/catalog/type_chunk.h"
-#include "mongo/s/chunk_manager.h"
#include "mongo/s/request_types/move_chunk_request.h"
#include "mongo/s/shard_id.h"
namespace mongo {
+
struct ZoneRange {
ZoneRange(const BSONObj& a_min, const BSONObj& a_max, const std::string& _zone);
@@ -78,15 +78,13 @@ struct MigrateInfo {
};
typedef std::vector<ClusterStatistics::ShardStatistics> ShardStatisticsVector;
-typedef std::map<ShardId, StringMap<size_t>> ShardToZoneSizeMap;
+typedef std::map<ShardId, std::vector<ChunkType>> ShardToChunksMap;
/**
* Keeps track of zones for a collection.
*/
class ZoneInfo {
public:
- static const std::string kNoZoneName;
-
ZoneInfo();
ZoneInfo(ZoneInfo&&) = default;
@@ -116,14 +114,6 @@ public:
return _zoneRanges;
}
- const ZoneRange& getZoneRange(const std::string& zoneName) const {
- for (const auto& [_, zoneRange] : _zoneRanges) {
- if (zoneRange.zone == zoneName)
- return zoneRange;
- }
- MONGO_UNREACHABLE;
- }
-
private:
// Map of zone max key to the zone description
BSONObjIndexedMap<ZoneRange> _zoneRanges;
@@ -142,7 +132,7 @@ class DistributionStatus {
DistributionStatus& operator=(const DistributionStatus&) = delete;
public:
- DistributionStatus(NamespaceString nss, ZoneInfo zoneInfo, const ChunkManager& chunkMngr);
+ DistributionStatus(NamespaceString nss, ShardToChunksMap shardToChunksMap, ZoneInfo zoneInfo);
DistributionStatus(DistributionStatus&&) = default;
/**
@@ -153,6 +143,11 @@ public:
}
/**
+ * Returns total number of chunks across all shards.
+ */
+ size_t totalChunks() const;
+
+ /**
* Returns the total number of chunks across all shards, which fall into the specified zone's
* range.
*/
@@ -169,6 +164,18 @@ public:
size_t numberOfChunksInShardWithTag(const ShardId& shardId, const std::string& tag) const;
/**
+ * Returns all chunks for the specified shard.
+ */
+ const std::vector<ChunkType>& getChunks(const ShardId& shardId) const;
+
+ /**
+ * Returns all tag ranges defined for the collection.
+ */
+ const BSONObjIndexedMap<ZoneRange>& tagRanges() const {
+ return _zoneInfo.zoneRanges();
+ }
+
+ /**
* Returns all tags defined for the collection.
*/
const std::set<std::string>& tags() const {
@@ -179,21 +186,7 @@ public:
* Using the set of tags defined for the collection, returns what tag corresponds to the
* specified chunk. If the chunk doesn't fall into any tag returns the empty string.
*/
- std::string getTagForRange(const ChunkRange& range) const;
-
- const ChunkManager& getChunkManager() const {
- return _chunkMngr;
- }
-
- const std::vector<ZoneRange>& getNormalizedZones() const {
- return _normalizedZones;
- }
-
- const ZoneInfo& getZoneInfo() const {
- return _zoneInfo;
- }
-
- const StringMap<size_t>& getChunksPerTagMap(const ShardId& shardId) const;
+ std::string getTagForChunk(const ChunkType& chunk) const;
/**
* Returns a BSON/string representation of this distribution status.
@@ -205,16 +198,11 @@ private:
// Namespace for which this distribution applies
NamespaceString _nss;
- // Map that tracks how many chunks every shard is owning in each zone
- // shardId -> zoneName -> numChunks
- ShardToZoneSizeMap _shardToZoneSizeMap;
+ // Map of what chunks are owned by each shard
+ ShardToChunksMap _shardChunks;
// Info for zones.
ZoneInfo _zoneInfo;
-
- std::vector<ZoneRange> _normalizedZones;
-
- ChunkManager _chunkMngr;
};
class BalancerPolicy {
diff --git a/src/mongo/db/s/balancer/balancer_policy_test.cpp b/src/mongo/db/s/balancer/balancer_policy_test.cpp
index 7441dfab2c7..8f3b340c82d 100644
--- a/src/mongo/db/s/balancer/balancer_policy_test.cpp
+++ b/src/mongo/db/s/balancer/balancer_policy_test.cpp
@@ -46,11 +46,9 @@ using std::stringstream;
using std::vector;
using ShardStatistics = ClusterStatistics::ShardStatistics;
-typedef std::map<ShardId, std::vector<ChunkType>> ShardToChunksMap;
const auto emptyTagSet = std::set<std::string>();
const std::string emptyShardVersion = "";
-const auto kConfigId = ShardId("config");
const auto kShardId0 = ShardId("shard0");
const auto kShardId1 = ShardId("shard1");
const auto kShardId2 = ShardId("shard2");
@@ -59,36 +57,6 @@ const auto kShardId4 = ShardId("shard4");
const auto kShardId5 = ShardId("shard5");
const NamespaceString kNamespace("TestDB", "TestColl");
const uint64_t kNoMaxSize = 0;
-const KeyPattern kSKeyPattern(BSON("x" << 1));
-const boost::optional<Timestamp> kCollTimestamp;
-const OID kCollEpoch;
-
-RoutingTableHistory makeRoutingTable(const std::vector<ChunkType>& chunks) {
- static const UUID kCollectionUUID{UUID::gen()};
-
- return RoutingTableHistory::makeNew(kNamespace,
- kCollectionUUID,
- kSKeyPattern,
- nullptr,
- false,
- kCollEpoch,
- kCollTimestamp,
- boost::none /* timeseriesFields */,
- boost::none /* reshardingFields */,
- true,
- chunks);
-}
-
-ChunkManager makeChunkManager(const std::vector<ChunkType>& chunks) {
- DatabaseVersion dbVersion;
- auto rt = std::make_shared<RoutingTableHistory>(makeRoutingTable(chunks));
-
- return {kConfigId, std::move(dbVersion), {std::move(rt)}, kCollTimestamp};
-}
-
-DistributionStatus makeDistStatus(const ChunkManager& cm, ZoneInfo zoneInfo = ZoneInfo()) {
- return {kNamespace, std::move(zoneInfo), cm};
-}
/**
* Constructs a shard statistics vector and a consistent mapping of chunks to shards given the
@@ -97,7 +65,7 @@ DistributionStatus makeDistStatus(const ChunkManager& cm, ZoneInfo zoneInfo = Zo
*
* [MinKey, 1), [1, 2), [2, 3) ... [N - 1, MaxKey)
*/
-std::pair<std::pair<ShardStatisticsVector, ShardToChunksMap>, ChunkManager> generateCluster(
+std::pair<ShardStatisticsVector, ShardToChunksMap> generateCluster(
const vector<std::pair<ShardStatistics, size_t>>& shardsAndNumChunks) {
int64_t totalNumChunks = 0;
for (const auto& entry : shardsAndNumChunks) {
@@ -109,9 +77,9 @@ std::pair<std::pair<ShardStatisticsVector, ShardToChunksMap>, ChunkManager> gene
int64_t currentChunk = 0;
- ChunkVersion chunkVersion(1, 0, kCollEpoch, kCollTimestamp);
+ ChunkVersion chunkVersion(1, 0, OID::gen(), boost::none /* timestamp */);
- std::vector<ChunkType> chunks;
+ const KeyPattern shardKeyPattern(BSON("x" << 1));
for (auto it = shardsAndNumChunks.begin(); it != shardsAndNumChunks.end(); it++) {
ShardStatistics shard = std::move(it->first);
@@ -124,23 +92,22 @@ std::pair<std::pair<ShardStatisticsVector, ShardToChunksMap>, ChunkManager> gene
ChunkType chunk;
chunk.setNS(kNamespace);
- chunk.setMin(currentChunk == 0 ? kSKeyPattern.globalMin() : BSON("x" << currentChunk));
- chunk.setMax(currentChunk == totalNumChunks - 1 ? kSKeyPattern.globalMax()
+ chunk.setMin(currentChunk == 0 ? shardKeyPattern.globalMin()
+ : BSON("x" << currentChunk));
+ chunk.setMax(currentChunk == totalNumChunks - 1 ? shardKeyPattern.globalMax()
: BSON("x" << currentChunk + 1));
chunk.setShard(shard.shardId);
chunk.setVersion(chunkVersion);
chunkVersion.incMajor();
- chunkMap[shard.shardId].push_back(chunk);
- chunks.push_back(std::move(chunk));
+ chunkMap[shard.shardId].push_back(std::move(chunk));
}
shardStats.push_back(std::move(shard));
}
- return std::make_pair(std::make_pair(std::move(shardStats), std::move(chunkMap)),
- makeChunkManager(chunks));
+ return std::make_pair(std::move(shardStats), std::move(chunkMap));
}
std::vector<MigrateInfo> balanceChunks(const ShardStatisticsVector& shardStats,
@@ -158,12 +125,13 @@ std::vector<MigrateInfo> balanceChunks(const ShardStatisticsVector& shardStats,
}
TEST(BalancerPolicy, Basic) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 4, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId1, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0},
{ShardStatistics(kShardId2, kNoMaxSize, 3, false, emptyTagSet, emptyShardVersion), 3}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT_EQ(1U, migrations.size());
ASSERT_EQ(kShardId0, migrations[0].from);
ASSERT_EQ(kShardId1, migrations[0].to);
@@ -173,12 +141,13 @@ TEST(BalancerPolicy, Basic) {
}
TEST(BalancerPolicy, SmallClusterShouldBePerfectlyBalanced) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 1, false, emptyTagSet, emptyShardVersion), 1},
{ShardStatistics(kShardId1, kNoMaxSize, 2, false, emptyTagSet, emptyShardVersion), 2},
{ShardStatistics(kShardId2, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT_EQ(1U, migrations.size());
ASSERT_EQ(kShardId1, migrations[0].from);
ASSERT_EQ(kShardId2, migrations[0].to);
@@ -188,33 +157,46 @@ TEST(BalancerPolicy, SmallClusterShouldBePerfectlyBalanced) {
}
TEST(BalancerPolicy, SingleChunkShouldNotMove) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 1, false, emptyTagSet, emptyShardVersion), 1},
{ShardStatistics(kShardId1, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0}});
- ASSERT(balanceChunks(cluster.first, makeDistStatus(cm), true, false).empty());
- ASSERT(balanceChunks(cluster.first, makeDistStatus(cm), false, false).empty());
+ ASSERT(
+ balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), true, false)
+ .empty());
+ ASSERT(
+ balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false)
+ .empty());
}
TEST(BalancerPolicy, BalanceThresholdObeyed) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 2, false, emptyTagSet, emptyShardVersion), 2},
{ShardStatistics(kShardId1, kNoMaxSize, 2, false, emptyTagSet, emptyShardVersion), 2},
{ShardStatistics(kShardId2, kNoMaxSize, 1, false, emptyTagSet, emptyShardVersion), 1},
{ShardStatistics(kShardId3, kNoMaxSize, 1, false, emptyTagSet, emptyShardVersion), 1}});
- ASSERT(balanceChunks(cluster.first, makeDistStatus(cm), true, false).empty());
- ASSERT(balanceChunks(cluster.first, makeDistStatus(cm), false, false).empty());
+ ASSERT(
+ balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), true, false)
+ .empty());
+ ASSERT(
+ balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false)
+ .empty());
}
TEST(BalancerPolicy, ParallelBalancing) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 4, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId1, kNoMaxSize, 4, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId2, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0},
{ShardStatistics(kShardId3, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT_EQ(2U, migrations.size());
ASSERT_EQ(kShardId0, migrations[0].from);
@@ -231,7 +213,7 @@ TEST(BalancerPolicy, ParallelBalancing) {
}
TEST(BalancerPolicy, ParallelBalancingDoesNotPutChunksOnShardsAboveTheOptimal) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 100, false, emptyTagSet, emptyShardVersion), 100},
{ShardStatistics(kShardId1, kNoMaxSize, 90, false, emptyTagSet, emptyShardVersion), 90},
{ShardStatistics(kShardId2, kNoMaxSize, 90, false, emptyTagSet, emptyShardVersion), 90},
@@ -239,7 +221,8 @@ TEST(BalancerPolicy, ParallelBalancingDoesNotPutChunksOnShardsAboveTheOptimal) {
{ShardStatistics(kShardId4, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0},
{ShardStatistics(kShardId5, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT_EQ(2U, migrations.size());
ASSERT_EQ(kShardId0, migrations[0].from);
@@ -256,13 +239,14 @@ TEST(BalancerPolicy, ParallelBalancingDoesNotPutChunksOnShardsAboveTheOptimal) {
}
TEST(BalancerPolicy, ParallelBalancingDoesNotMoveChunksFromShardsBelowOptimal) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 100, false, emptyTagSet, emptyShardVersion), 100},
{ShardStatistics(kShardId1, kNoMaxSize, 30, false, emptyTagSet, emptyShardVersion), 30},
{ShardStatistics(kShardId2, kNoMaxSize, 5, false, emptyTagSet, emptyShardVersion), 5},
{ShardStatistics(kShardId3, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT_EQ(1U, migrations.size());
ASSERT_EQ(kShardId0, migrations[0].from);
@@ -273,7 +257,7 @@ TEST(BalancerPolicy, ParallelBalancingDoesNotMoveChunksFromShardsBelowOptimal) {
}
TEST(BalancerPolicy, ParallelBalancingNotSchedulingOnInUseSourceShardsWithMoveNecessary) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 8, false, emptyTagSet, emptyShardVersion), 8},
{ShardStatistics(kShardId1, kNoMaxSize, 4, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId2, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0},
@@ -282,7 +266,10 @@ TEST(BalancerPolicy, ParallelBalancingNotSchedulingOnInUseSourceShardsWithMoveNe
// Here kShardId0 would have been selected as a donor
stdx::unordered_set<ShardId> availableShards{kShardId1, kShardId2, kShardId3};
const auto migrations(
- BalancerPolicy::balance(cluster.first, makeDistStatus(cm), &availableShards, false));
+ BalancerPolicy::balance(cluster.first,
+ DistributionStatus(kNamespace, cluster.second, ZoneInfo()),
+ &availableShards,
+ false));
ASSERT_EQ(1U, migrations.size());
ASSERT_EQ(kShardId1, migrations[0].from);
@@ -293,7 +280,7 @@ TEST(BalancerPolicy, ParallelBalancingNotSchedulingOnInUseSourceShardsWithMoveNe
}
TEST(BalancerPolicy, ParallelBalancingNotSchedulingOnInUseSourceShardsWithMoveNotNecessary) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 12, false, emptyTagSet, emptyShardVersion), 12},
{ShardStatistics(kShardId1, kNoMaxSize, 4, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId2, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0},
@@ -302,12 +289,15 @@ TEST(BalancerPolicy, ParallelBalancingNotSchedulingOnInUseSourceShardsWithMoveNo
// Here kShardId0 would have been selected as a donor
stdx::unordered_set<ShardId> availableShards{kShardId1, kShardId2, kShardId3};
const auto migrations(
- BalancerPolicy::balance(cluster.first, makeDistStatus(cm), &availableShards, false));
+ BalancerPolicy::balance(cluster.first,
+ DistributionStatus(kNamespace, cluster.second, ZoneInfo()),
+ &availableShards,
+ false));
ASSERT_EQ(0U, migrations.size());
}
TEST(BalancerPolicy, ParallelBalancingNotSchedulingOnInUseDestinationShards) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 4, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId1, kNoMaxSize, 4, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId2, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0},
@@ -316,7 +306,10 @@ TEST(BalancerPolicy, ParallelBalancingNotSchedulingOnInUseDestinationShards) {
// Here kShardId2 would have been selected as a recipient
stdx::unordered_set<ShardId> availableShards{kShardId0, kShardId1, kShardId3};
const auto migrations(
- BalancerPolicy::balance(cluster.first, makeDistStatus(cm), &availableShards, false));
+ BalancerPolicy::balance(cluster.first,
+ DistributionStatus(kNamespace, cluster.second, ZoneInfo()),
+ &availableShards,
+ false));
ASSERT_EQ(1U, migrations.size());
ASSERT_EQ(kShardId0, migrations[0].from);
@@ -326,115 +319,68 @@ TEST(BalancerPolicy, ParallelBalancingNotSchedulingOnInUseDestinationShards) {
ASSERT_EQ(MigrateInfo::chunksImbalance, migrations[0].reason);
}
-TEST(BalancerPolicy, JumboChunksNotMovedWhileEnforcingZones) {
- auto [cluster, cm] = generateCluster(
- {{ShardStatistics(kShardId0, kNoMaxSize, 3, false, emptyTagSet, emptyShardVersion), 3},
- {ShardStatistics(kShardId1, kNoMaxSize, 3, false, {"a"}, emptyShardVersion), 3}});
-
- // construct a new chunk map where all the chunks are jumbo except this one
- const auto& jumboChunk = cluster.second[kShardId0][1];
-
- std::vector<ChunkType> chunks;
- cm.forEachChunk([&](const auto& chunk) {
- ChunkType ct{kNamespace, chunk.getRange(), chunk.getLastmod(), chunk.getShardId()};
- if (chunk.getLastmod() == jumboChunk.getVersion())
- ct.setJumbo(false);
- else
- ct.setJumbo(true);
- chunks.emplace_back(std::move(ct));
- return true;
- });
-
- ZoneInfo zoneInfo;
- ASSERT_OK(zoneInfo.addRangeToZone(
- ZoneRange(kSKeyPattern.globalMin(), kSKeyPattern.globalMax(), "a")));
- const auto distribution = makeDistStatus(makeChunkManager(chunks), std::move(zoneInfo));
-
- const auto migrations(balanceChunks(cluster.first, distribution, false, false));
- ASSERT_EQ(1U, migrations.size());
- ASSERT_EQ(kShardId0, migrations[0].from);
- ASSERT_EQ(kShardId1, migrations[0].to);
- ASSERT_BSONOBJ_EQ(jumboChunk.getMin(), migrations[0].minKey);
- ASSERT_BSONOBJ_EQ(jumboChunk.getMax(), migrations[0].maxKey);
- ASSERT_EQ(MigrateInfo::zoneViolation, migrations[0].reason);
-}
-
TEST(BalancerPolicy, JumboChunksNotMoved) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 2, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId1, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0}});
- // construct a new chunk map where all the chunks are jumbo except this one
- const auto& jumboChunk = cluster.second[kShardId0][1];
-
- std::vector<ChunkType> chunks;
- cm.forEachChunk([&](const auto& chunk) {
- ChunkType ct{kNamespace, chunk.getRange(), chunk.getLastmod(), chunk.getShardId()};
- if (chunk.getLastmod() == jumboChunk.getVersion())
- ct.setJumbo(false);
- else
- ct.setJumbo(true);
- chunks.emplace_back(std::move(ct));
- return true;
- });
+ cluster.second[kShardId0][0].setJumbo(true);
+ cluster.second[kShardId0][1].setJumbo(false); // Only chunk 1 is not jumbo
+ cluster.second[kShardId0][2].setJumbo(true);
+ cluster.second[kShardId0][3].setJumbo(true);
- const auto migrations(
- balanceChunks(cluster.first, makeDistStatus(makeChunkManager(chunks)), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT_EQ(1U, migrations.size());
ASSERT_EQ(kShardId0, migrations[0].from);
ASSERT_EQ(kShardId1, migrations[0].to);
- ASSERT_BSONOBJ_EQ(jumboChunk.getMin(), migrations[0].minKey);
- ASSERT_BSONOBJ_EQ(jumboChunk.getMax(), migrations[0].maxKey);
+ ASSERT_BSONOBJ_EQ(cluster.second[kShardId0][1].getMin(), migrations[0].minKey);
+ ASSERT_BSONOBJ_EQ(cluster.second[kShardId0][1].getMax(), migrations[0].maxKey);
ASSERT_EQ(MigrateInfo::chunksImbalance, migrations[0].reason);
}
TEST(BalancerPolicy, JumboChunksNotMovedParallel) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 2, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId1, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0},
{ShardStatistics(kShardId2, kNoMaxSize, 2, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId3, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 0}});
- // construct a new chunk map where all the chunks are jumbo except the ones listed below
- const auto& jumboChunk0 = cluster.second[kShardId0][1];
- const auto& jumboChunk1 = cluster.second[kShardId2][2];
-
- std::vector<ChunkType> chunks;
- cm.forEachChunk([&](const auto& chunk) {
- ChunkType ct{kNamespace, chunk.getRange(), chunk.getLastmod(), chunk.getShardId()};
- if (chunk.getLastmod() == jumboChunk0.getVersion() ||
- chunk.getLastmod() == jumboChunk1.getVersion())
- ct.setJumbo(false);
- else
- ct.setJumbo(true);
- chunks.emplace_back(std::move(ct));
- return true;
- });
+ cluster.second[kShardId0][0].setJumbo(true);
+ cluster.second[kShardId0][1].setJumbo(false); // Only chunk 1 is not jumbo
+ cluster.second[kShardId0][2].setJumbo(true);
+ cluster.second[kShardId0][3].setJumbo(true);
- const auto migrations(
- balanceChunks(cluster.first, makeDistStatus(makeChunkManager(chunks)), false, false));
+ cluster.second[kShardId2][0].setJumbo(true);
+ cluster.second[kShardId2][1].setJumbo(true);
+ cluster.second[kShardId2][2].setJumbo(false); // Only chunk 1 is not jumbo
+ cluster.second[kShardId2][3].setJumbo(true);
+
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT_EQ(2U, migrations.size());
ASSERT_EQ(kShardId0, migrations[0].from);
ASSERT_EQ(kShardId1, migrations[0].to);
- ASSERT_BSONOBJ_EQ(jumboChunk0.getMin(), migrations[0].minKey);
- ASSERT_BSONOBJ_EQ(jumboChunk0.getMax(), migrations[0].maxKey);
+ ASSERT_BSONOBJ_EQ(cluster.second[kShardId0][1].getMin(), migrations[0].minKey);
+ ASSERT_BSONOBJ_EQ(cluster.second[kShardId0][1].getMax(), migrations[0].maxKey);
ASSERT_EQ(MigrateInfo::chunksImbalance, migrations[0].reason);
ASSERT_EQ(kShardId2, migrations[1].from);
ASSERT_EQ(kShardId3, migrations[1].to);
- ASSERT_BSONOBJ_EQ(jumboChunk1.getMin(), migrations[1].minKey);
- ASSERT_BSONOBJ_EQ(jumboChunk1.getMax(), migrations[1].maxKey);
+ ASSERT_BSONOBJ_EQ(cluster.second[kShardId2][2].getMin(), migrations[1].minKey);
+ ASSERT_BSONOBJ_EQ(cluster.second[kShardId2][2].getMax(), migrations[1].maxKey);
ASSERT_EQ(MigrateInfo::chunksImbalance, migrations[1].reason);
}
TEST(BalancerPolicy, DrainingSingleChunk) {
// shard0 is draining and chunks will go to shard1, even though it has a lot more chunks
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 2, true, emptyTagSet, emptyShardVersion), 1},
{ShardStatistics(kShardId1, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 5}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT_EQ(1U, migrations.size());
ASSERT_EQ(kShardId0, migrations[0].from);
ASSERT_EQ(kShardId1, migrations[0].to);
@@ -445,13 +391,14 @@ TEST(BalancerPolicy, DrainingSingleChunk) {
TEST(BalancerPolicy, DrainingSingleChunkPerShard) {
// shard0 and shard2 are draining and chunks will go to shard1 and shard3 in parallel
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 2, true, emptyTagSet, emptyShardVersion), 1},
{ShardStatistics(kShardId1, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 5},
{ShardStatistics(kShardId2, kNoMaxSize, 2, true, emptyTagSet, emptyShardVersion), 1},
{ShardStatistics(kShardId3, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 5}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT_EQ(2U, migrations.size());
ASSERT_EQ(kShardId0, migrations[0].from);
@@ -469,11 +416,12 @@ TEST(BalancerPolicy, DrainingSingleChunkPerShard) {
TEST(BalancerPolicy, DrainingWithTwoChunksFirstOneSelected) {
// shard0 is draining and chunks will go to shard1, even though it has a lot more chunks
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 2, true, emptyTagSet, emptyShardVersion), 2},
{ShardStatistics(kShardId1, kNoMaxSize, 0, false, emptyTagSet, emptyShardVersion), 5}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT_EQ(1U, migrations.size());
ASSERT_EQ(kShardId0, migrations[0].from);
ASSERT_EQ(kShardId1, migrations[0].to);
@@ -485,12 +433,13 @@ TEST(BalancerPolicy, DrainingWithTwoChunksFirstOneSelected) {
TEST(BalancerPolicy, DrainingMultipleShardsFirstOneSelected) {
// shard0 and shard1 are both draining with very little chunks in them and chunks will go to
// shard2, even though it has a lot more chunks that the other two
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 5, true, emptyTagSet, emptyShardVersion), 1},
{ShardStatistics(kShardId1, kNoMaxSize, 5, true, emptyTagSet, emptyShardVersion), 2},
{ShardStatistics(kShardId2, kNoMaxSize, 5, false, emptyTagSet, emptyShardVersion), 16}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT_EQ(1U, migrations.size());
ASSERT_EQ(kShardId0, migrations[0].from);
ASSERT_EQ(kShardId2, migrations[0].to);
@@ -501,17 +450,18 @@ TEST(BalancerPolicy, DrainingMultipleShardsFirstOneSelected) {
TEST(BalancerPolicy, DrainingMultipleShardsWontAcceptChunks) {
// shard0 has many chunks, but can't move them to shard1 or shard2 because they are draining
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 2, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId1, kNoMaxSize, 0, true, emptyTagSet, emptyShardVersion), 0},
{ShardStatistics(kShardId2, kNoMaxSize, 0, true, emptyTagSet, emptyShardVersion), 0}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT(migrations.empty());
}
TEST(BalancerPolicy, DrainingSingleAppropriateShardFoundDueToTag) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 2, false, {"NYC"}, emptyShardVersion), 4},
{ShardStatistics(kShardId1, kNoMaxSize, 2, false, {"LAX"}, emptyShardVersion), 4},
{ShardStatistics(kShardId2, kNoMaxSize, 1, true, {"LAX"}, emptyShardVersion), 1}});
@@ -519,7 +469,7 @@ TEST(BalancerPolicy, DrainingSingleAppropriateShardFoundDueToTag) {
ZoneInfo zoneInfo;
ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(
cluster.second[kShardId2][0].getMin(), cluster.second[kShardId2][0].getMax(), "LAX")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
+ DistributionStatus distribution(kNamespace, cluster.second, std::move(zoneInfo));
const auto migrations(balanceChunks(cluster.first, distribution, false, false));
ASSERT_EQ(1U, migrations.size());
@@ -531,7 +481,7 @@ TEST(BalancerPolicy, DrainingSingleAppropriateShardFoundDueToTag) {
}
TEST(BalancerPolicy, DrainingNoAppropriateShardsFoundDueToTag) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 2, false, {"NYC"}, emptyShardVersion), 4},
{ShardStatistics(kShardId1, kNoMaxSize, 2, false, {"LAX"}, emptyShardVersion), 4},
{ShardStatistics(kShardId2, kNoMaxSize, 1, true, {"SEA"}, emptyShardVersion), 1}});
@@ -539,7 +489,7 @@ TEST(BalancerPolicy, DrainingNoAppropriateShardsFoundDueToTag) {
ZoneInfo zoneInfo;
ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(
cluster.second[kShardId2][0].getMin(), cluster.second[kShardId2][0].getMax(), "SEA")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
+ DistributionStatus distribution(kNamespace, cluster.second, std::move(zoneInfo));
const auto migrations(balanceChunks(cluster.first, distribution, false, false));
ASSERT(migrations.empty());
@@ -547,12 +497,13 @@ TEST(BalancerPolicy, DrainingNoAppropriateShardsFoundDueToTag) {
TEST(BalancerPolicy, NoBalancingDueToAllNodesEitherDrainingOrMaxedOut) {
// shard0 and shard2 are draining, shard1 is maxed out
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 2, true, emptyTagSet, emptyShardVersion), 1},
{ShardStatistics(kShardId1, 1, 1, false, emptyTagSet, emptyShardVersion), 6},
{ShardStatistics(kShardId2, kNoMaxSize, 1, true, emptyTagSet, emptyShardVersion), 1}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT(migrations.empty());
}
@@ -560,12 +511,13 @@ TEST(BalancerPolicy, BalancerRespectsMaxShardSizeOnlyBalanceToNonMaxed) {
// Note that maxSize of shard0 is 1, and it is therefore overloaded with currSize = 3. Other
// shards have maxSize = 0 = unset. Even though the overloaded shard has the least number of
// less chunks, we shouldn't move chunks to that shard.
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, 1, 3, false, emptyTagSet, emptyShardVersion), 2},
{ShardStatistics(kShardId1, kNoMaxSize, 5, false, emptyTagSet, emptyShardVersion), 5},
{ShardStatistics(kShardId2, kNoMaxSize, 10, false, emptyTagSet, emptyShardVersion), 10}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT_EQ(1U, migrations.size());
ASSERT_EQ(kShardId2, migrations[0].from);
ASSERT_EQ(kShardId1, migrations[0].to);
@@ -577,26 +529,27 @@ TEST(BalancerPolicy, BalancerRespectsMaxShardSizeWhenAllBalanced) {
// Note that maxSize of shard0 is 1, and it is therefore overloaded with currSize = 4. Other
// shards have maxSize = 0 = unset. We check that being over the maxSize is NOT equivalent to
// draining, we don't want to empty shards for no other reason than they are over this limit.
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, 1, 4, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId1, kNoMaxSize, 4, false, emptyTagSet, emptyShardVersion), 4},
{ShardStatistics(kShardId2, kNoMaxSize, 4, false, emptyTagSet, emptyShardVersion), 4}});
- const auto migrations(balanceChunks(cluster.first, makeDistStatus(cm), false, false));
+ const auto migrations(balanceChunks(
+ cluster.first, DistributionStatus(kNamespace, cluster.second, ZoneInfo()), false, false));
ASSERT(migrations.empty());
}
TEST(BalancerPolicy, BalancerRespectsTagsWhenDraining) {
// shard1 drains the proper chunk to shard0, even though it is more loaded than shard2
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 5, false, {"a"}, emptyShardVersion), 6},
- {ShardStatistics(kShardId1, kNoMaxSize, 5, true, {"a", "b"}, emptyShardVersion), 1},
+ {ShardStatistics(kShardId1, kNoMaxSize, 5, true, {"a", "b"}, emptyShardVersion), 2},
{ShardStatistics(kShardId2, kNoMaxSize, 5, false, {"b"}, emptyShardVersion), 2}});
ZoneInfo zoneInfo;
- ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kSKeyPattern.globalMin(), BSON("x" << 7), "a")));
- ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(BSON("x" << 8), kSKeyPattern.globalMax(), "b")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
+ ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kMinBSONKey, BSON("x" << 7), "a")));
+ ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(BSON("x" << 8), kMaxBSONKey, "b")));
+ DistributionStatus distribution(kNamespace, cluster.second, std::move(zoneInfo));
const auto migrations(balanceChunks(cluster.first, distribution, false, false));
ASSERT_EQ(1U, migrations.size());
@@ -610,14 +563,14 @@ TEST(BalancerPolicy, BalancerRespectsTagsWhenDraining) {
TEST(BalancerPolicy, BalancerRespectsTagPolicyBeforeImbalance) {
// There is a large imbalance between shard0 and shard1, but the balancer must first fix the
// chunks, which are on a wrong shard due to tag policy
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 5, false, {"a"}, emptyShardVersion), 2},
{ShardStatistics(kShardId1, kNoMaxSize, 5, false, {"a"}, emptyShardVersion), 6},
{ShardStatistics(kShardId2, kNoMaxSize, 5, false, emptyTagSet, emptyShardVersion), 2}});
ZoneInfo zoneInfo;
- ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kSKeyPattern.globalMin(), BSON("x" << 100), "a")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
+ ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kMinBSONKey, BSON("x" << 100), "a")));
+ DistributionStatus distribution(kNamespace, cluster.second, std::move(zoneInfo));
const auto migrations(balanceChunks(cluster.first, distribution, false, false));
ASSERT_EQ(1U, migrations.size());
@@ -631,15 +584,15 @@ TEST(BalancerPolicy, BalancerRespectsTagPolicyBeforeImbalance) {
TEST(BalancerPolicy, BalancerFixesIncorrectTagsWithCrossShardViolationOfTags) {
// The zone policy dictates that the same shard must donate and also receive chunks. The test
// validates that the same shard is not used as a donor and recipient as part of the same round.
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 5, false, {"a"}, emptyShardVersion), 3},
{ShardStatistics(kShardId1, kNoMaxSize, 5, false, {"a"}, emptyShardVersion), 3},
{ShardStatistics(kShardId2, kNoMaxSize, 5, false, {"b"}, emptyShardVersion), 3}});
ZoneInfo zoneInfo;
- ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kSKeyPattern.globalMin(), BSON("x" << 1), "b")));
- ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(BSON("x" << 8), kSKeyPattern.globalMax(), "a")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
+ ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kMinBSONKey, BSON("x" << 1), "b")));
+ ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(BSON("x" << 8), kMaxBSONKey, "a")));
+ DistributionStatus distribution(kNamespace, cluster.second, std::move(zoneInfo));
const auto migrations(balanceChunks(cluster.first, distribution, false, false));
ASSERT_EQ(1U, migrations.size());
@@ -652,14 +605,14 @@ TEST(BalancerPolicy, BalancerFixesIncorrectTagsWithCrossShardViolationOfTags) {
TEST(BalancerPolicy, BalancerFixesIncorrectTagsInOtherwiseBalancedCluster) {
// Chunks are balanced across shards, but there are wrong tags, which need to be fixed
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 5, false, {"a"}, emptyShardVersion), 3},
{ShardStatistics(kShardId1, kNoMaxSize, 5, false, {"a"}, emptyShardVersion), 3},
{ShardStatistics(kShardId2, kNoMaxSize, 5, false, emptyTagSet, emptyShardVersion), 3}});
ZoneInfo zoneInfo;
- ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kSKeyPattern.globalMin(), BSON("x" << 10), "a")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
+ ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kMinBSONKey, BSON("x" << 10), "a")));
+ DistributionStatus distribution(kNamespace, cluster.second, std::move(zoneInfo));
const auto migrations(balanceChunks(cluster.first, distribution, false, false));
ASSERT_EQ(1U, migrations.size());
@@ -672,30 +625,29 @@ TEST(BalancerPolicy, BalancerFixesIncorrectTagsInOtherwiseBalancedCluster) {
TEST(BalancerPolicy, BalancerTagAlreadyBalanced) {
// Chunks are balanced across shards for the tag.
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 3, false, {"a"}, emptyShardVersion), 2},
{ShardStatistics(kShardId1, kNoMaxSize, 2, false, {"a"}, emptyShardVersion), 2}});
ZoneInfo zoneInfo;
- ASSERT_OK(zoneInfo.addRangeToZone(
- ZoneRange(kSKeyPattern.globalMin(), kSKeyPattern.globalMax(), "a")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
+ ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kMinBSONKey, kMaxBSONKey, "a")));
+ DistributionStatus distribution(kNamespace, cluster.second, std::move(zoneInfo));
ASSERT(balanceChunks(cluster.first, distribution, false, false).empty());
}
TEST(BalancerPolicy, BalancerMostOverLoadShardHasMultipleTags) {
// shard0 has chunks [MinKey, 1), [1, 2), [2, 3), [3, 4), [4, 5), so two chunks each
// for tag "b" and "c". So [1, 2) is expected to be moved to shard1 in round 1.
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 5, false, {"a", "b", "c"}, emptyShardVersion), 5},
{ShardStatistics(kShardId1, kNoMaxSize, 1, false, {"b"}, emptyShardVersion), 1},
{ShardStatistics(kShardId2, kNoMaxSize, 1, false, {"c"}, emptyShardVersion), 1}});
ZoneInfo zoneInfo;
- ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kSKeyPattern.globalMin(), BSON("x" << 1), "a")));
+ ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kMinBSONKey, BSON("x" << 1), "a")));
ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(BSON("x" << 1), BSON("x" << 3), "b")));
ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(BSON("x" << 3), BSON("x" << 5), "c")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
+ DistributionStatus distribution(kNamespace, cluster.second, std::move(zoneInfo));
const auto migrations(balanceChunks(cluster.first, distribution, false, false));
ASSERT_EQ(1U, migrations.size());
@@ -710,16 +662,16 @@ TEST(BalancerPolicy, BalancerMostOverLoadShardHasMultipleTagsSkipTagWithShardInU
// shard0 has chunks [MinKey, 1), [1, 2), [2, 3), [3, 4), [4, 5), so two chunks each
// for tag "b" and "c". So [3, 4) is expected to be moved to shard2 because shard1 is
// in use.
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 5, false, {"a", "b", "c"}, emptyShardVersion), 5},
{ShardStatistics(kShardId1, kNoMaxSize, 1, false, {"b"}, emptyShardVersion), 1},
{ShardStatistics(kShardId2, kNoMaxSize, 1, false, {"c"}, emptyShardVersion), 1}});
ZoneInfo zoneInfo;
- ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kSKeyPattern.globalMin(), BSON("x" << 1), "a")));
+ ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kMinBSONKey, BSON("x" << 1), "a")));
ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(BSON("x" << 1), BSON("x" << 3), "b")));
ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(BSON("x" << 3), BSON("x" << 5), "c")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
+ DistributionStatus distribution(kNamespace, cluster.second, std::move(zoneInfo));
stdx::unordered_set<ShardId> availableShards{kShardId0, kShardId2, kShardId3};
const auto migrations(
@@ -734,15 +686,15 @@ TEST(BalancerPolicy, BalancerMostOverLoadShardHasMultipleTagsSkipTagWithShardInU
TEST(BalancerPolicy, BalancerFixesIncorrectTagsInOtherwiseBalancedClusterParallel) {
// Chunks are balanced across shards, but there are wrong tags, which need to be fixed
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 5, false, {"a"}, emptyShardVersion), 3},
{ShardStatistics(kShardId1, kNoMaxSize, 5, false, {"a"}, emptyShardVersion), 3},
{ShardStatistics(kShardId2, kNoMaxSize, 5, false, emptyTagSet, emptyShardVersion), 3},
{ShardStatistics(kShardId3, kNoMaxSize, 5, false, emptyTagSet, emptyShardVersion), 3}});
ZoneInfo zoneInfo;
- ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kSKeyPattern.globalMin(), BSON("x" << 20), "a")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
+ ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kMinBSONKey, BSON("x" << 20), "a")));
+ DistributionStatus distribution(kNamespace, cluster.second, std::move(zoneInfo));
const auto migrations(balanceChunks(cluster.first, distribution, false, false));
ASSERT_EQ(2U, migrations.size());
@@ -760,55 +712,14 @@ TEST(BalancerPolicy, BalancerFixesIncorrectTagsInOtherwiseBalancedClusterParalle
ASSERT_EQ(MigrateInfo::zoneViolation, migrations[0].reason);
}
-TEST(BalancerPolicy, ChunksInNoZoneSpanOnAllShardsWithEmptyZones) {
- // Balanacer is able to move chunks in the noZone to shards with tags
- auto [cluster, cm] = generateCluster(
- {{ShardStatistics(kShardId0, kNoMaxSize, 5, false, emptyTagSet, emptyShardVersion), 3},
- {ShardStatistics(kShardId1, kNoMaxSize, 5, false, {"a"}, emptyShardVersion), 0}});
-
- ZoneInfo zoneInfo;
- ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(BSON("x" << 100), kSKeyPattern.globalMax(), "a")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
-
- const auto migrations(balanceChunks(cluster.first, distribution, false, false));
- ASSERT_EQ(1U, migrations.size());
-
- ASSERT_EQ(kShardId0, migrations[0].from);
- ASSERT_EQ(kShardId1, migrations[0].to);
- ASSERT_BSONOBJ_EQ(cluster.second[kShardId0][0].getMin(), migrations[0].minKey);
- ASSERT_BSONOBJ_EQ(cluster.second[kShardId0][0].getMax(), migrations[0].maxKey);
- ASSERT_EQ(MigrateInfo::chunksImbalance, migrations[0].reason);
-}
-
-TEST(BalancerPolicy, BalancingNoZoneIgnoreTotalShardSize) {
- // Shard1 is overloaded and contains:
- // [min, 1) [1, 2) [2, 3] -> zone("a")
- // [3, 4) [4, 5) [5, 6) -> NoZone
- //
- // But it won't donate any chunk since the
- auto [cluster, cm] = generateCluster(
- {{ShardStatistics(kShardId0, kNoMaxSize, 5, false, {"a"}, emptyShardVersion), 3},
- {ShardStatistics(kShardId1, kNoMaxSize, 5, false, {"a"}, emptyShardVersion), 6},
- {ShardStatistics(kShardId2, kNoMaxSize, 5, false, emptyTagSet, emptyShardVersion), 3}});
-
- ZoneInfo zoneInfo;
- ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kSKeyPattern.globalMin(), BSON("x" << 6), "a")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
-
- const auto migrations(balanceChunks(cluster.first, distribution, false, false));
- ASSERT_EQ(0U, migrations.size());
-}
-
-
TEST(BalancerPolicy, BalancerHandlesNoShardsWithTag) {
- auto [cluster, cm] = generateCluster(
+ auto cluster = generateCluster(
{{ShardStatistics(kShardId0, kNoMaxSize, 5, false, emptyTagSet, emptyShardVersion), 2},
{ShardStatistics(kShardId1, kNoMaxSize, 5, false, emptyTagSet, emptyShardVersion), 2}});
ZoneInfo zoneInfo;
- ASSERT_OK(zoneInfo.addRangeToZone(
- ZoneRange(kSKeyPattern.globalMin(), BSON("x" << 7), "NonExistentZone")));
- const auto distribution = makeDistStatus(cm, std::move(zoneInfo));
+ ASSERT_OK(zoneInfo.addRangeToZone(ZoneRange(kMinBSONKey, BSON("x" << 7), "NonExistentZone")));
+ DistributionStatus distribution(kNamespace, cluster.second, std::move(zoneInfo));
ASSERT(balanceChunks(cluster.first, distribution, false, false).empty());
}
@@ -821,7 +732,7 @@ TEST(DistributionStatus, AddTagRangeOverlap) {
ASSERT_OK(zInfo.addRangeToZone(ZoneRange(BSON("x" << 20), BSON("x" << 30), "b")));
ASSERT_EQ(ErrorCodes::RangeOverlapConflict,
- zInfo.addRangeToZone(ZoneRange(kSKeyPattern.globalMin(), BSON("x" << 2), "d")));
+ zInfo.addRangeToZone(ZoneRange(kMinBSONKey, BSON("x" << 2), "d")));
ASSERT_EQ(ErrorCodes::RangeOverlapConflict,
zInfo.addRangeToZone(ZoneRange(BSON("x" << -1), BSON("x" << 5), "d")));
ASSERT_EQ(ErrorCodes::RangeOverlapConflict,
@@ -833,7 +744,7 @@ TEST(DistributionStatus, AddTagRangeOverlap) {
ASSERT_EQ(ErrorCodes::RangeOverlapConflict,
zInfo.addRangeToZone(ZoneRange(BSON("x" << -1), BSON("x" << 32), "d")));
ASSERT_EQ(ErrorCodes::RangeOverlapConflict,
- zInfo.addRangeToZone(ZoneRange(BSON("x" << 25), kSKeyPattern.globalMax(), "d")));
+ zInfo.addRangeToZone(ZoneRange(BSON("x" << 25), kMaxBSONKey, "d")));
}
TEST(DistributionStatus, ChunkTagsSelectorWithRegularKeys) {
@@ -841,37 +752,128 @@ TEST(DistributionStatus, ChunkTagsSelectorWithRegularKeys) {
ASSERT_OK(zInfo.addRangeToZone(ZoneRange(BSON("x" << 1), BSON("x" << 10), "a")));
ASSERT_OK(zInfo.addRangeToZone(ZoneRange(BSON("x" << 10), BSON("x" << 20), "b")));
ASSERT_OK(zInfo.addRangeToZone(ZoneRange(BSON("x" << 20), BSON("x" << 30), "c")));
+ DistributionStatus d(kNamespace, ShardToChunksMap{}, std::move(zInfo));
+
+ {
+ ChunkType chunk;
+ chunk.setMin(kMinBSONKey);
+ chunk.setMax(BSON("x" << 1));
+ ASSERT_EQUALS("", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << 0));
+ chunk.setMax(BSON("x" << 1));
+ ASSERT_EQUALS("", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << 1));
+ chunk.setMax(BSON("x" << 5));
+ ASSERT_EQUALS("a", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << 10));
+ chunk.setMax(BSON("x" << 20));
+ ASSERT_EQUALS("b", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << 15));
+ chunk.setMax(BSON("x" << 20));
+ ASSERT_EQUALS("b", d.getTagForChunk(chunk));
+ }
- ASSERT_EQUALS(ZoneInfo::kNoZoneName,
- zInfo.getZoneForChunk({kSKeyPattern.globalMin(), BSON("x" << 1)}));
- ASSERT_EQUALS(ZoneInfo::kNoZoneName, zInfo.getZoneForChunk({BSON("x" << 0), BSON("x" << 1)}));
- ASSERT_EQUALS("a", zInfo.getZoneForChunk({BSON("x" << 1), BSON("x" << 5)}));
- ASSERT_EQUALS("b", zInfo.getZoneForChunk({BSON("x" << 10), BSON("x" << 20)}));
- ASSERT_EQUALS("b", zInfo.getZoneForChunk({BSON("x" << 15), BSON("x" << 20)}));
- ASSERT_EQUALS("c", zInfo.getZoneForChunk({BSON("x" << 25), BSON("x" << 30)}));
- ASSERT_EQUALS(ZoneInfo::kNoZoneName, zInfo.getZoneForChunk({BSON("x" << 35), BSON("x" << 40)}));
- ASSERT_EQUALS(ZoneInfo::kNoZoneName,
- zInfo.getZoneForChunk({BSON("x" << 30), kSKeyPattern.globalMax()}));
- ASSERT_EQUALS(ZoneInfo::kNoZoneName,
- zInfo.getZoneForChunk({BSON("x" << 40), kSKeyPattern.globalMax()}));
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << 25));
+ chunk.setMax(BSON("x" << 30));
+ ASSERT_EQUALS("c", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << 35));
+ chunk.setMax(BSON("x" << 40));
+ ASSERT_EQUALS("", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << 30));
+ chunk.setMax(kMaxBSONKey);
+ ASSERT_EQUALS("", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << 40));
+ chunk.setMax(kMaxBSONKey);
+ ASSERT_EQUALS("", d.getTagForChunk(chunk));
+ }
}
TEST(DistributionStatus, ChunkTagsSelectorWithMinMaxKeys) {
ZoneInfo zInfo;
- ASSERT_OK(zInfo.addRangeToZone(ZoneRange(kSKeyPattern.globalMin(), BSON("x" << -100), "a")));
+ ASSERT_OK(zInfo.addRangeToZone(ZoneRange(kMinBSONKey, BSON("x" << -100), "a")));
ASSERT_OK(zInfo.addRangeToZone(ZoneRange(BSON("x" << -10), BSON("x" << 10), "b")));
- ASSERT_OK(zInfo.addRangeToZone(ZoneRange(BSON("x" << 100), kSKeyPattern.globalMax(), "c")));
-
- ASSERT_EQUALS("a", zInfo.getZoneForChunk({kSKeyPattern.globalMin(), BSON("x" << -100)}));
- ASSERT_EQUALS(ZoneInfo::kNoZoneName,
- zInfo.getZoneForChunk({BSON("x" << -100), BSON("x" << -11)}));
- ASSERT_EQUALS("b", zInfo.getZoneForChunk({BSON("x" << -10), BSON("x" << 0)}));
- ASSERT_EQUALS("b", zInfo.getZoneForChunk({BSON("x" << 0), BSON("x" << 10)}));
- ASSERT_EQUALS(ZoneInfo::kNoZoneName, zInfo.getZoneForChunk({BSON("x" << 10), BSON("x" << 20)}));
- ASSERT_EQUALS(ZoneInfo::kNoZoneName,
- zInfo.getZoneForChunk({BSON("x" << 10), BSON("x" << 100)}));
- ASSERT_EQUALS("c", zInfo.getZoneForChunk({BSON("x" << 200), kSKeyPattern.globalMax()}));
+ ASSERT_OK(zInfo.addRangeToZone(ZoneRange(BSON("x" << 100), kMaxBSONKey, "c")));
+ DistributionStatus d(kNamespace, ShardToChunksMap{}, std::move(zInfo));
+
+ {
+ ChunkType chunk;
+ chunk.setMin(kMinBSONKey);
+ chunk.setMax(BSON("x" << -100));
+ ASSERT_EQUALS("a", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << -100));
+ chunk.setMax(BSON("x" << -11));
+ ASSERT_EQUALS("", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << -10));
+ chunk.setMax(BSON("x" << 0));
+ ASSERT_EQUALS("b", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << 0));
+ chunk.setMax(BSON("x" << 10));
+ ASSERT_EQUALS("b", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << 10));
+ chunk.setMax(BSON("x" << 20));
+ ASSERT_EQUALS("", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << 10));
+ chunk.setMax(BSON("x" << 100));
+ ASSERT_EQUALS("", d.getTagForChunk(chunk));
+ }
+
+ {
+ ChunkType chunk;
+ chunk.setMin(BSON("x" << 200));
+ chunk.setMax(kMaxBSONKey);
+ ASSERT_EQUALS("c", d.getTagForChunk(chunk));
+ }
}
} // namespace
diff --git a/src/mongo/s/chunk_manager.h b/src/mongo/s/chunk_manager.h
index 138b04ffe34..d8ab66bc59f 100644
--- a/src/mongo/s/chunk_manager.h
+++ b/src/mongo/s/chunk_manager.h
@@ -680,23 +680,6 @@ public:
});
}
- template <typename Callable>
- void forEachOverlappingChunk(const BSONObj& min,
- const BSONObj& max,
- bool isMaxInclusive,
- Callable&& handler) const {
- _rt->optRt->forEachOverlappingChunk(
- min,
- max,
- isMaxInclusive,
- [this, handler = std::forward<Callable>(handler)](const auto& chunkInfo) mutable {
- if (!handler(Chunk{*chunkInfo, _clusterTime})) {
- return false;
- }
- return true;
- });
- }
-
/**
* Returns true if a document with the given "shardKey" is owned by the shard with the given
* "shardId" in this routing table. If "shardKey" is empty returns false. If "shardKey" is not a