Esempio n. 1
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. 2
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. 3
0
void WriteBatchExecutor::execUpdate( const BatchItemRef& updateItem,
                                     BSONObj* upsertedId,
                                     WriteErrorDetail** error ) {

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

    WriteOpResult result;

    WriteUnitOfWork wunit(_txn->recoveryUnit());
    multiUpdate( _txn, updateItem, &result );
    wunit.commit();

    if ( !result.getStats().upsertedID.isEmpty() ) {
        *upsertedId = result.getStats().upsertedID;
    }
    // END CURRENT OP
    incWriteStats( updateItem, result.getStats(), result.getError(), currentOp.get() );
    finishCurrentOp( _txn, _client, currentOp.get(), result.getError() );

    if ( result.getError() ) {
        result.getError()->setIndex( updateItem.getItemIndex() );
        *error = result.releaseError();
    }
}
Esempio n. 4
0
    void WriteBatchExecutor::execRemove( const BatchItemRef& removeItem,
                                         WriteErrorDetail** error ) {

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

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

        WriteOpResult result;

        // NOTE: Deletes will not fault outside the lock once any data has been written
        PageFaultRetryableSection pageFaultSection;
        while ( true ) {
            try {
                multiRemove( removeItem, &result );
                break;
            }
            catch (PageFaultException& pfe) {
                pfe.touch();
                invariant(!result.getError());
                continue;
            }
            fassertFailed(17429);
        }

        // END CURRENT OP
        incWriteStats( removeItem, result.getStats(), result.getError(), currentOp.get() );
        finishCurrentOp( _client, currentOp.get(), result.getError() );

        if ( result.getError() ) {
            result.getError()->setIndex( removeItem.getItemIndex() );
            *error = result.releaseError();
        }
    }
Esempio n. 5
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. 6
0
    void WriteBatchExecutor::execRemove( const BatchItemRef& removeItem,
                                         WriteErrorDetail** error ) {

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

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

        WriteOpResult result;

        multiRemove( _txn, removeItem, &result );

        // END CURRENT OP
        incWriteStats( removeItem, result.getStats(), result.getError(), currentOp.get() );
        finishCurrentOp( _txn, _client, currentOp.get(), result.getError() );

        if ( result.getError() ) {
            result.getError()->setIndex( removeItem.getItemIndex() );
            *error = result.releaseError();
        }
    }
Esempio n. 7
0
void WriteBatchExecutor::execRemove( const BatchItemRef& removeItem,
                                     WriteErrorDetail** error ) {

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

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

    WriteOpResult result;

    while ( true ) {
        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();
    }
}
Esempio n. 8
0
void WriteBatchExecutor::execUpdate( const BatchItemRef& updateItem,
                                     BSONObj* upsertedId,
                                     WriteErrorDetail** error ) {

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

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

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

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

    if ( result.error ) {
        result.error->setIndex( updateItem.getItemIndex() );
        *error = result.releaseError();
    }
}
Esempio n. 9
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. 10
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. 11
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();
        }
    }