Esempio n. 1
0
    static void multiUpdate( OperationContext* txn,
                             const BatchItemRef& updateItem,
                             WriteOpResult* result ) {

        const NamespaceString nsString(updateItem.getRequest()->getNS());
        UpdateRequest request(nsString);
        request.setQuery(updateItem.getUpdate()->getQuery());
        request.setUpdates(updateItem.getUpdate()->getUpdateExpr());
        request.setMulti(updateItem.getUpdate()->getMulti());
        request.setUpsert(updateItem.getUpdate()->getUpsert());
        request.setUpdateOpLog(true);
        UpdateLifecycleImpl updateLifecycle(true, request.getNamespaceString());
        request.setLifecycle(&updateLifecycle);

        UpdateExecutor executor(&request, &txn->getCurOp()->debug());
        Status status = executor.prepare();
        if (!status.isOK()) {
            result->setError(toWriteError(status));
            return;
        }

        ///////////////////////////////////////////
        Lock::DBWrite writeLock(txn->lockState(), nsString.ns());
        ///////////////////////////////////////////

        if (!checkShardVersion(txn, &shardingState, *updateItem.getRequest(), result))
            return;

        Client::Context ctx( nsString.ns(),
                             storageGlobalParams.dbpath,
                             false /* don't check version */ );

        try {
            UpdateResult res = executor.execute(txn, ctx.db());

            const long long numDocsModified = res.numDocsModified;
            const long long numMatched = res.numMatched;
            const BSONObj resUpsertedID = res.upserted;

            // We have an _id from an insert
            const bool didInsert = !resUpsertedID.isEmpty();

            result->getStats().nModified = didInsert ? 0 : numDocsModified;
            result->getStats().n = didInsert ? 1 : numMatched;
            result->getStats().upsertedID = resUpsertedID;
        }
        catch (const DBException& ex) {
            status = ex.toStatus();
            if (ErrorCodes::isInterruption(status.code())) {
                throw;
            }
            result->setError(toWriteError(status));
        }
    }
Esempio n. 2
0
/**
 * Perform a remove operation, which might remove multiple documents.  Dispatches to remove code
 * currently to do most of this.
 *
 * Might fault or error, otherwise populates the result.
 */
static void multiRemove( const BatchItemRef& removeItem,
                         WriteOpResult* result ) {

    const NamespaceString nss( removeItem.getRequest()->getNS() );
    DeleteRequest request( nss );
    request.setQuery( removeItem.getDelete()->getQuery() );
    request.setMulti( removeItem.getDelete()->getLimit() != 1 );
    request.setUpdateOpLog(true);
    request.setGod( false );
    DeleteExecutor executor( &request );
    Status status = executor.prepare();
    if ( !status.isOK() ) {
        result->error = toWriteError( status );
        return;
    }

    // NOTE: Deletes will not fault outside the lock once any data has been written
    PageFaultRetryableSection pFaultSection;

    ///////////////////////////////////////////
    Lock::DBWrite writeLock( nss.ns() );
    ///////////////////////////////////////////

    // Check version once we're locked

    if ( !checkShardVersion( &shardingState, *removeItem.getRequest(), &result->error ) ) {
        // Version error
        return;
    }

    // Context once we're locked, to set more details in currentOp()
    // TODO: better constructor?
    Client::Context writeContext( nss.ns(),
                                  storageGlobalParams.dbpath,
                                  false /* don't check version */);

    try {
        result->stats.n = executor.execute();
    }
    catch ( const PageFaultException& ex ) {
        // TODO: An actual data structure that's not an exception for this
        result->fault = new PageFaultException( ex );
    }
    catch ( const DBException& ex ) {
        status = ex.toStatus();
        if (ErrorCodes::isInterruption(status.code())) {
            throw;
        }
        result->error = toWriteError(status);
    }
}
Esempio n. 3
0
    void WriteBatchExecutor::execUpdate( const BatchItemRef& updateItem,
                                         BSONObj* upsertedId,
                                         WriteErrorDetail** error ) {

        // Updates currently do a lot of the lock management internally

        const BatchedCommandRequest& request = *updateItem.getRequest();
        const NamespaceString nss( updateItem.getRequest()->getNS() );

        // BEGIN CURRENT OP
        scoped_ptr<CurOp> currentOp( beginCurrentOp( _client, updateItem ) );
        incOpStats( updateItem );

        WriteOpResult result;

        {
            ///////////////////////////////////////////
            Lock::DBWrite writeLock( nss.ns() );
            ///////////////////////////////////////////

            // Check version once we're locked

            if ( checkShardVersion( &shardingState, request, &result.error ) ) {

                // Context once we're locked, to set more details in currentOp()
                // TODO: better constructor?
                Client::Context writeContext( nss.ns(),
                                              storageGlobalParams.dbpath,
                                              false /* don't check version */);

                multiUpdate( updateItem, &result );

                incWriteStats( updateItem, result.stats, result.error, currentOp.get() );

                if ( !result.stats.upsertedID.isEmpty() ) {
                    *upsertedId = result.stats.upsertedID.getOwned();
                }
            }
        }

        // END CURRENT OP
        finishCurrentOp( _client, currentOp.get(), result.error );

        if ( result.error ) {
            result.error->setIndex( updateItem.getItemIndex() );
            *error = result.releaseError();
        }
    }
