Status WriteOp::targetWrites(OperationContext* opCtx, const NSTargeter& targeter, std::vector<TargetedWrite*>* targetedWrites) { auto swEndpoints = [&]() -> StatusWith<std::vector<ShardEndpoint>> { if (_itemRef.getOpType() == BatchedCommandRequest::BatchType_Insert) { auto swEndpoint = targeter.targetInsert(opCtx, _itemRef.getDocument()); if (!swEndpoint.isOK()) return swEndpoint.getStatus(); return std::vector<ShardEndpoint>{std::move(swEndpoint.getValue())}; } else if (_itemRef.getOpType() == BatchedCommandRequest::BatchType_Update) { return targeter.targetUpdate(opCtx, _itemRef.getUpdate()); } else if (_itemRef.getOpType() == BatchedCommandRequest::BatchType_Delete) { return targeter.targetDelete(opCtx, _itemRef.getDelete()); } else { MONGO_UNREACHABLE; } }(); // Unless executing as part of a transaction, if we're targeting more than one endpoint with an // update/delete, we have to target everywhere since we cannot currently retry partial results. // // NOTE: Index inserts are currently specially targeted only at the current collection to avoid // creating collections everywhere. const bool inTransaction = TransactionRouter::get(opCtx) != nullptr; if (swEndpoints.isOK() && swEndpoints.getValue().size() > 1u && !inTransaction) { swEndpoints = targeter.targetAllShards(opCtx); } // If we had an error, stop here if (!swEndpoints.isOK()) return swEndpoints.getStatus(); auto& endpoints = swEndpoints.getValue(); for (auto&& endpoint : endpoints) { _childOps.emplace_back(this); WriteOpRef ref(_itemRef.getItemIndex(), _childOps.size() - 1); // Outside of a transaction, multiple endpoints currently imply no versioning, since we // can't retry half a regular multi-write. if (endpoints.size() > 1u && !inTransaction) { endpoint.shardVersion = ChunkVersion::IGNORED(); } targetedWrites->push_back(new TargetedWrite(std::move(endpoint), ref)); _childOps.back().pendingWrite = targetedWrites->back(); _childOps.back().state = WriteOpState_Pending; } _state = WriteOpState_Pending; return Status::OK(); }
Status WriteOp::targetWrites( const NSTargeter& targeter, std::vector<TargetedWrite*>* targetedWrites ) { bool isUpdate = _itemRef.getOpType() == BatchedCommandRequest::BatchType_Update; bool isDelete = _itemRef.getOpType() == BatchedCommandRequest::BatchType_Delete; // In case of error, don't leak. OwnedPointerVector<ShardEndpoint> endpointsOwned; vector<ShardEndpoint*>& endpoints = endpointsOwned.mutableVector(); if ( isUpdate || isDelete ) { // Updates/deletes targeted by query BSONObj queryDoc = isUpdate ? _itemRef.getUpdate()->getQuery() : _itemRef.getDelete()->getQuery(); Status targetStatus = targeter.targetQuery( queryDoc, &endpoints ); if ( targetStatus.isOK() ) { targetStatus = isUpdate ? updateTargetsOk( *this, endpoints ) : deleteTargetsOk( *this, endpoints ); } if ( !targetStatus.isOK() ) return targetStatus; } else { dassert( _itemRef.getOpType() == BatchedCommandRequest::BatchType_Insert ); // Inserts targeted by doc itself ShardEndpoint* endpoint = NULL; Status targetStatus = targeter.targetDoc( _itemRef.getDocument(), &endpoint ); if ( !targetStatus.isOK() ) { dassert( NULL == endpoint ); return targetStatus; } dassert( NULL != endpoint ); endpoints.push_back( endpoint ); } for ( vector<ShardEndpoint*>::iterator it = endpoints.begin(); it != endpoints.end(); ++it ) { ShardEndpoint* endpoint = *it; _childOps.push_back( new ChildWriteOp( this ) ); WriteOpRef ref( _itemRef.getItemIndex(), _childOps.size() - 1 ); // For now, multiple endpoints imply no versioning if ( endpoints.size() == 1u ) { targetedWrites->push_back( new TargetedWrite( *endpoint, ref ) ); } else { ShardEndpoint broadcastEndpoint( endpoint->shardName, ChunkVersion::IGNORED(), endpoint->shardHost ); targetedWrites->push_back( new TargetedWrite( broadcastEndpoint, ref ) ); } _childOps.back()->pendingWrite = targetedWrites->back(); _childOps.back()->state = WriteOpState_Pending; } _state = WriteOpState_Pending; return Status::OK(); }
Status WriteOp::targetWrites( const NSTargeter& targeter, std::vector<TargetedWrite*>* targetedWrites ) { bool isUpdate = _itemRef.getOpType() == BatchedCommandRequest::BatchType_Update; bool isDelete = _itemRef.getOpType() == BatchedCommandRequest::BatchType_Delete; bool isIndexInsert = _itemRef.getRequest()->isInsertIndexRequest(); Status targetStatus = Status::OK(); OwnedPointerVector<ShardEndpoint> endpointsOwned; vector<ShardEndpoint*>& endpoints = endpointsOwned.mutableVector(); if ( isUpdate ) { targetStatus = targeter.targetUpdate( *_itemRef.getUpdate(), &endpoints ); } else if ( isDelete ) { targetStatus = targeter.targetDelete( *_itemRef.getDelete(), &endpoints ); } else { dassert( _itemRef.getOpType() == BatchedCommandRequest::BatchType_Insert ); ShardEndpoint* endpoint = NULL; // TODO: Remove the index targeting stuff once there is a command for it if ( !isIndexInsert ) { targetStatus = targeter.targetInsert( _itemRef.getDocument(), &endpoint ); } else { // TODO: Retry index writes with stale version? targetStatus = targeter.targetAll( &endpoints ); } if ( !targetStatus.isOK() ) { dassert( NULL == endpoint ); return targetStatus; } // Store single endpoint result if we targeted a single endpoint if ( endpoint ) endpoints.push_back( endpoint ); } // If we had an error, stop here if ( !targetStatus.isOK() ) return targetStatus; for ( vector<ShardEndpoint*>::iterator it = endpoints.begin(); it != endpoints.end(); ++it ) { ShardEndpoint* endpoint = *it; _childOps.push_back( new ChildWriteOp( this ) ); WriteOpRef ref( _itemRef.getItemIndex(), _childOps.size() - 1 ); // For now, multiple endpoints imply no versioning if ( endpoints.size() == 1u ) { targetedWrites->push_back( new TargetedWrite( *endpoint, ref ) ); } else { ShardEndpoint broadcastEndpoint( endpoint->shardName, ChunkVersion::IGNORED() ); targetedWrites->push_back( new TargetedWrite( broadcastEndpoint, ref ) ); } _childOps.back()->pendingWrite = targetedWrites->back(); _childOps.back()->state = WriteOpState_Pending; } _state = WriteOpState_Pending; return Status::OK(); }