ShardId BalancerPolicy::_getMostOverloadedShard(const ShardStatisticsVector& shardStats, const DistributionStatus& distribution, const string& chunkTag, const set<ShardId>& excludedShards) { ShardId worst; unsigned maxChunks = 0; for (const auto& stat : shardStats) { if (excludedShards.count(stat.shardId)) continue; const unsigned shardChunkCount = distribution.numberOfChunksInShardWithTag(stat.shardId, chunkTag); if (shardChunkCount <= maxChunks) continue; worst = stat.shardId; maxChunks = shardChunkCount; } return worst; }
bool BalancerPolicy::_singleZoneBalance(const ShardStatisticsVector& shardStats, const DistributionStatus& distribution, const string& tag, size_t imbalanceThreshold, vector<MigrateInfo>* migrations, set<ShardId>* usedShards) { const ShardId from = _getMostOverloadedShard(shardStats, distribution, tag, *usedShards); if (!from.isValid()) return false; const size_t max = distribution.numberOfChunksInShardWithTag(from, tag); if (max == 0) return false; const ShardId to = _getLeastLoadedReceiverShard(shardStats, distribution, tag, *usedShards); if (!to.isValid()) { if (migrations->empty()) { log() << "No available shards to take chunks for tag [" << tag << "]"; } return false; } const size_t min = distribution.numberOfChunksInShardWithTag(to, tag); if (min >= max) return false; const size_t totalNumberOfChunksWithTag = (tag.empty() ? distribution.totalChunks() : distribution.totalChunksWithTag(tag)); size_t totalNumberOfShardsWithTag = 0; for (const auto& stat : shardStats) { if (tag.empty() || stat.shardTags.count(tag)) { totalNumberOfShardsWithTag++; } } // totalNumberOfShardsWithTag cannot be zero if the to shard is valid invariant(totalNumberOfShardsWithTag); invariant(totalNumberOfChunksWithTag >= max); // The ideal should be at least one per shard const size_t idealNumberOfChunksPerShardWithTag = (totalNumberOfChunksWithTag < totalNumberOfShardsWithTag) ? 1 : (totalNumberOfChunksWithTag / totalNumberOfShardsWithTag); const size_t imbalance = max - idealNumberOfChunksPerShardWithTag; LOG(1) << "collection : " << distribution.nss().ns(); LOG(1) << "zone : " << tag; LOG(1) << "donor : " << from << " chunks on " << max; LOG(1) << "receiver : " << to << " chunks on " << min; LOG(1) << "ideal : " << idealNumberOfChunksPerShardWithTag; LOG(1) << "threshold : " << imbalanceThreshold; // Check whether it is necessary to balance within this zone if (imbalance < imbalanceThreshold) return false; const vector<ChunkType>& chunks = distribution.getChunks(from); unsigned numJumboChunks = 0; for (const auto& chunk : chunks) { if (distribution.getTagForChunk(chunk) != tag) continue; if (chunk.getJumbo()) { numJumboChunks++; continue; } migrations->emplace_back(distribution.nss().ns(), to, chunk); invariant(usedShards->insert(chunk.getShard()).second); invariant(usedShards->insert(to).second); return true; } if (numJumboChunks) { warning() << "Shard: " << from << ", collection: " << distribution.nss().ns() << " has only jumbo chunks for zone \'" << tag << "\' and cannot be balanced. Jumbo chunks count: " << numJumboChunks; } return false; }