Esempio n. 4
0
    void DBClientSafeWriter::safeWrite( DBClientBase* conn,
                                        const BatchItemRef& itemRef,
                                        LastError* error ) {

        const BatchedCommandRequest* request = itemRef.getRequest();

        try {

            // Default settings for checkShardVersion
            const bool authoritative = false;
            const int tryNum = 1;

            // We need to set our version using setShardVersion, managed by checkShardVersionCB
            versionManager.checkShardVersionCB( conn,
                                                request->getTargetingNS(),
                                                authoritative,
                                                tryNum );

            if ( request->getBatchType() == BatchedCommandRequest::BatchType_Insert ) {
                conn->insert( request->getNS(),
                              request->getInsertRequest()->getDocumentsAt( itemRef.getItemIndex() ),
                              0 );
            }
            else if ( request->getBatchType() == BatchedCommandRequest::BatchType_Update ) {
                const BatchedUpdateDocument* update =
                    request->getUpdateRequest()->getUpdatesAt( itemRef.getItemIndex() );
                conn->update( request->getNS(),
                              update->getQuery(),
                              update->getUpdateExpr(),
                              update->getUpsert(),
                              update->getMulti() );
            }
            else {
                dassert( request->getBatchType() == BatchedCommandRequest::BatchType_Delete );
                const BatchedDeleteDocument* deleteDoc =
                    request->getDeleteRequest()->getDeletesAt( itemRef.getItemIndex() );
                conn->remove( request->getNS(),
                              deleteDoc->getQuery(),
                              deleteDoc->getLimit() == 1 /*just one*/);
            }

            // Default GLE Options
            const bool fsync = false;
            const bool j = false;
            const int w = 1;
            const int wtimeout = 0;

            BSONObj result = conn->getLastErrorDetailed( NamespaceString( request->getNS() ).db()
                                                             .toString(),
                                                         fsync,
                                                         j,
                                                         w,
                                                         wtimeout );

            SafeWriter::fillLastError( result, error );
        }
        catch ( const DBException& ex ) {
            error->raiseError( ex.getCode(), ex.toString().c_str() );
        }
    }
Esempio n. 5
0
    /**
     * Perform a single insert into a collection.  Requires the insert be preprocessed and the
     * collection already has been created.
     *
     * Might fault or error, otherwise populates the result.
     */
    static void singleInsert( const BatchItemRef& insertItem,
                              const BSONObj& normalInsert,
                              Collection* collection,
                              WriteOpResult* result ) {

        const string& insertNS = insertItem.getRequest()->getNS();

        Lock::assertWriteLocked( insertNS );

        try {

            // XXX - are we 100% sure that all !OK statuses do not write a document?
            StatusWith<DiskLoc> status = collection->insertDocument( normalInsert, true );

            if ( !status.isOK() ) {
                result->error = toWriteError( status.getStatus() );
            }
            else {
                logOp( "i", insertNS.c_str(), normalInsert );
                getDur().commitIfNeeded();
                result->stats.n = 1;
            }
        }
        catch ( const PageFaultException& ex ) {
            // TODO: An actual data structure that's not an exception for this
            result->fault = new PageFaultException( ex );
        }
        catch ( const DBException& ex ) {
            result->error = toWriteError( ex.toStatus() );
        }

    }
