void BatchSafeWriter::safeWriteBatch( DBClientBase* conn, const BatchedCommandRequest& request, BatchedCommandResponse* response ) { // N starts at zero, and we add to it for each item response->setN( 0 ); for ( size_t i = 0; i < request.sizeWriteOps(); ++i ) { BatchItemRef itemRef( &request, static_cast<int>( i ) ); LastError lastError; _safeWriter->safeWrite( conn, itemRef, &lastError ); // Register the error if we need to BatchedErrorDetail* batchError = lastErrorToBatchError( lastError ); if ( batchError ) { batchError->setIndex( i ); response->addToErrDetails( batchError ); } response->setN( response->getN() + lastError.nObjects ); if ( !lastError.upsertedId.isEmpty() ) { BatchedUpsertDetail* upsertedId = new BatchedUpsertDetail; upsertedId->setIndex( i ); upsertedId->setUpsertedID( lastError.upsertedId ); response->addToUpsertDetails( upsertedId ); } // Break on first error if we're ordered if ( request.getOrdered() && BatchSafeWriter::isFailedOp( lastError ) ) break; } if ( request.sizeWriteOps() == 1 && response->isErrDetailsSet() && !response->isErrCodeSet() ) { // Promote single error to batch error const BatchedErrorDetail* error = response->getErrDetailsAt( 0 ); response->setErrCode( error->getErrCode() ); if ( error->isErrInfoSet() ) response->setErrInfo( error->getErrInfo() ); response->setErrMessage( error->getErrMessage() ); response->unsetErrDetails(); } if ( request.sizeWriteOps() == 1 && response->isUpsertDetailsSet() ) { // Promote single upsert to batch upsert const BatchedUpsertDetail* upsertedId = response->getUpsertDetailsAt( 0 ); response->setSingleUpserted( upsertedId->getUpsertedID() ); response->unsetUpsertDetails(); } response->setOk( !response->isErrCodeSet() ); dassert( response->isValid( NULL ) ); }
BatchedErrorDetail* BatchSafeWriter::lastErrorToBatchError( const LastError& lastError ) { if ( BatchSafeWriter::isFailedOp( lastError ) ) { BatchedErrorDetail* batchError = new BatchedErrorDetail; if ( lastError.code != 0 ) batchError->setErrCode( lastError.code ); else batchError->setErrCode( ErrorCodes::UnknownError ); batchError->setErrMessage( lastError.msg ); return batchError; } return NULL; }
void WriteOp::setOpError( const BatchedErrorDetail& error ) { dassert( _state == WriteOpState_Ready ); _error.reset( new BatchedErrorDetail ); error.cloneTo( _error.get() ); _state = WriteOpState_Error; // No need to updateOpState, set directly }
void BatchSafeWriter::safeWriteBatch( DBClientBase* conn, const BatchedCommandRequest& request, BatchedCommandResponse* response ) { for ( size_t i = 0; i < request.sizeWriteOps(); ++i ) { BatchItemRef itemRef( &request, static_cast<int>( i ) ); LastError lastError; _safeWriter->safeWrite( conn, itemRef, &lastError ); // Register the error if we need to BatchedErrorDetail* batchError = lastErrorToBatchError( lastError ); batchError->setIndex( i ); response->addToErrDetails( batchError ); // TODO: Other stats, etc. // Break on first error if we're ordered if ( request.getOrdered() && BatchSafeWriter::isFailedOp( lastError ) ) break; } }
void WriteOp::noteWriteError( const TargetedWrite& targetedWrite, const BatchedErrorDetail& error ) { const WriteOpRef& ref = targetedWrite.writeOpRef; ChildWriteOp& childOp = *_childOps.at( ref.second ); childOp.pendingWrite = NULL; childOp.endpoint.reset( new ShardEndpoint( targetedWrite.endpoint ) ); childOp.error.reset( new BatchedErrorDetail ); error.cloneTo( childOp.error.get() ); childOp.state = WriteOpState_Error; updateOpState(); }
BatchedErrorDetail* BatchSafeWriter::lastErrorToBatchError( const LastError& lastError ) { bool isFailedOp = lastError.msg != ""; bool isStaleOp = lastError.writebackId.isSet(); dassert( !( isFailedOp && isStaleOp ) ); if ( isFailedOp ) { BatchedErrorDetail* batchError = new BatchedErrorDetail; if ( lastError.code != 0 ) batchError->setErrCode( lastError.code ); else batchError->setErrCode( ErrorCodes::UnknownError ); batchError->setErrMessage( lastError.msg ); return batchError; } else if ( isStaleOp ) { BatchedErrorDetail* batchError = new BatchedErrorDetail; batchError->setErrCode( ErrorCodes::StaleShardVersion ); batchError->setErrInfo( BSON( "downconvert" << true ) ); // For debugging batchError->setErrMessage( "shard version was stale" ); return batchError; } return NULL; }
void batchErrorToLastError( const BatchedCommandRequest& request, const BatchedCommandResponse& response, LastError* error ) { scoped_ptr<BatchedErrorDetail> topLevelError; BatchedErrorDetail* lastBatchError = NULL; if ( !response.getOk() ) { int code = response.getErrCode(); // Check for batch error // We don't care about write concern errors, these happen in legacy mode in GLE if ( code != ErrorCodes::WriteConcernFailed && !response.isErrDetailsSet() ) { // Top-level error, all writes failed topLevelError.reset( new BatchedErrorDetail ); buildErrorFromResponse( response, topLevelError.get() ); lastBatchError = topLevelError.get(); } else if ( response.isErrDetailsSet() ) { // The last error in the batch is always reported - this matches expected COE // semantics for insert batches and works for single writes lastBatchError = response.getErrDetails().back(); } } // Record an error if one exists if ( lastBatchError ) { error->raiseError( lastBatchError->getErrCode(), lastBatchError->getErrMessage().c_str() ); return; } // Record write stats otherwise // NOTE: For multi-write batches, our semantics change a little because we don't have // un-aggregated "n" stats. if ( request.getBatchType() == BatchedCommandRequest::BatchType_Update ) { BSONObj upsertedId; if ( response.isSingleUpsertedSet() ) upsertedId = response.getSingleUpserted(); else if( response.isUpsertDetailsSet() ) { // Only report the very last item's upserted id if applicable if ( response.getUpsertDetails().back()->getIndex() + 1 == static_cast<int>( request.sizeWriteOps() ) ) { upsertedId = response.getUpsertDetails().back()->getUpsertedID(); } } int numUpserted = 0; if ( response.isSingleUpsertedSet() ) ++numUpserted; else if ( response.isUpsertDetailsSet() ) numUpserted += response.sizeUpsertDetails(); int numUpdated = response.getN() - numUpserted; dassert( numUpdated >= 0 ); error->recordUpdate( numUpdated > 0, response.getN(), upsertedId ); } else if ( request.getBatchType() == BatchedCommandRequest::BatchType_Delete ) { error->recordDelete( response.getN() ); } }
static void cloneBatchErrorFrom( const BatchedErrorDetail& details, BatchedCommandResponse* response ) { response->setErrCode( details.getErrCode() ); if ( details.isErrInfoSet() ) response->setErrInfo( details.getErrInfo() ); response->setErrMessage( details.getErrMessage() ); }