コード例 #1
0
void BatchSafeWriter::safeWriteBatch( DBClientBase* conn,
                                      const BatchedCommandRequest& request,
                                      BatchedCommandResponse* response ) {

    // N starts at zero, and we add to it for each item
    response->setN( 0 );

    for ( size_t i = 0; i < request.sizeWriteOps(); ++i ) {

        BatchItemRef itemRef( &request, static_cast<int>( i ) );
        LastError lastError;

        _safeWriter->safeWrite( conn, itemRef, &lastError );

        // Register the error if we need to
        WriteErrorDetail* batchError = lastErrorToBatchError( lastError );
        if ( batchError ) {
            batchError->setIndex( i );
            response->addToErrDetails( batchError );
        }

        response->setN( response->getN() + lastError.nObjects );

        if ( !lastError.upsertedId.isEmpty() ) {
            BatchedUpsertDetail* upsertedId = new BatchedUpsertDetail;
            upsertedId->setIndex( i );
            upsertedId->setUpsertedID( lastError.upsertedId );
            response->addToUpsertDetails( upsertedId );
        }

        // Break on first error if we're ordered
        if ( request.getOrdered() && BatchSafeWriter::isFailedOp( lastError ) ) break;
    }

    if ( request.sizeWriteOps() == 1 && response->isErrDetailsSet()
            && !response->isErrCodeSet() ) {

        // Promote single error to batch error
        const WriteErrorDetail* error = response->getErrDetailsAt( 0 );
        response->setErrCode( error->getErrCode() );
        if ( error->isErrInfoSet() ) response->setErrInfo( error->getErrInfo() );
        response->setErrMessage( error->getErrMessage() );

        response->unsetErrDetails();
    }

    if ( request.sizeWriteOps() == 1 && response->isUpsertDetailsSet() ) {

        // Promote single upsert to batch upsert
        const BatchedUpsertDetail* upsertedId = response->getUpsertDetailsAt( 0 );
        response->setSingleUpserted( upsertedId->getUpsertedID() );

        response->unsetUpsertDetails();
    }

    response->setOk( !response->isErrCodeSet() );
    dassert( response->isValid( NULL ) );
}
コード例 #2
0
    void WriteBatchExecutor::execInserts( const BatchedCommandRequest& request,
                                          std::vector<WriteErrorDetail*>* errors ) {

        // Theory of operation:
        //
        // Instantiates an ExecInsertsState, which represents all of the state involved in the batch
        // insert execution algorithm.  Most importantly, encapsulates the lock state.
        //
        // Every iteration of the loop in execInserts() processes one document insertion, by calling
        // insertOne() exactly once for a given value of state.currIndex.
        //
        // If the ExecInsertsState indicates that the requisite write locks are not held, insertOne
        // acquires them and performs lock-acquisition-time checks.  However, on non-error
        // execution, it does not release the locks.  Therefore, the yielding logic in the while
        // loop in execInserts() is solely responsible for lock release in the non-error case.
        //
        // Internally, insertOne loops performing the single insert until it completes without a
        // PageFaultException, or until it fails with some kind of error.  Errors are mostly
        // propagated via the request->error field, but DBExceptions or std::exceptions may escape,
        // particularly on operation interruption.  These kinds of errors necessarily prevent
        // further insertOne calls, and stop the batch.  As a result, the only expected source of
        // such exceptions are interruptions.
        ExecInsertsState state(&request);
        normalizeInserts(request, &state.normalizedInserts, &state.pregeneratedKeys);

        ElapsedTracker elapsedTracker(128, 10); // 128 hits or 10 ms, matching RunnerYieldPolicy's

        for (state.currIndex = 0;
             state.currIndex < state.request->sizeWriteOps();
             ++state.currIndex) {

            if (elapsedTracker.intervalHasElapsed()) {
                // Consider yielding between inserts.

                if (state.hasLock()) {
                    int micros = ClientCursor::suggestYieldMicros();
                    if (micros > 0) {
                        state.unlock();
                        killCurrentOp.checkForInterrupt();
                        sleepmicros(micros);
                    }
                }
                killCurrentOp.checkForInterrupt();
                elapsedTracker.resetLastTime();
            }

            WriteErrorDetail* error = NULL;
            execOneInsert(&state, &error);
            if (error) {
                errors->push_back(error);
                error->setIndex(state.currIndex);
                if (request.getOrdered())
                    return;
            }
        }
    }