Esempio n. 6
0
    /**
     * Perform a single index insert into a collection.  Requires the index descriptor be
     * preprocessed and the collection already has been created.
     *
     * Might fault or error, otherwise populates the result.
     */
    static void singleCreateIndex( const BatchItemRef& insertItem,
                                   const BSONObj& normalIndexDesc,
                                   Collection* collection,
                                   WriteOpResult* result ) {

        const string& indexNS = insertItem.getRequest()->getNS();

        Lock::assertWriteLocked( indexNS );

        try {

            Status status = collection->getIndexCatalog()->createIndex( normalIndexDesc, true );

            if ( status.code() == ErrorCodes::IndexAlreadyExists ) {
                result->stats.n = 0;
            }
            else if ( !status.isOK() ) {
                result->error = toWriteError( status );
            }
            else {
                logOp( "i", indexNS.c_str(), normalIndexDesc );
                result->stats.n = 1;
            }
        }
        catch ( const PageFaultException& ex ) {
            // TODO: An actual data structure that's not an exception for this
            result->fault = new PageFaultException( ex );
        }
        catch ( const DBException& ex ) {
            result->error = toWriteError( ex.toStatus() );
        }
    }
Esempio n. 7
0
    /**
     * Perform an update operation, which might update multiple documents in the lock.  Dispatches
     * to update code currently to do most of this.
     *
     * Might error, otherwise populates the result.
     */
    static void multiUpdate( const BatchItemRef& updateItem,
                             WriteOpResult* result ) {

        Lock::assertWriteLocked( updateItem.getRequest()->getNS() );

        BSONObj queryObj = updateItem.getUpdate()->getQuery();
        BSONObj updateObj = updateItem.getUpdate()->getUpdateExpr();
        bool multi = updateItem.getUpdate()->getMulti();
        bool upsert = updateItem.getUpdate()->getUpsert();

        bool didInsert = false;
        long long numMatched = 0;
        long long numDocsModified = 0;
        BSONObj resUpsertedID;

        try {

            const NamespaceString requestNs( updateItem.getRequest()->getNS() );
            UpdateRequest request( requestNs );

            request.setQuery( queryObj );
            request.setUpdates( updateObj );
            request.setUpsert( upsert );
            request.setMulti( multi );
            request.setUpdateOpLog();
            // TODO(greg) We need to send if we are ignoring the shard version below,
            // but for now yes
            UpdateLifecycleImpl updateLifecycle( true, requestNs );
            request.setLifecycle( &updateLifecycle );

            UpdateResult res = update( request, &cc().curop()->debug() );

            numDocsModified = res.numDocsModified;
            numMatched = res.numMatched;
            resUpsertedID = res.upserted;

            // We have an _id from an insert
            didInsert = !resUpsertedID.isEmpty();

            result->stats.nModified = didInsert ? 0 : numDocsModified;
            result->stats.n = didInsert ? 1 : numMatched;
            result->stats.upsertedID = resUpsertedID;
        }
        catch ( const DBException& ex ) {
            result->error = toWriteError( ex.toStatus() );
        }
    }
