void DatabaseCloner::_collectionClonerCallback(const Status& status, const NamespaceString& nss) { auto newStatus = status; UniqueLock lk(_mutex); if (!status.isOK()) { newStatus = {status.code(), str::stream() << "While cloning collection '" << nss.toString() << "' there was an error '" << status.reason() << "'" }; _failedNamespaces.push_back({newStatus, nss}); } ++_stats.clonedCollections; // Forward collection cloner result to caller. // Failure to clone a collection does not stop the database cloner // from cloning the rest of the collections in the listCollections result. lk.unlock(); _collectionWork(newStatus, nss); lk.lock(); _currentCollectionClonerIter++; if (_currentCollectionClonerIter != _collectionCloners.end()) { Status startStatus = _startCollectionCloner(*_currentCollectionClonerIter); if (!startStatus.isOK()) { LOG(1) << " failed to start collection cloning on " << _currentCollectionClonerIter->getSourceNamespace() << ": " << redact(startStatus); _finishCallback_inlock(lk, startStatus); return; } return; } Status finalStatus(Status::OK()); if (_failedNamespaces.size() > 0) { finalStatus = {ErrorCodes::InitialSyncFailure, str::stream() << "Failed to clone " << _failedNamespaces.size() << " collection(s) in '" << _dbname << "' from " << _source.toString() }; } _finishCallback_inlock(lk, finalStatus); }
void DatabaseCloner::_listCollectionsCallback(const StatusWith<Fetcher::QueryResponse>& result, Fetcher::NextAction* nextAction, BSONObjBuilder* getMoreBob) { if (!result.isOK()) { _finishCallback({result.getStatus().code(), str::stream() << "While issuing listCollections on db '" << _dbname << "' (host:" << _source.toString() << ") there was an error '" << result.getStatus().reason() << "'"}); return; } auto batchData(result.getValue()); auto&& documents = batchData.documents; UniqueLock lk(_mutex); // We may be called with multiple batches leading to a need to grow _collectionInfos. _collectionInfos.reserve(_collectionInfos.size() + documents.size()); _stats.collections += documents.size(); std::copy_if(documents.begin(), documents.end(), std::back_inserter(_collectionInfos), _listCollectionsPredicate); // The fetcher will continue to call with kGetMore until an error or the last batch. if (*nextAction == Fetcher::NextAction::kGetMore) { invariant(getMoreBob); getMoreBob->append("getMore", batchData.cursorId); getMoreBob->append("collection", batchData.nss.coll()); return; } // Nothing to do for an empty database. if (_collectionInfos.empty()) { _finishCallback_inlock(lk, Status::OK()); return; } _collectionNamespaces.reserve(_collectionInfos.size()); std::set<std::string> seen; for (auto&& info : _collectionInfos) { BSONElement nameElement = info.getField(kNameFieldName); if (nameElement.eoo()) { _finishCallback_inlock( lk, {ErrorCodes::FailedToParse, str::stream() << "collection info must contain '" << kNameFieldName << "' " << "field : " << info}); return; } if (nameElement.type() != mongo::String) { _finishCallback_inlock( lk, {ErrorCodes::TypeMismatch, str::stream() << "'" << kNameFieldName << "' field must be a string: " << info}); return; } const std::string collectionName = nameElement.String(); if (seen.find(collectionName) != seen.end()) { _finishCallback_inlock(lk, {ErrorCodes::DuplicateKey, str::stream() << "collection info contains duplicate collection name " << "'" << collectionName << "': " << info}); return; } BSONElement optionsElement = info.getField(kOptionsFieldName); if (optionsElement.eoo()) { _finishCallback_inlock( lk, {ErrorCodes::FailedToParse, str::stream() << "collection info must contain '" << kOptionsFieldName << "' " << "field : " << info}); return; } if (!optionsElement.isABSONObj()) { _finishCallback_inlock(lk, Status(ErrorCodes::TypeMismatch, str::stream() << "'" << kOptionsFieldName << "' field must be an object: " << info)); return; } const BSONObj optionsObj = optionsElement.Obj(); CollectionOptions options; Status parseStatus = options.parse(optionsObj); if (!parseStatus.isOK()) { _finishCallback_inlock(lk, parseStatus); return; } seen.insert(collectionName); _collectionNamespaces.emplace_back(_dbname, collectionName); auto&& nss = *_collectionNamespaces.crbegin(); try { _collectionCloners.emplace_back( _executor, _dbWorkThreadPool, _source, nss, options, stdx::bind( &DatabaseCloner::_collectionClonerCallback, this, stdx::placeholders::_1, nss), _storageInterface); } catch (const UserException& ex) { _finishCallback_inlock(lk, ex.toStatus()); return; } } if (_scheduleDbWorkFn) { for (auto&& collectionCloner : _collectionCloners) { collectionCloner.setScheduleDbWorkFn_forTest(_scheduleDbWorkFn); } } // Start first collection cloner. _currentCollectionClonerIter = _collectionCloners.begin(); LOG(1) << " cloning collection " << _currentCollectionClonerIter->getSourceNamespace(); Status startStatus = _startCollectionCloner(*_currentCollectionClonerIter); if (!startStatus.isOK()) { LOG(1) << " failed to start collection cloning on " << _currentCollectionClonerIter->getSourceNamespace() << ": " << startStatus; _finishCallback_inlock(lk, startStatus); return; } }