void MongoCluster::loadIndex(NsTagUBIndex* index, const std::string& queryNs, MappingType* linkmap, const std::string& mappingName, const std::string group, const mongo::BSONObj& key) { //The indexes held type using index_mapped_type = typename NsTagUBIndex::mapped_type; index_mapped_type* idx = nullptr; mongo::Cursor cur = _dbConn->query(queryNs, mongo::Query().sort(BSON(group << 1))); std::string prevNs; while (cur->more()) { mongo::BSONObj obj = cur->next(); std::string mappingValue = obj.getStringField(mappingName); assert(!mappingValue.empty()); std::string ns = obj.getStringField(group); assert(!ns.empty()); if (ns != prevNs) { if (idx) idx->finalize(); prevNs = ns; idx = &index->emplace(ns, index_mapped_type(tools::BSONObjCmp(key))).first->second; } idx->insertUnordered(obj.getField("max").Obj().getOwned(), TagRange(linkmap->find(mappingValue), obj.getField("max").Obj().getOwned(), obj.getField("min").Obj().getOwned())); } //Sort chunks here. if (idx) idx->finalize(); }
void Balancer::_doBalanceRound( DBClientBase& conn, vector<CandidateChunkPtr>* candidateChunks ) { verify( candidateChunks ); // // 1. Check whether there is any sharded collection to be balanced by querying // the ShardsNS::collections collection // auto_ptr<DBClientCursor> cursor = conn.query( ShardNS::collection , BSONObj() ); vector< string > collections; while ( cursor->more() ) { BSONObj col = cursor->nextSafe(); // sharded collections will have a shard "key". if ( ! col["key"].eoo() && ! col["noBalance"].trueValue() ){ collections.push_back( col["_id"].String() ); } else if( col["noBalance"].trueValue() ){ LOG(1) << "not balancing collection " << col["_id"].String() << ", explicitly disabled" << endl; } } cursor.reset(); if ( collections.empty() ) { LOG(1) << "no collections to balance" << endl; return; } // // 2. Get a list of all the shards that are participating in this balance round // along with any maximum allowed quotas and current utilization. We get the // latter by issuing db.serverStatus() (mem.mapped) to all shards. // // TODO: skip unresponsive shards and mark information as stale. // vector<Shard> allShards; Shard::getAllShards( allShards ); if ( allShards.size() < 2) { LOG(1) << "can't balance without more active shards" << endl; return; } ShardInfoMap shardInfo; for ( vector<Shard>::const_iterator it = allShards.begin(); it != allShards.end(); ++it ) { const Shard& s = *it; ShardStatus status = s.getStatus(); shardInfo[ s.getName() ] = ShardInfo( s.getMaxSize(), status.mapped(), s.isDraining(), status.hasOpsQueued(), s.tags() ); } // // 3. For each collection, check if the balancing policy recommends moving anything around. // for (vector<string>::const_iterator it = collections.begin(); it != collections.end(); ++it ) { const string& ns = *it; map< string,vector<BSONObj> > shardToChunksMap; cursor = conn.query( ShardNS::chunk , QUERY( "ns" << ns ).sort( "min" ) ); while ( cursor->more() ) { BSONObj chunk = cursor->nextSafe(); if ( chunk["jumbo"].trueValue() ) continue; vector<BSONObj>& chunks = shardToChunksMap[chunk["shard"].String()]; chunks.push_back( chunk.getOwned() ); } cursor.reset(); if (shardToChunksMap.empty()) { LOG(1) << "skipping empty collection (" << ns << ")"; continue; } for ( vector<Shard>::iterator i=allShards.begin(); i!=allShards.end(); ++i ) { // this just makes sure there is an entry in shardToChunksMap for every shard Shard s = *i; shardToChunksMap[s.getName()].size(); } DistributionStatus status( shardInfo, shardToChunksMap ); // load tags conn.ensureIndex( ShardNS::tags, BSON( "ns" << 1 << "min" << 1 ), true ); cursor = conn.query( ShardNS::tags , QUERY( "ns" << ns ).sort( "min" ) ); while ( cursor->more() ) { BSONObj tag = cursor->nextSafe(); uassert( 16356 , str::stream() << "tag ranges not valid for: " << ns , status.addTagRange( TagRange( tag["min"].Obj().getOwned(), tag["max"].Obj().getOwned(), tag["tag"].String() ) ) ); } cursor.reset(); CandidateChunk* p = _policy->balance( ns, status, _balancedLastTime ); if ( p ) candidateChunks->push_back( CandidateChunkPtr( p ) ); } }
StatusWith<vector<MigrateInfo>> Balancer::_getCandidateChunks(OperationContext* txn) { vector<CollectionType> collections; Status collsStatus = grid.catalogManager(txn)->getCollections(txn, nullptr, &collections, nullptr); if (!collsStatus.isOK()) { return collsStatus; } if (collections.empty()) { return vector<MigrateInfo>(); } // Get a list of all the shards that are participating in this balance round along with any // maximum allowed quotas and current utilization. We get the latter by issuing // db.serverStatus() (mem.mapped) to all shards. // // TODO: skip unresponsive shards and mark information as stale. auto shardInfoStatus = DistributionStatus::populateShardInfoMap(txn); if (!shardInfoStatus.isOK()) { return shardInfoStatus.getStatus(); } const ShardInfoMap shardInfo(std::move(shardInfoStatus.getValue())); if (shardInfo.size() < 2) { return vector<MigrateInfo>(); } OCCASIONALLY warnOnMultiVersion(shardInfo); std::vector<MigrateInfo> candidateChunks; // For each collection, check if the balancing policy recommends moving anything around. for (const auto& coll : collections) { // Skip collections for which balancing is disabled const NamespaceString& nss = coll.getNs(); if (!coll.getAllowBalance()) { LOG(1) << "Not balancing collection " << nss << "; explicitly disabled."; continue; } std::vector<ChunkType> allNsChunks; Status status = grid.catalogManager(txn)->getChunks(txn, BSON(ChunkType::ns(nss.ns())), BSON(ChunkType::min() << 1), boost::none, // all chunks &allNsChunks, nullptr); if (!status.isOK()) { warning() << "failed to load chunks for ns " << nss.ns() << causedBy(status); continue; } set<BSONObj> allChunkMinimums; map<string, vector<ChunkType>> shardToChunksMap; for (const ChunkType& chunk : allNsChunks) { allChunkMinimums.insert(chunk.getMin().getOwned()); vector<ChunkType>& chunksList = shardToChunksMap[chunk.getShard()]; chunksList.push_back(chunk); } if (shardToChunksMap.empty()) { LOG(1) << "skipping empty collection (" << nss.ns() << ")"; continue; } for (ShardInfoMap::const_iterator i = shardInfo.begin(); i != shardInfo.end(); ++i) { // This loop just makes sure there is an entry in shardToChunksMap for every shard shardToChunksMap[i->first]; } DistributionStatus distStatus(shardInfo, shardToChunksMap); // 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. vector<TagRange> ranges; { vector<TagsType> collectionTags; uassertStatusOK( grid.catalogManager(txn)->getTagsForCollection(txn, nss.ns(), &collectionTags)); for (const auto& tt : collectionTags) { ranges.push_back( TagRange(tt.getMinKey().getOwned(), tt.getMaxKey().getOwned(), tt.getTag())); uassert(16356, str::stream() << "tag ranges not valid for: " << nss.ns(), distStatus.addTagRange(ranges.back())); } } auto statusGetDb = grid.catalogCache()->getDatabase(txn, nss.db().toString()); if (!statusGetDb.isOK()) { warning() << "could not load db config to balance collection [" << nss.ns() << "]: " << statusGetDb.getStatus(); continue; } shared_ptr<DBConfig> cfg = statusGetDb.getValue(); // This line reloads the chunk manager once if this process doesn't know the collection // is sharded yet. shared_ptr<ChunkManager> cm = cfg->getChunkManagerIfExists(txn, nss.ns(), true); if (!cm) { warning() << "could not load chunks to balance " << nss.ns() << " collection"; continue; } // Loop through tags to make sure no chunk spans tags. Split on tag min for all chunks. bool didAnySplits = false; for (const TagRange& range : ranges) { BSONObj min = cm->getShardKeyPattern().getKeyPattern().extendRangeBound(range.min, false); if (allChunkMinimums.count(min) > 0) { continue; } didAnySplits = true; log() << "nss: " << nss.ns() << " need to split on " << min << " because there is a range there"; vector<BSONObj> splitPoints; splitPoints.push_back(min); shared_ptr<Chunk> c = cm->findIntersectingChunk(txn, min); Status status = c->multiSplit(txn, splitPoints, NULL); if (!status.isOK()) { error() << "split failed: " << status; } else { LOG(1) << "split worked"; } break; } if (didAnySplits) { // State change, just wait till next round continue; } shared_ptr<MigrateInfo> migrateInfo( BalancerPolicy::balance(nss.ns(), distStatus, _balancedLastTime)); if (migrateInfo) { candidateChunks.emplace_back(*migrateInfo); } } return candidateChunks; }