Esempio n. 8
0
    /**
     * Perform a remove operation, which might remove multiple documents.  Dispatches to remove code
     * currently to do most of this.
     *
     * Might fault or error, otherwise populates the result.
     */
    static void multiRemove( OperationContext* txn,
                             const BatchItemRef& removeItem,
                             WriteOpResult* result ) {

        const NamespaceString nss( removeItem.getRequest()->getNS() );
        DeleteRequest request( nss );
        request.setQuery( removeItem.getDelete()->getQuery() );
        request.setMulti( removeItem.getDelete()->getLimit() != 1 );
        request.setUpdateOpLog(true);
        request.setGod( false );
        DeleteExecutor executor( &request );
        Status status = executor.prepare();
        if ( !status.isOK() ) {
            result->setError(toWriteError(status));
            return;
        }

        ///////////////////////////////////////////
        Lock::DBWrite writeLock(txn->lockState(), nss.ns());
        ///////////////////////////////////////////

        // Check version once we're locked

        if (!checkShardVersion(txn, &shardingState, *removeItem.getRequest(), result)) {
            // Version error
            return;
        }

        // Context once we're locked, to set more details in currentOp()
        // TODO: better constructor?
        Client::Context writeContext( nss.ns(),
                                      storageGlobalParams.dbpath,
                                      false /* don't check version */);

        try {
            result->getStats().n = executor.execute(txn, writeContext.db());
        }
        catch ( const DBException& ex ) {
            status = ex.toStatus();
            if (ErrorCodes::isInterruption(status.code())) {
                throw;
            }
            result->setError(toWriteError(status));
        }
    }
Esempio n. 9
0
    Status DBClientSafeWriter::safeWrite( DBClientBase* conn,
                                          const BatchItemRef& itemRef,
                                          const BSONObj& writeConcern,
                                          BSONObj* gleResponse ) {

        const BatchedCommandRequest* request = itemRef.getRequest();

        try {

            // Default settings for checkShardVersion
            const bool authoritative = false;
            const int tryNum = 1;

            // We need to set our version using setShardVersion, managed by checkShardVersionCB
            versionManager.checkShardVersionCB( conn,
                                                request->getTargetingNS(),
                                                authoritative,
                                                tryNum );

            if ( request->getBatchType() == BatchedCommandRequest::BatchType_Insert ) {
                conn->insert( request->getNS(),
                              request->getInsertRequest()->getDocumentsAt( itemRef.getItemIndex() ),
                              0 );
            }
            else if ( request->getBatchType() == BatchedCommandRequest::BatchType_Update ) {
                const BatchedUpdateDocument* update =
                    request->getUpdateRequest()->getUpdatesAt( itemRef.getItemIndex() );
                conn->update( request->getNS(),
                              update->getQuery(),
                              update->getUpdateExpr(),
                              update->getUpsert(),
                              update->getMulti() );
            }
            else {
                dassert( request->getBatchType() == BatchedCommandRequest::BatchType_Delete );
                const BatchedDeleteDocument* deleteDoc =
                    request->getDeleteRequest()->getDeletesAt( itemRef.getItemIndex() );
                conn->remove( request->getNS(),
                              deleteDoc->getQuery(),
                              deleteDoc->getLimit() == 1 /*just one*/);
            }

            const StringData& dbName = NamespaceString( request->getNS() ).db();

            BSONObjBuilder gleCmdB;
            gleCmdB.append( "getLastError", true );
            gleCmdB.appendElements( writeConcern );

            conn->runCommand( dbName.toString(), gleCmdB.obj(), *gleResponse );
        }
        catch ( const DBException& ex ) {
            return ex.toStatus();
        }

        return Status::OK();
    }
Esempio n. 10
0
    /**
     * Perform a remove operation, which might remove multiple documents.  Dispatches to remove code
     * currently to do most of this.
     *
     * Might fault or error, otherwise populates the result.
     */
    static void multiRemove( const BatchItemRef& removeItem,
                             WriteOpResult* result ) {

        Lock::assertWriteLocked( removeItem.getRequest()->getNS() );

        try {
            long long n = deleteObjects( removeItem.getRequest()->getNS(),
                                         removeItem.getDelete()->getQuery(),
                                         removeItem.getDelete()->getLimit() == 1, // justOne
                                         true, // logOp
                                         false // god
                                         );

            result->stats.n = n;
        }
        catch ( const PageFaultException& ex ) {
            // TODO: An actual data structure that's not an exception for this
            result->fault = new PageFaultException( ex );
        }
        catch ( const DBException& ex ) {
            result->error = toWriteError( ex.toStatus() );
        }
    }
