Beispiel #1
0
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);
}
Beispiel #2
0
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;
    }
}