void clusterWrite( const BatchedCommandRequest& request, BatchedCommandResponse* response, bool autoSplit ) { // App-level validation of a create index insert if ( request.isInsertIndexRequest() ) { if ( request.sizeWriteOps() != 1 || request.isWriteConcernSet() ) { // Invalid request to create index response->setOk( false ); response->setErrCode( ErrorCodes::InvalidOptions ); response->setErrMessage( "invalid batch request for index creation" ); dassert( response->isValid( NULL ) ); return; } } // Config writes and shard writes are done differently string dbName = NamespaceString( request.getNS() ).db().toString(); if ( dbName == "config" || dbName == "admin" ) { bool verboseWC = request.isVerboseWC(); // We only support batch sizes of one and {w:0} write concern for config writes if ( request.sizeWriteOps() != 1 || ( verboseWC && request.isWriteConcernSet() ) ) { // Invalid config server write response->setOk( false ); response->setErrCode( ErrorCodes::InvalidOptions ); response->setErrMessage( "invalid batch request for config write" ); dassert( response->isValid( NULL ) ); return; } // We need to support "best-effort" writes for pings to the config server. // {w:0} (!verbose) writes are interpreted as best-effort in this case - they may still // error, but do not do the initial fsync check. configWrite( request, response, verboseWC ); } else { shardWrite( request, response, autoSplit ); } }
void WriteBatchExecutor::executeBatch( const BatchedCommandRequest& request, BatchedCommandResponse* response ) { Timer commandTimer; WriteStats stats; std::auto_ptr<BatchedErrorDetail> error( new BatchedErrorDetail ); BSONObj upsertedID = BSONObj(); bool batchSuccess = true; bool staleBatch = false; // Apply each batch item, stopping on an error if we were asked to apply the batch // sequentially. size_t numBatchOps = request.sizeWriteOps(); bool verbose = request.isVerboseWC(); for ( size_t i = 0; i < numBatchOps; i++ ) { if ( applyWriteItem( BatchItemRef( &request, i ), &stats, &upsertedID, error.get() ) ) { // In case updates turned out to be upserts, the callers may be interested // in learning what _id was used for that document. if ( !upsertedID.isEmpty() ) { if ( numBatchOps == 1 ) { response->setSingleUpserted(upsertedID); } else if ( verbose ) { std::auto_ptr<BatchedUpsertDetail> upsertDetail(new BatchedUpsertDetail); upsertDetail->setIndex(i); upsertDetail->setUpsertedID(upsertedID); response->addToUpsertDetails(upsertDetail.release()); } upsertedID = BSONObj(); } } else { // The applyWriteItem did not go thgrou // If the error is sharding related, we'll have to investigate whether we // have a stale view of sharding state. if ( error->getErrCode() == ErrorCodes::StaleShardVersion ) staleBatch = true; // Don't bother recording if the user doesn't want a verbose answer. We want to // keep the error if this is a one-item batch, since we already compact the // response for those. if (verbose || numBatchOps == 1) { error->setIndex( static_cast<int>( i ) ); response->addToErrDetails( error.release() ); } batchSuccess = false; if ( request.getOrdered() ) break; error.reset( new BatchedErrorDetail ); } } // So far, we may have failed some of the batch's items. So we record // that. Rergardless, we still need to apply the write concern. If that generates a // more specific error, we'd replace for the intermediate error here. Note that we // "compatct" the error messge if this is an one-item batch. (See rationale later in // this file.) if ( !batchSuccess ) { if (numBatchOps > 1) { // TODO // Define the final error code here. // Might be used as a final error, depending on write concern success. response->setErrCode( 99999 ); response->setErrMessage( "batch op errors occurred" ); } else { // Promote the single error. const BatchedErrorDetail* error = response->getErrDetailsAt( 0 ); response->setErrCode( error->getErrCode() ); if ( error->isErrInfoSet() ) response->setErrInfo( error->getErrInfo() ); response->setErrMessage( error->getErrMessage() ); response->unsetErrDetails(); error = NULL; } } // Apply write concern. Note, again, that we're only assembling a full response if the // user is interested in it. BSONObj writeConcern; if ( request.isWriteConcernSet() ) { writeConcern = request.getWriteConcern(); } else { writeConcern = _defaultWriteConcern; } string errMsg; BSONObjBuilder wcResultsB; if ( !waitForWriteConcern( writeConcern, !batchSuccess, &wcResultsB, &errMsg ) ) { // TODO Revisit when user visible family error codes are set response->setErrCode( ErrorCodes::WriteConcernFailed ); response->setErrMessage( errMsg ); if ( verbose ) { response->setErrInfo( wcResultsB.obj() ); } } // TODO: Audit where we want to queue here if ( staleBatch ) { ChunkVersion latestShardVersion; shardingState.refreshMetadataIfNeeded( request.getTargetingNS(), request.getShardVersion(), &latestShardVersion ); } // Set the main body of the response. We assume that, if there was an error, the error // code would already be set. response->setOk( !response->isErrCodeSet() ); response->setN( stats.numInserted + stats.numUpserted + stats.numUpdated + stats.numDeleted ); dassert( response->isValid( NULL ) ); }
void ClusterWriter::write( const BatchedCommandRequest& request, BatchedCommandResponse* response ) { const NamespaceString nss = NamespaceString( request.getNS() ); if ( !nss.isValid() ) { toBatchError( Status( ErrorCodes::InvalidNamespace, nss.ns() + " is not a valid namespace" ), response ); return; } if ( !NamespaceString::validCollectionName( nss.coll() ) ) { toBatchError( Status( ErrorCodes::BadValue, str::stream() << "invalid collection name " << nss.coll() ), response ); return; } if ( request.sizeWriteOps() > BatchedCommandRequest::kMaxWriteBatchSize ) { toBatchError( Status( ErrorCodes::FailedToParse, str::stream() << "exceeded maximum write batch size of " << BatchedCommandRequest::kMaxWriteBatchSize ), response ); return; } string errMsg; if ( request.isInsertIndexRequest() && !request.isValidIndexRequest( &errMsg ) ) { toBatchError( Status( ErrorCodes::InvalidOptions, errMsg ), response ); return; } // Config writes and shard writes are done differently string dbName = nss.db().toString(); if ( dbName == "config" || dbName == "admin" ) { bool verboseWC = request.isVerboseWC(); // We only support batch sizes of one for config writes if ( request.sizeWriteOps() != 1 ) { toBatchError( Status( ErrorCodes::InvalidOptions, mongoutils::str::stream() << "Writes to config servers must " "have batch size of 1, found " << request.sizeWriteOps() ), response ); return; } // We only support {w: 0}, {w: 1}, and {w: 'majority'} write concern for config writes if ( request.isWriteConcernSet() && !validConfigWC( request.getWriteConcern() )) { toBatchError( Status( ErrorCodes::InvalidOptions, mongoutils::str::stream() << "Invalid write concern for write" " to config servers: " << request.getWriteConcern() ), response ); return; } // We need to support "best-effort" writes for pings to the config server. // {w:0} (!verbose) writes are interpreted as best-effort in this case - they may still // error, but do not do the initial fsync check. configWrite( request, response, verboseWC ); } else { shardWrite( request, response ); } }