StatusWith<MigrateInfoVector> BalancerChunkSelectionPolicyImpl::_getMigrateCandidatesForCollection( OperationContext* txn, const NamespaceString& nss, const ShardStatisticsVector& shardStats, bool aggressiveBalanceHint) { auto scopedCMStatus = ScopedChunkManager::getExisting(txn, nss); if (!scopedCMStatus.isOK()) { return scopedCMStatus.getStatus(); } auto scopedCM = std::move(scopedCMStatus.getValue()); ChunkManager* const cm = scopedCM.cm(); auto collInfoStatus = createCollectionDistributionInfo(txn, shardStats, cm); if (!collInfoStatus.isOK()) { return collInfoStatus.getStatus(); } auto collInfo = std::move(collInfoStatus.getValue()); DistributionStatus distribution = std::move(std::get<0>(collInfo)); ChunkMinimumsSet allChunkMinimums = std::move(std::get<1>(collInfo)); for (const auto& tagRangeEntry : distribution.tagRanges()) { const auto& tagRange = tagRangeEntry.second; if (!allChunkMinimums.count(tagRange.min)) { // This tag falls somewhere at the middle of a chunk. Therefore we must skip balancing // this collection until it is split at the next iteration. // // TODO: We should be able to just skip chunks, which straddle tags and still make some // progress balancing. return {ErrorCodes::IllegalOperation, str::stream() << "Tag boundaries " << tagRange.toString() << " fall in the middle of an existing chunk. Balancing for collection " << nss.ns() << " will be postponed until the chunk is split appropriately."}; } } return BalancerPolicy::balance(shardStats, distribution, aggressiveBalanceHint); }
StatusWith<SplitInfoVector> BalancerChunkSelectionPolicyImpl::_getSplitCandidatesForCollection( OperationContext* txn, const NamespaceString& nss) { auto scopedCMStatus = ScopedChunkManager::getExisting(txn, nss); if (!scopedCMStatus.isOK()) { return scopedCMStatus.getStatus(); } auto scopedCM = std::move(scopedCMStatus.getValue()); ChunkManager* const cm = scopedCM.cm(); vector<TagsType> collectionTags; Status tagsStatus = Grid::get(txn)->catalogManager(txn)->getTagsForCollection(txn, nss.ns(), &collectionTags); if (!tagsStatus.isOK()) { return {tagsStatus.code(), str::stream() << "Unable to load tags for collection " << nss.ns() << " due to " << tagsStatus.toString()}; } auto collInfo = createCollectionDistributionInfo({}, cm); ChunkMinimumsSet allChunkMinimums = std::move(std::get<1>(collInfo)); SplitInfoVector splitCandidates; for (const auto& tagInfo : collectionTags) { BSONObj min = cm->getShardKeyPattern().getKeyPattern().extendRangeBound(tagInfo.getMinKey(), false); if (allChunkMinimums.count(min)) { continue; } shared_ptr<Chunk> chunk = cm->findIntersectingChunk(txn, min); invariant(chunk); splitCandidates.emplace_back( chunk->getShardId(), nss, cm->getVersion(), chunk->getMin(), chunk->getMax(), min); } return splitCandidates; }
StatusWith<SplitInfoVector> BalancerChunkSelectionPolicyImpl::_getSplitCandidatesForCollection( OperationContext* txn, const NamespaceString& nss, const ShardStatisticsVector& shardStats) { auto scopedCMStatus = ScopedChunkManager::getExisting(txn, nss); if (!scopedCMStatus.isOK()) { return scopedCMStatus.getStatus(); } auto scopedCM = std::move(scopedCMStatus.getValue()); ChunkManager* const cm = scopedCM.cm(); auto collInfoStatus = createCollectionDistributionInfo(txn, shardStats, cm); if (!collInfoStatus.isOK()) { return collInfoStatus.getStatus(); } auto collInfo = std::move(collInfoStatus.getValue()); DistributionStatus distribution = std::move(std::get<0>(collInfo)); ChunkMinimumsSet allChunkMinimums = std::move(std::get<1>(collInfo)); SplitInfoVector splitCandidates; // Accumulate split points for the same chunk together shared_ptr<Chunk> currentChunk; vector<BSONObj> currentSplitVector; for (const auto& tagRangeEntry : distribution.tagRanges()) { const auto& tagRange = tagRangeEntry.second; if (allChunkMinimums.count(tagRange.min)) { continue; } shared_ptr<Chunk> chunk = cm->findIntersectingChunkWithSimpleCollation(txn, tagRange.min); if (!currentChunk) { currentChunk = chunk; } invariant(currentChunk); if (chunk == currentChunk) { currentSplitVector.push_back(tagRange.min); } else { splitCandidates.emplace_back(currentChunk->getShardId(), nss, cm->getVersion(), currentChunk->getLastmod(), currentChunk->getMin(), currentChunk->getMax(), std::move(currentSplitVector)); currentChunk = chunk; currentSplitVector.push_back(tagRange.min); } } // Drain the current split vector if there are any entries left if (currentChunk) { invariant(!currentSplitVector.empty()); splitCandidates.emplace_back(currentChunk->getShardId(), nss, cm->getVersion(), currentChunk->getLastmod(), currentChunk->getMin(), currentChunk->getMax(), std::move(currentSplitVector)); } return splitCandidates; }
StatusWith<MigrateInfoVector> BalancerChunkSelectionPolicyImpl::_getMigrateCandidatesForCollection( OperationContext* txn, const NamespaceString& nss, const ShardStatisticsVector& shardStats, bool aggressiveBalanceHint) { auto scopedCMStatus = ScopedChunkManager::getExisting(txn, nss); if (!scopedCMStatus.isOK()) { return scopedCMStatus.getStatus(); } auto scopedCM = std::move(scopedCMStatus.getValue()); ChunkManager* const cm = scopedCM.cm(); auto collInfo = createCollectionDistributionInfo(shardStats, cm); ShardToChunksMap shardToChunksMap = std::move(std::get<0>(collInfo)); ChunkMinimumsSet allChunkMinimums = std::move(std::get<1>(collInfo)); DistributionStatus distStatus(shardStats, shardToChunksMap); { vector<TagsType> collectionTags; Status status = Grid::get(txn)->catalogManager(txn)->getTagsForCollection( txn, nss.ns(), &collectionTags); if (!status.isOK()) { return status; } for (const auto& tagInfo : collectionTags) { BSONObj min = cm->getShardKeyPattern().getKeyPattern().extendRangeBound( tagInfo.getMinKey(), false); if (!allChunkMinimums.count(min)) { // This tag falls somewhere at the middle of a chunk. Therefore we must skip // balancing this collection until it is split at the next iteration. // // TODO: We should be able to just skip chunks, which straddle tags and still make // some progress balancing. return {ErrorCodes::IllegalOperation, str::stream() << "Tag boundaries " << tagInfo.toString() << " fall in the middle of an existing chunk. Balancing for collection " << nss.ns() << " will be postponed until the chunk is split appropriately."}; } // TODO: TagRange contains all the information from TagsType except for the namespace, // so maybe the two can be merged at some point in order to avoid the transformation // below. if (!distStatus.addTagRange(TagRange(tagInfo.getMinKey().getOwned(), tagInfo.getMaxKey().getOwned(), tagInfo.getTag()))) { return {ErrorCodes::BadValue, str::stream() << "Tag ranges are not valid for collection " << nss.ns() << ". Balancing for this collection will be skipped until " "the ranges are fixed."}; } } } unique_ptr<MigrateInfo> migrateInfo( BalancerPolicy::balance(nss.ns(), distStatus, aggressiveBalanceHint)); if (migrateInfo) { return MigrateInfoVector{*migrateInfo}; } return MigrateInfoVector{}; }