bool WriteBatchExecutor::applyWriteBatch(const WriteBatch& writeBatch, BSONArrayBuilder* resultsArray) { bool batchSuccess = true; for (size_t i = 0; i < writeBatch.getNumWriteItems(); ++i) { const WriteBatch::WriteItem& writeItem = writeBatch.getWriteItem(i); // All writes in the batch must be of the same type: dassert(writeBatch.getWriteType() == writeItem.getWriteType()); BSONObjBuilder results; bool opSuccess = applyWriteItem(writeBatch.getNS(), writeItem, &results); resultsArray->append(results.obj()); batchSuccess &= opSuccess; if (!opSuccess && !writeBatch.getContinueOnError()) { break; } } return batchSuccess; }
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 = verboseResponse( request ); 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.getNS(), 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.numUpdated + stats.numInserted + stats.numDeleted ); }