static void multiUpdate( OperationContext* txn, const BatchItemRef& updateItem, WriteOpResult* result ) { const NamespaceString nsString(updateItem.getRequest()->getNS()); UpdateRequest request(nsString); request.setQuery(updateItem.getUpdate()->getQuery()); request.setUpdates(updateItem.getUpdate()->getUpdateExpr()); request.setMulti(updateItem.getUpdate()->getMulti()); request.setUpsert(updateItem.getUpdate()->getUpsert()); request.setUpdateOpLog(true); UpdateLifecycleImpl updateLifecycle(true, request.getNamespaceString()); request.setLifecycle(&updateLifecycle); UpdateExecutor executor(&request, &txn->getCurOp()->debug()); Status status = executor.prepare(); if (!status.isOK()) { result->setError(toWriteError(status)); return; } /////////////////////////////////////////// Lock::DBWrite writeLock(txn->lockState(), nsString.ns()); /////////////////////////////////////////// if (!checkShardVersion(txn, &shardingState, *updateItem.getRequest(), result)) return; Client::Context ctx( nsString.ns(), storageGlobalParams.dbpath, false /* don't check version */ ); try { UpdateResult res = executor.execute(txn, ctx.db()); const long long numDocsModified = res.numDocsModified; const long long numMatched = res.numMatched; const BSONObj resUpsertedID = res.upserted; // We have an _id from an insert const bool didInsert = !resUpsertedID.isEmpty(); result->getStats().nModified = didInsert ? 0 : numDocsModified; result->getStats().n = didInsert ? 1 : numMatched; result->getStats().upsertedID = resUpsertedID; } catch (const DBException& ex) { status = ex.toStatus(); if (ErrorCodes::isInterruption(status.code())) { throw; } result->setError(toWriteError(status)); } }
/** * Perform an update operation, which might update multiple documents in the lock. Dispatches * to update code currently to do most of this. * * Might error, otherwise populates the result. */ static void multiUpdate( const BatchItemRef& updateItem, WriteOpResult* result ) { Lock::assertWriteLocked( updateItem.getRequest()->getNS() ); BSONObj queryObj = updateItem.getUpdate()->getQuery(); BSONObj updateObj = updateItem.getUpdate()->getUpdateExpr(); bool multi = updateItem.getUpdate()->getMulti(); bool upsert = updateItem.getUpdate()->getUpsert(); bool didInsert = false; long long numMatched = 0; long long numDocsModified = 0; BSONObj resUpsertedID; try { const NamespaceString requestNs( updateItem.getRequest()->getNS() ); UpdateRequest request( requestNs ); request.setQuery( queryObj ); request.setUpdates( updateObj ); request.setUpsert( upsert ); request.setMulti( multi ); request.setUpdateOpLog(); // TODO(greg) We need to send if we are ignoring the shard version below, // but for now yes UpdateLifecycleImpl updateLifecycle( true, requestNs ); request.setLifecycle( &updateLifecycle ); UpdateResult res = update( request, &cc().curop()->debug() ); numDocsModified = res.numDocsModified; numMatched = res.numMatched; resUpsertedID = res.upserted; // We have an _id from an insert didInsert = !resUpsertedID.isEmpty(); result->stats.nModified = didInsert ? 0 : numDocsModified; result->stats.n = didInsert ? 1 : numMatched; result->stats.upsertedID = resUpsertedID; } catch ( const DBException& ex ) { result->error = toWriteError( ex.toStatus() ); } }
Status Strategy::commandOpWrite(const std::string& dbName, const BSONObj& command, BatchItemRef targetingBatchItem, std::vector<CommandResult>* results) { // Note that this implementation will not handle targeting retries and does not completely // emulate write behavior ChunkManagerTargeter targeter(NamespaceString( targetingBatchItem.getRequest()->getTargetingNS())); Status status = targeter.init(); if (!status.isOK()) return status; OwnedPointerVector<ShardEndpoint> endpointsOwned; vector<ShardEndpoint*>& endpoints = endpointsOwned.mutableVector(); if (targetingBatchItem.getOpType() == BatchedCommandRequest::BatchType_Insert) { ShardEndpoint* endpoint; Status status = targeter.targetInsert(targetingBatchItem.getDocument(), &endpoint); if (!status.isOK()) return status; endpoints.push_back(endpoint); } else if (targetingBatchItem.getOpType() == BatchedCommandRequest::BatchType_Update) { Status status = targeter.targetUpdate(*targetingBatchItem.getUpdate(), &endpoints); if (!status.isOK()) return status; } else { invariant(targetingBatchItem.getOpType() == BatchedCommandRequest::BatchType_Delete); Status status = targeter.targetDelete(*targetingBatchItem.getDelete(), &endpoints); if (!status.isOK()) return status; } DBClientShardResolver resolver; DBClientMultiCommand dispatcher; // Assemble requests for (vector<ShardEndpoint*>::const_iterator it = endpoints.begin(); it != endpoints.end(); ++it) { const ShardEndpoint* endpoint = *it; ConnectionString host; Status status = resolver.chooseWriteHost(endpoint->shardName, &host); if (!status.isOK()) return status; RawBSONSerializable request(command); dispatcher.addCommand(host, dbName, request); } // Errors reported when recv'ing responses dispatcher.sendAll(); Status dispatchStatus = Status::OK(); // Recv responses while (dispatcher.numPending() > 0) { ConnectionString host; RawBSONSerializable response; Status status = dispatcher.recvAny(&host, &response); if (!status.isOK()) { // We always need to recv() all the sent operations dispatchStatus = status; continue; } CommandResult result; result.target = host; result.shardTarget = Shard::make(host.toString()); result.result = response.toBSON(); results->push_back(result); } return dispatchStatus; }