Esempio n. 11
0
    // Does preprocessing of inserts, special casing for indexes
    // TODO: Simplify this when indexes aren't here anymore
    static StatusWith<BSONObj> normalizeInsert( const BatchItemRef& insertItem ) {

        if ( insertItem.getRequest()->isInsertIndexRequest() ) {

            StatusWith<BSONObj> normalInsert = fixDocumentForInsert( insertItem.getDocument() );
            if ( normalInsert.isOK() && insertItem.getDocument()["ns"].type() != String ) {
                return StatusWith<BSONObj>( ErrorCodes::BadValue, "tried to create an index "
                                            "without specifying namespace" );
            }
            else {
                return normalInsert;
            }
        }
        else {
            return fixDocumentForInsert( insertItem.getDocument() );
        }
    }
Esempio n. 12
0
    bool WriteBatchExecutor::doWrite( const string& ns,
                                      const BatchItemRef& itemRef,
                                      CurOp* currentOp,
                                      WriteStats* stats,
                                      BSONObj* upsertedID,
                                      BatchedErrorDetail* error ) {

        const BatchedCommandRequest& request = *itemRef.getRequest();
        int index = itemRef.getItemIndex();

        //
        // Check our shard version if we need to (must be in the write lock)
        //

        CollectionMetadataPtr metadata;
        if ( shardingState.enabled() ) {

            // Index inserts make the namespace nontrivial for versioning
            string targetingNS = itemRef.getRequest()->getTargetingNS();
            Lock::assertWriteLocked( targetingNS );
            metadata = shardingState.getCollectionMetadata( targetingNS );

            if ( request.isShardVersionSet()
                 && !ChunkVersion::isIgnoredVersion( request.getShardVersion() ) ) {

                ChunkVersion shardVersion =
                    metadata ? metadata->getShardVersion() : ChunkVersion::UNSHARDED();

                if ( !request.getShardVersion() //
                    .isWriteCompatibleWith( shardVersion ) ) {

                    buildStaleError( request.getShardVersion(), shardVersion, error );
                    return false;
                }
            }
        }

        //
        // Not stale, do the actual write
        //

        if ( request.getBatchType() == BatchedCommandRequest::BatchType_Insert ) {

            // Need to check for unique index problems
            if ( metadata && request.isUniqueIndexRequest() ) {
                if ( !isUniqueIndexCompatible( metadata->getKeyPattern(),
                                               request.getIndexKeyPattern() ) ) {

                    buildUniqueIndexError( metadata->getKeyPattern(),
                                           request.getIndexKeyPattern(),
                                           error );
                    return false;
                }
            }

            // Insert
            return doInsert( ns,
                             request.getInsertRequest()->getDocumentsAt( index ),
                             currentOp,
                             stats,
                             error );
        }
        else if ( request.getBatchType() == BatchedCommandRequest::BatchType_Update ) {

            // TODO: Pass down immutable shard key fields

            // Update
            return doUpdate( ns,
                             *request.getUpdateRequest()->getUpdatesAt( index ),
                             currentOp,
                             stats,
                             upsertedID,
                             error );
        }
        else {
            dassert( request.getBatchType() ==
                BatchedCommandRequest::BatchType_Delete );

            // Delete
            return doDelete( ns,
                             *request.getDeleteRequest()->getDeletesAt( index ),
                             currentOp,
                             stats,
                             error );
        }
    }