コード例 #3
0
ファイル: batch_executor.cpp プロジェクト: AndrewCEmil/mongo
    static WriteErrorDetail* toWriteError( const Status& status ) {

        WriteErrorDetail* error = new WriteErrorDetail;

        // TODO: Complex transform here?
        error->setErrCode( status.code() );
        error->setErrMessage( status.reason() );

        return error;
    }
コード例 #4
0
static bool checkIsMasterForCollection(const NamespaceString& ns, WriteErrorDetail** error) {
    if (!isMasterNs(ns.ns().c_str())) {
        WriteErrorDetail* errorDetail = *error = new WriteErrorDetail;
        errorDetail->setErrCode(ErrorCodes::NotMaster);
        errorDetail->setErrMessage(std::string(mongoutils::str::stream() <<
                                               "Not primary while writing to " << ns.ns()));
        return false;
    }
    return true;
}
コード例 #5
0
 static bool checkIsMasterForCollection(const std::string& ns, WriteOpResult* result) {
     if (!isMasterNs(ns.c_str())) {
         WriteErrorDetail* errorDetail = new WriteErrorDetail;
         result->setError(errorDetail);
         errorDetail->setErrCode(ErrorCodes::NotMaster);
         errorDetail->setErrMessage("Not primary while writing to " + ns);
         return false;
     }
     return true;
 }
コード例 #6
0
ファイル: batch_executor.cpp プロジェクト: AndrewCEmil/mongo
 static bool checkIsMasterForDatabase(const std::string& ns, WriteOpResult* result) {
     if (!repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(
             NamespaceString(ns).db())) {
         WriteErrorDetail* errorDetail = new WriteErrorDetail;
         result->setError(errorDetail);
         errorDetail->setErrCode(ErrorCodes::NotMaster);
         errorDetail->setErrMessage("Not primary while writing to " + ns);
         return false;
     }
     return true;
 }
