Ejemplo n.º 1
0
 BSONObj generateSection(const BSONElement& configElement) const {
     if ( ! anyReplEnabled() )
         return BSONObj();
     
     int level = configElement.numberInt();
     
     BSONObjBuilder result;
     appendReplicationInfo( result, level );
     return result.obj();
 }
Ejemplo n.º 2
0
void WriteBatchExecutor::executeBatch( const BatchedCommandRequest& request,
                                       BatchedCommandResponse* response ) {

    // Validate namespace
    const NamespaceString nss = NamespaceString( request.getNS() );
    if ( !nss.isValid() ) {
        toBatchError( Status( ErrorCodes::InvalidNamespace,
                              nss.ns() + " is not a valid namespace" ),
                      response );
        return;
    }

    // Make sure we can write to the namespace
    Status allowedStatus = userAllowedWriteNS( nss );
    if ( !allowedStatus.isOK() ) {
        toBatchError( allowedStatus, response );
        return;
    }

    // Validate insert index requests
    // TODO: Push insert index requests through createIndex once all upgrade paths support it
    string errMsg;
    if ( request.isInsertIndexRequest() && !request.isValidIndexRequest( &errMsg ) ) {
        toBatchError( Status( ErrorCodes::InvalidOptions, errMsg ), response );
        return;
    }

    // Validate write concern
    // TODO: Lift write concern parsing out of this entirely
    WriteConcernOptions writeConcern;

    BSONObj wcDoc;
    if ( request.isWriteConcernSet() ) {
        wcDoc = request.getWriteConcern();
    }

    Status wcStatus = Status::OK();
    if ( wcDoc.isEmpty() ) {

        // The default write concern if empty is w : 1
        // Specifying w : 0 is/was allowed, but is interpreted identically to w : 1

        wcStatus = writeConcern.parse(
                       _defaultWriteConcern.isEmpty() ?
                       WriteConcernOptions::Acknowledged : _defaultWriteConcern );

        if ( writeConcern.wNumNodes == 0 && writeConcern.wMode.empty() ) {
            writeConcern.wNumNodes = 1;
        }
    }
    else {
        wcStatus = writeConcern.parse( wcDoc );
    }

    if ( wcStatus.isOK() ) {
        wcStatus = validateWriteConcern( writeConcern );
    }

    if ( !wcStatus.isOK() ) {
        toBatchError( wcStatus, response );
        return;
    }

    if ( request.sizeWriteOps() == 0u ) {
        toBatchError( Status( ErrorCodes::InvalidLength,
                              "no write ops were included in the batch" ),
                      response );
        return;
    }

    // Validate batch size
    if ( request.sizeWriteOps() > BatchedCommandRequest::kMaxWriteBatchSize ) {
        toBatchError( Status( ErrorCodes::InvalidLength,
                              stream() << "exceeded maximum write batch size of "
                              << BatchedCommandRequest::kMaxWriteBatchSize ),
                      response );
        return;
    }

    //
    // End validation
    //

    bool silentWC = writeConcern.wMode.empty() && writeConcern.wNumNodes == 0
                    && writeConcern.syncMode == WriteConcernOptions::NONE;

    Timer commandTimer;

    OwnedPointerVector<WriteErrorDetail> writeErrorsOwned;
    vector<WriteErrorDetail*>& writeErrors = writeErrorsOwned.mutableVector();

    OwnedPointerVector<BatchedUpsertDetail> upsertedOwned;
    vector<BatchedUpsertDetail*>& upserted = upsertedOwned.mutableVector();

    //
    // Apply each batch item, possibly bulking some items together in the write lock.
    // Stops on error if batch is ordered.
    //

    bulkExecute( request, &upserted, &writeErrors );

    //
    // Try to enforce the write concern if everything succeeded (unordered or ordered)
    // OR if something succeeded and we're unordered.
    //

    auto_ptr<WCErrorDetail> wcError;
    bool needToEnforceWC = writeErrors.empty()
                           || ( !request.getOrdered()
                                && writeErrors.size() < request.sizeWriteOps() );

    if ( needToEnforceWC ) {

        _client->curop()->setMessage( "waiting for write concern" );

        WriteConcernResult res;
        Status status = waitForWriteConcern( writeConcern, _client->getLastOp(), &res );

        if ( !status.isOK() ) {
            wcError.reset( toWriteConcernError( status, res ) );
        }
    }

    //
    // Refresh metadata if needed
    //

    bool staleBatch = !writeErrors.empty()
                      && writeErrors.back()->getErrCode() == ErrorCodes::StaleShardVersion;

    if ( staleBatch ) {

        const BatchedRequestMetadata* requestMetadata = request.getMetadata();
        dassert( requestMetadata );

        // Make sure our shard name is set or is the same as what was set previously
        if ( shardingState.setShardName( requestMetadata->getShardName() ) ) {

            //
            // First, we refresh metadata if we need to based on the requested version.
            //

            ChunkVersion latestShardVersion;
            shardingState.refreshMetadataIfNeeded( request.getTargetingNS(),
                                                   requestMetadata->getShardVersion(),
                                                   &latestShardVersion );

            // Report if we're still changing our metadata
            // TODO: Better reporting per-collection
            if ( shardingState.inCriticalMigrateSection() ) {
                noteInCriticalSection( writeErrors.back() );
            }

            if ( queueForMigrationCommit ) {

                //
                // Queue up for migration to end - this allows us to be sure that clients will
                // not repeatedly try to refresh metadata that is not yet written to the config
                // server.  Not necessary for correctness.
                // Exposed as optional parameter to allow testing of queuing behavior with
                // different network timings.
                //

                const ChunkVersion& requestShardVersion = requestMetadata->getShardVersion();

                //
                // Only wait if we're an older version (in the current collection epoch) and
                // we're not write compatible, implying that the current migration is affecting
                // writes.
                //

                if ( requestShardVersion.isOlderThan( latestShardVersion ) &&
                        !requestShardVersion.isWriteCompatibleWith( latestShardVersion ) ) {

                    while ( shardingState.inCriticalMigrateSection() ) {

                        log() << "write request to old shard version "
                              << requestMetadata->getShardVersion().toString()
                              << " waiting for migration commit" << endl;

                        shardingState.waitTillNotInCriticalSection( 10 /* secs */);
                    }
                }
            }
        }
        else {
            // If our shard name is stale, our version must have been stale as well
            dassert( writeErrors.size() == request.sizeWriteOps() );
        }
    }

    //
    // Construct response
    //

    response->setOk( true );

    if ( !silentWC ) {

        if ( upserted.size() ) {
            response->setUpsertDetails( upserted );
            upserted.clear();
        }

        if ( writeErrors.size() ) {
            response->setErrDetails( writeErrors );
            writeErrors.clear();
        }

        if ( wcError.get() ) {
            response->setWriteConcernError( wcError.release() );
        }

        if ( anyReplEnabled() ) {
            response->setLastOp( _client->getLastOp() );
            if (theReplSet) {
                response->setElectionId( theReplSet->getElectionId() );
            }
        }

        // Set the stats for the response
        response->setN( _stats->numInserted + _stats->numUpserted + _stats->numMatched
                        + _stats->numDeleted );
        if ( request.getBatchType() == BatchedCommandRequest::BatchType_Update )
            response->setNModified( _stats->numModified );
    }

    dassert( response->isValid( NULL ) );
}