Esempio n. 13
0
    bool WriteBatchExecutor::applyWriteItem( const BatchItemRef& itemRef,
                                             WriteStats* stats,
                                             BSONObj* upsertedID,
                                             BatchedErrorDetail* error ) {
        const BatchedCommandRequest& request = *itemRef.getRequest();
        const string& ns = request.getNS();

        // Clear operation's LastError before starting.
        _le->reset( true );

        //uint64_t itemTimeMicros = 0;
        bool opSuccess = true;

        // Each write operation executes in its own PageFaultRetryableSection.  This means that
        // a single batch can throw multiple PageFaultException's, which is not the case for
        // other operations.
        PageFaultRetryableSection s;
        while ( true ) {
            try {
                // Execute the write item as a child operation of the current operation.
                CurOp childOp( _client, _client->curop() );

                HostAndPort remote =
                    _client->hasRemote() ? _client->getRemote() : HostAndPort( "0.0.0.0", 0 );

                // TODO Modify CurOp "wrapped" constructor to take an opcode, so calling .reset()
                // is unneeded
                childOp.reset( remote, getOpCode( request.getBatchType() ) );

                childOp.ensureStarted();
                OpDebug& opDebug = childOp.debug();
                opDebug.ns = ns;
                {
                    Lock::DBWrite dbLock( ns );
                    Client::Context ctx( ns,
                                         storageGlobalParams.dbpath, // TODO: better constructor?
                                         false /* don't check version here */);

                    opSuccess = doWrite( ns, itemRef, &childOp, stats, upsertedID, error );
                }
                childOp.done();
                //itemTimeMicros = childOp.totalTimeMicros();

                opDebug.executionTime = childOp.totalTimeMillis();
                opDebug.recordStats();

                // Log operation if running with at least "-v", or if exceeds slow threshold.
                if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1))
                     || opDebug.executionTime >
                        serverGlobalParams.slowMS + childOp.getExpectedLatencyMs()) {

                    MONGO_TLOG(1) << opDebug.report( childOp ) << endl;
                }

                // TODO Log operation if logLevel >= 3 and assertion thrown (as assembleResponse()
                // does).

                // Save operation to system.profile if shouldDBProfile().
                if ( childOp.shouldDBProfile( opDebug.executionTime ) ) {
                    profile( *_client, getOpCode( request.getBatchType() ), childOp );
                }
                break;
            }
            catch ( PageFaultException& e ) {
                e.touch();
            }
        }

        return opSuccess;
    }
Esempio n. 14
0
    Status Strategy::commandOpWrite(const std::string& dbName,
                                    const BSONObj& command,
                                    BatchItemRef targetingBatchItem,
                                    std::vector<CommandResult>* results) {

        // Note that this implementation will not handle targeting retries and does not completely
        // emulate write behavior

        ChunkManagerTargeter targeter(NamespaceString(
                                        targetingBatchItem.getRequest()->getTargetingNS()));
        Status status = targeter.init();
        if (!status.isOK())
            return status;

        OwnedPointerVector<ShardEndpoint> endpointsOwned;
        vector<ShardEndpoint*>& endpoints = endpointsOwned.mutableVector();

        if (targetingBatchItem.getOpType() == BatchedCommandRequest::BatchType_Insert) {
            ShardEndpoint* endpoint;
            Status status = targeter.targetInsert(targetingBatchItem.getDocument(), &endpoint);
            if (!status.isOK())
                return status;
            endpoints.push_back(endpoint);
        }
        else if (targetingBatchItem.getOpType() == BatchedCommandRequest::BatchType_Update) {
            Status status = targeter.targetUpdate(*targetingBatchItem.getUpdate(), &endpoints);
            if (!status.isOK())
                return status;
        }
        else {
            invariant(targetingBatchItem.getOpType() == BatchedCommandRequest::BatchType_Delete);
            Status status = targeter.targetDelete(*targetingBatchItem.getDelete(), &endpoints);
            if (!status.isOK())
                return status;
        }

        DBClientShardResolver resolver;
        DBClientMultiCommand dispatcher;

        // Assemble requests
        for (vector<ShardEndpoint*>::const_iterator it = endpoints.begin(); it != endpoints.end();
            ++it) {

            const ShardEndpoint* endpoint = *it;

            ConnectionString host;
            Status status = resolver.chooseWriteHost(endpoint->shardName, &host);
            if (!status.isOK())
                return status;

            RawBSONSerializable request(command);
            dispatcher.addCommand(host, dbName, request);
        }

        // Errors reported when recv'ing responses
        dispatcher.sendAll();
        Status dispatchStatus = Status::OK();

        // Recv responses
        while (dispatcher.numPending() > 0) {

            ConnectionString host;
            RawBSONSerializable response;

            Status status = dispatcher.recvAny(&host, &response);
            if (!status.isOK()) {
                // We always need to recv() all the sent operations
                dispatchStatus = status;
                continue;
            }

            CommandResult result;
            result.target = host;
            result.shardTarget = Shard::make(host.toString());
            result.result = response.toBSON();

            results->push_back(result);
        }

        return dispatchStatus;
    }