コード例 #7
0
void WriteOp::setOpError(const WriteErrorDetail& error) {
    dassert(_state == WriteOpState_Ready);
    _error.reset(new WriteErrorDetail);
    error.cloneTo(_error.get());
    _error->setIndex(_itemRef.getItemIndex());
    _state = WriteOpState_Error;
    // No need to updateOpState, set directly
}
コード例 #8
0
void WriteOp::noteWriteError(const TargetedWrite& targetedWrite, const WriteErrorDetail& error) {
    const WriteOpRef& ref = targetedWrite.writeOpRef;
    auto& childOp = _childOps[ref.second];

    childOp.pendingWrite = NULL;
    childOp.endpoint.reset(new ShardEndpoint(targetedWrite.endpoint));
    childOp.error.reset(new WriteErrorDetail);
    error.cloneTo(childOp.error.get());
    dassert(ref.first == _itemRef.getItemIndex());
    childOp.error->setIndex(_itemRef.getItemIndex());
    childOp.state = WriteOpState_Error;
    _updateOpState();
}
コード例 #9
0
ファイル: batch_write_op.cpp プロジェクト: qihsh/mongo
static void toWriteErrorResponse(const WriteErrorDetail& error,
                                 bool ordered,
                                 int numWrites,
                                 BatchedCommandResponse* writeErrResponse) {
    writeErrResponse->setOk(true);
    writeErrResponse->setN(0);

    int numErrors = ordered ? 1 : numWrites;
    for (int i = 0; i < numErrors; i++) {
        unique_ptr<WriteErrorDetail> errorClone(new WriteErrorDetail);
        error.cloneTo(errorClone.get());
        errorClone->setIndex(i);
        writeErrResponse->addToErrDetails(errorClone.release());
    }

    dassert(writeErrResponse->isValid(NULL));
}
コード例 #10
0
WriteErrorDetail* BatchSafeWriter::lastErrorToBatchError( const LastError& lastError ) {

    bool isFailedOp = lastError.msg != "";
    bool isStaleOp = lastError.writebackId.isSet();
    dassert( !( isFailedOp && isStaleOp ) );

    if ( isFailedOp ) {
        WriteErrorDetail* batchError = new WriteErrorDetail;
        if ( lastError.code != 0 ) batchError->setErrCode( lastError.code );
        else batchError->setErrCode( ErrorCodes::UnknownError );
        batchError->setErrMessage( lastError.msg );
        return batchError;
    }
    else if ( isStaleOp ) {
        WriteErrorDetail* batchError = new WriteErrorDetail;
        batchError->setErrCode( ErrorCodes::StaleShardVersion );
        batchError->setErrInfo( BSON( "downconvert" << true ) ); // For debugging
        batchError->setErrMessage( "shard version was stale" );
        return batchError;
    }

    return NULL;
}
コード例 #11
0
bool batchErrorToLastError(const BatchedCommandRequest& request,
                           const BatchedCommandResponse& response,
                           LastError* error) {
    unique_ptr<WriteErrorDetail> commandError;
    WriteErrorDetail* lastBatchError = NULL;

    if (!response.getOk()) {
        // Command-level error, all writes failed

        commandError.reset(new WriteErrorDetail);
        buildErrorFromResponse(response, commandError.get());
        lastBatchError = commandError.get();
    } else if (response.isErrDetailsSet()) {
        // The last error in the batch is always reported - this matches expected COE
        // semantics for insert batches. For updates and deletes, error is only reported
        // if the error was on the last item.

        const bool lastOpErrored = response.getErrDetails().back()->getIndex() ==
            static_cast<int>(request.sizeWriteOps() - 1);
        if (request.getBatchType() == BatchedCommandRequest::BatchType_Insert || lastOpErrored) {
            lastBatchError = response.getErrDetails().back();
        }
    } else {
        // We don't care about write concern errors, these happen in legacy mode in GLE.
    }

    // Record an error if one exists
    if (lastBatchError) {
        string errMsg = lastBatchError->getErrMessage();
        error->setLastError(lastBatchError->getErrCode(),
                            errMsg.empty() ? "see code for details" : errMsg.c_str());
        return true;
    }

    // Record write stats otherwise
    // NOTE: For multi-write batches, our semantics change a little because we don't have
    // un-aggregated "n" stats.
    if (request.getBatchType() == BatchedCommandRequest::BatchType_Update) {
        BSONObj upsertedId;
        if (response.isUpsertDetailsSet()) {
            // Only report the very last item's upserted id if applicable
            if (response.getUpsertDetails().back()->getIndex() + 1 ==
                static_cast<int>(request.sizeWriteOps())) {
                upsertedId = response.getUpsertDetails().back()->getUpsertedID();
            }
        }

        int numUpserted = 0;
        if (response.isUpsertDetailsSet())
            numUpserted = response.sizeUpsertDetails();

        int numMatched = response.getN() - numUpserted;
        dassert(numMatched >= 0);

        // Wrap upserted id in "upserted" field
        BSONObj leUpsertedId;
        if (!upsertedId.isEmpty())
            leUpsertedId = upsertedId.firstElement().wrap(kUpsertedFieldName);

        error->recordUpdate(numMatched > 0, response.getN(), leUpsertedId);
    } else if (request.getBatchType() == BatchedCommandRequest::BatchType_Delete) {
        error->recordDelete(response.getN());
    }

    return false;
}