void BatchWriteOp::noteBatchResponse( const TargetedWriteBatch& targetedBatch, const BatchedCommandResponse& response, TrackedErrors* trackedErrors ) { if ( !response.getOk() ) { WriteErrorDetail error; cloneCommandErrorTo( response, &error ); // Treat command errors exactly like other failures of the batch // Note that no errors will be tracked from these failures - as-designed noteBatchError( targetedBatch, error ); return; } dassert( response.getOk() ); // Stop tracking targeted batch _targeted.erase( &targetedBatch ); // Increment stats for this batch incBatchStats( _clientRequest->getBatchType(), response, _stats.get() ); // // Assign errors to particular items. // Write Concern errors are stored and handled later. // // Special handling for write concern errors, save for later if ( response.isWriteConcernErrorSet() ) { auto_ptr<ShardWCError> wcError( new ShardWCError( targetedBatch.getEndpoint(), *response.getWriteConcernError() )); _wcErrors.mutableVector().push_back( wcError.release() ); } vector<WriteErrorDetail*> itemErrors; // Handle batch and per-item errors if ( response.isErrDetailsSet() ) { // Per-item errors were set itemErrors.insert( itemErrors.begin(), response.getErrDetails().begin(), response.getErrDetails().end() ); // Sort per-item errors by index std::sort( itemErrors.begin(), itemErrors.end(), WriteErrorDetailComp() ); } // // Go through all pending responses of the op and sorted remote reponses, populate errors // This will either set all errors to the batch error or apply per-item errors as-needed // // If the batch is ordered, cancel all writes after the first error for retargeting. // bool ordered = _clientRequest->getOrdered(); vector<WriteErrorDetail*>::iterator itemErrorIt = itemErrors.begin(); int index = 0; WriteErrorDetail* lastError = NULL; for ( vector<TargetedWrite*>::const_iterator it = targetedBatch.getWrites().begin(); it != targetedBatch.getWrites().end(); ++it, ++index ) { const TargetedWrite* write = *it; WriteOp& writeOp = _writeOps[write->writeOpRef.first]; dassert( writeOp.getWriteState() == WriteOpState_Pending ); // See if we have an error for the write WriteErrorDetail* writeError = NULL; if ( itemErrorIt != itemErrors.end() && ( *itemErrorIt )->getIndex() == index ) { // We have an per-item error for this write op's index writeError = *itemErrorIt; ++itemErrorIt; } // Finish the response (with error, if needed) if ( NULL == writeError ) { if ( !ordered || !lastError ){ writeOp.noteWriteComplete( *write ); } else { // We didn't actually apply this write - cancel so we can retarget dassert( writeOp.getNumTargeted() == 1u ); writeOp.cancelWrites( lastError ); } } else { writeOp.noteWriteError( *write, *writeError ); lastError = writeError; } } // Track errors we care about, whether batch or individual errors if ( NULL != trackedErrors ) { trackErrors( targetedBatch.getEndpoint(), itemErrors, trackedErrors ); } // Track upserted ids if we need to if ( response.isUpsertDetailsSet() ) { const vector<BatchedUpsertDetail*>& upsertedIds = response.getUpsertDetails(); for ( vector<BatchedUpsertDetail*>::const_iterator it = upsertedIds.begin(); it != upsertedIds.end(); ++it ) { // The child upserted details don't have the correct index for the full batch const BatchedUpsertDetail* childUpsertedId = *it; // Work backward from the child batch item index to the batch item index int childBatchIndex = childUpsertedId->getIndex(); int batchIndex = targetedBatch.getWrites()[childBatchIndex]->writeOpRef.first; // Push the upserted id with the correct index into the batch upserted ids BatchedUpsertDetail* upsertedId = new BatchedUpsertDetail; upsertedId->setIndex( batchIndex ); upsertedId->setUpsertedID( childUpsertedId->getUpsertedID() ); _upsertedIds.mutableVector().push_back( upsertedId ); } } }
void BatchWriteOp::noteBatchResponse( const TargetedWriteBatch& targetedBatch, const BatchedCommandResponse& response, TrackedErrors* trackedErrors ) { // // Organize errors based on error code. // We may have *either* a batch error or errors per-item. // (Write Concern errors are stored and handled later.) // vector<BatchedErrorDetail*> itemErrors; scoped_ptr<BatchedErrorDetail> batchError; if ( !response.getOk() ) { int errCode = response.getErrCode(); bool isWCError = isWCErrCode( errCode ); // Special handling for write concern errors, save for later if ( isWCError ) { BatchedErrorDetail error; cloneBatchErrorTo( response, &error ); ShardError* wcError = new ShardError( targetedBatch.getEndpoint(), error ); _wcErrors.mutableVector().push_back( wcError ); } // Handle batch and per-item errors if ( response.isErrDetailsSet() ) { // Per-item errors were set itemErrors.insert( itemErrors.begin(), response.getErrDetails().begin(), response.getErrDetails().end() ); // Sort per-item errors by index std::sort( itemErrors.begin(), itemErrors.end(), BatchedErrorDetailComp() ); } else if ( !isWCError ) { // Per-item errors were not set and this error is not a WC error // => this is a full-batch error batchError.reset( new BatchedErrorDetail ); cloneBatchErrorTo( response, batchError.get() ); } } // We can't have both a batch error and per-item errors dassert( !( batchError && !itemErrors.empty() ) ); // // Go through all pending responses of the op and sorted remote reponses, populate errors // This will either set all errors to the batch error or apply per-item errors as-needed // vector<BatchedErrorDetail*>::iterator itemErrorIt = itemErrors.begin(); int index = 0; for ( vector<TargetedWrite*>::const_iterator it = targetedBatch.getWrites().begin(); it != targetedBatch.getWrites().end(); ++it, ++index ) { const TargetedWrite* write = *it; WriteOp& writeOp = _writeOps[write->writeOpRef.first]; dassert( writeOp.getWriteState() == WriteOpState_Pending ); // See if we have an error for the write BatchedErrorDetail* writeError = NULL; if ( batchError ) { // Default to batch error, if it exists writeError = batchError.get(); } else if ( itemErrorIt != itemErrors.end() && ( *itemErrorIt )->getIndex() == index ) { // We have an per-item error for this write op's index writeError = *itemErrorIt; ++itemErrorIt; } // Finish the response (with error, if needed) if ( NULL == writeError ) { writeOp.noteWriteComplete( *write ); } else { writeOp.noteWriteError( *write, *writeError ); } } // Track errors we care about, whether batch or individual errors if ( NULL != trackedErrors ) { trackErrors( targetedBatch.getEndpoint(), batchError.get(), itemErrors, trackedErrors ); } // Stop tracking targeted batch _targeted.erase( &targetedBatch ); }