Esempio n. 15
0
    bool WriteBatchExecutor::doWrite( const string& ns,
                                      const BatchItemRef& itemRef,
                                      CurOp* currentOp,
                                      WriteStats* stats,
                                      BSONObj* upsertedID,
                                      BatchedErrorDetail* error ) {

        const BatchedCommandRequest& request = *itemRef.getRequest();
        int index = itemRef.getItemIndex();

        //
        // Check our shard version if we need to (must be in the write lock)
        //

        if ( shardingState.enabled() && request.isShardVersionSet()
             && !ChunkVersion::isIgnoredVersion( request.getShardVersion() ) ) {

            Lock::assertWriteLocked( ns );
            CollectionMetadataPtr metadata = shardingState.getCollectionMetadata( ns );
            ChunkVersion shardVersion =
                metadata ? metadata->getShardVersion() : ChunkVersion::UNSHARDED();

            if ( !request.getShardVersion() //
                .isWriteCompatibleWith( shardVersion ) ) {

                // Write stale error to results
                error->setErrCode( ErrorCodes::StaleShardVersion );

                BSONObjBuilder infoB;
                shardVersion.addToBSON( infoB, "vWanted" );
                error->setErrInfo( infoB.obj() );

                string errMsg = mongoutils::str::stream()
                    << "stale shard version detected before write, received "
                    << request.getShardVersion().toString() << " but local version is "
                    << shardVersion.toString();
                error->setErrMessage( errMsg );

                return false;
            }
        }

        //
        // Not stale, do the actual write
        //

        if ( request.getBatchType() == BatchedCommandRequest::BatchType_Insert ) {

            // Insert
            return doInsert( ns,
                             request.getInsertRequest()->getDocumentsAt( index ),
                             currentOp,
                             stats,
                             error );
        }
        else if ( request.getBatchType() == BatchedCommandRequest::BatchType_Update ) {

            // Update
            return doUpdate( ns,
                             *request.getUpdateRequest()->getUpdatesAt( index ),
                             currentOp,
                             stats,
                             upsertedID,
                             error );
        }
        else {
            dassert( request.getBatchType() ==
                BatchedCommandRequest::BatchType_Delete );

            // Delete
            return doDelete( ns,
                             *request.getDeleteRequest()->getDeletesAt( index ),
                             currentOp,
                             stats,
                             error );
        }
    }
Esempio n. 16
0
    void WriteBatchExecutor::execRemove( const BatchItemRef& removeItem,
                                         WriteErrorDetail** error ) {

        // Removes are similar to updates, but page faults are handled externally

        const BatchedCommandRequest& request = *removeItem.getRequest();
        const NamespaceString nss( removeItem.getRequest()->getNS() );

        // BEGIN CURRENT OP
        scoped_ptr<CurOp> currentOp( beginCurrentOp( _client, removeItem ) );
        incOpStats( removeItem );

        WriteOpResult result;

        while ( true ) {

            {
                // NOTE: Deletes will not fault outside the lock once any data has been written
                PageFaultRetryableSection pFaultSection;

                ///////////////////////////////////////////
                Lock::DBWrite writeLock( nss.ns() );
                ///////////////////////////////////////////

                // Check version once we're locked

                if ( !checkShardVersion( &shardingState, request, &result.error ) ) {
                    // Version error
                    break;
                }

                // Context once we're locked, to set more details in currentOp()
                // TODO: better constructor?
                Client::Context writeContext( nss.ns(),
                                              storageGlobalParams.dbpath,
                                              false /* don't check version */);

                multiRemove( removeItem, &result );

                if ( !result.fault ) {
                    incWriteStats( removeItem, result.stats, result.error, currentOp.get() );
                    break;
                }
            }

            //
            // Check page fault out of lock
            //

            dassert( result.fault );
            result.fault->touch();
            result.reset();
        }

        // END CURRENT OP
        finishCurrentOp( _client, currentOp.get(), result.error );

        if ( result.error ) {
            result.error->setIndex( removeItem.getItemIndex() );
            *error = result.releaseError();
        }
    }