Status BatchWriteOp::targetBatch( const NSTargeter& targeter, bool recordTargetErrors, vector<TargetedWriteBatch*>* targetedBatches ) { // // Targeting of unordered batches is fairly simple - each remaining write op is targeted, // and each of those targeted writes are grouped into a batch for a particular shard // endpoint. // // Targeting of ordered batches is a bit more complex - to respect the ordering of the // batch, we can only send: // A) a single targeted batch to one shard endpoint // B) multiple targeted batches, but only containing targeted writes for a single write op // // This means that any multi-shard write operation must be targeted and sent one-by-one. // Subsequent single-shard write operations can be batched together if they go to the same // place. // // Ex: ShardA : { skey : a->k }, ShardB : { skey : k->z } // // Ordered insert batch of: [{ skey : a }, { skey : b }, { skey : x }] // broken into: // [{ skey : a }, { skey : b }], // [{ skey : x }] // // Ordered update Batch of : // [{ skey : a }{ $push }, // { skey : b }{ $push }, // { skey : [c, x] }{ $push }, // { skey : y }{ $push }, // { skey : z }{ $push }] // broken into: // [{ skey : a }, { skey : b }], // [{ skey : [c,x] }], // [{ skey : y }, { skey : z }] // const bool ordered = _clientRequest->getOrdered(); TargetedBatchMap batchMap; int numTargetErrors = 0; size_t numWriteOps = _clientRequest->sizeWriteOps(); for ( size_t i = 0; i < numWriteOps; ++i ) { WriteOp& writeOp = _writeOps[i]; // Only target _Ready ops if ( writeOp.getWriteState() != WriteOpState_Ready ) continue; // // Get TargetedWrites from the targeter for the write operation // // TargetedWrites need to be owned once returned OwnedPointerVector<TargetedWrite> writesOwned; vector<TargetedWrite*>& writes = writesOwned.mutableVector(); Status targetStatus = writeOp.targetWrites( targeter, &writes ); if ( !targetStatus.isOK() ) { WriteErrorDetail targetError; buildTargetError( targetStatus, &targetError ); if ( !recordTargetErrors ) { // Cancel current batch state with an error cancelBatches( targetError, _writeOps, &batchMap ); dassert( batchMap.empty() ); return targetStatus; } else if ( !ordered || batchMap.empty() ) { // Record an error for this batch writeOp.setOpError( targetError ); ++numTargetErrors; if ( ordered ) return Status::OK(); continue; } else { dassert( ordered && !batchMap.empty() ); // Send out what we have, but don't record an error yet, since there may be an // error in the writes before this point. writeOp.cancelWrites( &targetError ); break; } } // // If ordered and we have a previous endpoint, make sure we don't need to send these // targeted writes to any other endpoints. // if ( ordered && !batchMap.empty() ) { dassert( batchMap.size() == 1u ); if ( isNewBatchRequired( writes, batchMap ) ) { writeOp.cancelWrites( NULL ); break; } } // // Targeting went ok, add to appropriate TargetedBatch // for ( vector<TargetedWrite*>::iterator it = writes.begin(); it != writes.end(); ++it ) { TargetedWrite* write = *it; TargetedBatchMap::iterator seenIt = batchMap.find( &write->endpoint ); if ( seenIt == batchMap.end() ) { TargetedWriteBatch* newBatch = new TargetedWriteBatch( write->endpoint ); seenIt = batchMap.insert( make_pair( &newBatch->getEndpoint(), // newBatch ) ).first; } TargetedWriteBatch* batch = seenIt->second; batch->addWrite( write ); } // Relinquish ownership of TargetedWrites, now the TargetedBatches own them writesOwned.mutableVector().clear(); // // Break if we're ordered and we have more than one endpoint - later writes cannot be // enforced as ordered across multiple shard endpoints. // if ( ordered && batchMap.size() > 1u ) break; } // // Send back our targeted batches // for ( TargetedBatchMap::iterator it = batchMap.begin(); it != batchMap.end(); ++it ) { TargetedWriteBatch* batch = it->second; if ( batch->getWrites().empty() ) continue; // Remember targeted batch for reporting _targeted.insert( batch ); // Send the handle back to caller targetedBatches->push_back( batch ); } return Status::OK(); }
Status BatchWriteOp::targetBatch( const NSTargeter& targeter, bool recordTargetErrors, vector<TargetedWriteBatch*>* targetedBatches ) { TargetedBatchMap batchMap; size_t numWriteOps = _clientRequest->sizeWriteOps(); for ( size_t i = 0; i < numWriteOps; ++i ) { // Only do one-at-a-time ops if COE is false if ( !_clientRequest->getContinueOnError() && !batchMap.empty() ) break; WriteOp& writeOp = _writeOps[i]; // Only target _Ready ops if ( writeOp.getWriteState() != WriteOpState_Ready ) continue; // // Get TargetedWrites from the targeter for the write operation // // TargetedWrites need to be owned once returned OwnedPointerVector<TargetedWrite> writesOwned; vector<TargetedWrite*>& writes = writesOwned.mutableVector(); Status targetStatus = writeOp.targetWrites( targeter, &writes ); if ( !targetStatus.isOK() ) { // // We're not sure how to target here, so either record the error or cancel the // current batches. // BatchedErrorDetail targetError; buildTargetError( targetStatus, &targetError ); if ( recordTargetErrors ) { writeOp.setOpError( targetError ); continue; } else { // Cancel current batch state with an error cancelBatches( targetError, _writeOps, &batchMap ); dassert( batchMap.empty() ); return targetStatus; } } // // Targeting went ok, add to appropriate TargetedBatch // for ( vector<TargetedWrite*>::iterator it = writes.begin(); it != writes.end(); ++it ) { TargetedWrite* write = *it; TargetedBatchMap::iterator seenIt = batchMap.find( &write->endpoint ); if ( seenIt == batchMap.end() ) { TargetedWriteBatch* newBatch = new TargetedWriteBatch( write->endpoint ); seenIt = batchMap.insert( make_pair( &newBatch->getEndpoint(), // newBatch ) ).first; } TargetedWriteBatch* batch = seenIt->second; batch->addWrite( write ); } // Relinquish ownership of TargetedWrites, now the TargetedBatches own them writesOwned.mutableVector().clear(); } // // Send back our targeted batches // for ( TargetedBatchMap::iterator it = batchMap.begin(); it != batchMap.end(); ++it ) { TargetedWriteBatch* batch = it->second; // Remember targeted batch for reporting _targeted.insert( batch ); // Send the handle back to caller targetedBatches->push_back( batch ); } return Status::OK(); }