void ShardingTestFixture::expectInserts(const NamespaceString& nss, const std::vector<BSONObj>& expected) { onCommand([&nss, &expected](const RemoteCommandRequest& request) { ASSERT_EQUALS(nss.db(), request.dbname); BatchedInsertRequest actualBatchedInsert; std::string errmsg; ASSERT_TRUE(actualBatchedInsert.parseBSON(request.dbname, request.cmdObj, &errmsg)); ASSERT_EQUALS(nss.toString(), actualBatchedInsert.getNS().toString()); auto inserted = actualBatchedInsert.getDocuments(); ASSERT_EQUALS(expected.size(), inserted.size()); auto itInserted = inserted.begin(); auto itExpected = expected.begin(); for (; itInserted != inserted.end(); itInserted++, itExpected++) { ASSERT_EQ(*itExpected, *itInserted); } BatchedCommandResponse response; response.setOk(true); return response.toBSON(); }); }
bool WriteCmd::run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, int options, string& errMsg, BSONObjBuilder& result, bool fromRepl) { // Can't be run on secondaries (logTheOp() == false, slaveOk() == false). dassert(!fromRepl); BatchedCommandRequest request(_writeType); BatchedCommandResponse response; if (!request.parseBSON(cmdObj, &errMsg) || !request.isValid(&errMsg)) { return appendCommandStatus(result, Status(ErrorCodes::FailedToParse, errMsg)); } // Note that this is a runCommmand, and therefore, the database and the collection name // are in different parts of the grammar for the command. But it's more convenient to // work with a NamespaceString. We built it here and replace it in the parsed command. // Internally, everything work with the namespace string as opposed to just the // collection name. NamespaceString nss(dbName, request.getNS()); request.setNSS(nss); WriteConcernOptions defaultWriteConcern = repl::getGlobalReplicationCoordinator()->getGetLastErrorDefault(); WriteBatchExecutor writeBatchExecutor( txn, defaultWriteConcern, &globalOpCounters, lastError.get()); writeBatchExecutor.executeBatch(request, &response); result.appendElements(response.toBSON()); return response.getOk(); }
bool WriteCmd::run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, int options, string& errMsg, BSONObjBuilder& result) { // Can't be run on secondaries. dassert(txn->writesAreReplicated()); BatchedCommandRequest request(_writeType); BatchedCommandResponse response; if (!request.parseBSON(dbName, cmdObj, &errMsg) || !request.isValid(&errMsg)) { return appendCommandStatus(result, Status(ErrorCodes::FailedToParse, errMsg)); } StatusWith<WriteConcernOptions> wcStatus = extractWriteConcern(cmdObj, dbName); if (!wcStatus.isOK()) { return appendCommandStatus(result, wcStatus.getStatus()); } txn->setWriteConcern(wcStatus.getValue()); WriteBatchExecutor writeBatchExecutor( txn, &globalOpCounters, &LastError::get(txn->getClient())); writeBatchExecutor.executeBatch(request, &response); result.appendElements(response.toBSON()); return response.getOk(); }
void ShardingTestFixture::expectUpdateCollection(const HostAndPort& expectedHost, const CollectionType& coll) { onCommand([&](const RemoteCommandRequest& request) { ASSERT_EQUALS(expectedHost, request.target); ASSERT_EQUALS(BSON(rpc::kReplSetMetadataFieldName << 1), request.metadata); ASSERT_EQUALS("config", request.dbname); BatchedUpdateRequest actualBatchedUpdate; std::string errmsg; ASSERT_TRUE(actualBatchedUpdate.parseBSON(request.dbname, request.cmdObj, &errmsg)); ASSERT_EQUALS(CollectionType::ConfigNS, actualBatchedUpdate.getNS().ns()); auto updates = actualBatchedUpdate.getUpdates(); ASSERT_EQUALS(1U, updates.size()); auto update = updates.front(); ASSERT_TRUE(update->getUpsert()); ASSERT_FALSE(update->getMulti()); ASSERT_EQUALS(update->getQuery(), BSON(CollectionType::fullNs(coll.getNs().toString()))); ASSERT_EQUALS(update->getUpdateExpr(), coll.toBSON()); BatchedCommandResponse response; response.setOk(true); response.setNModified(1); return response.toBSON(); }); }
void BatchWriteOp::noteBatchError( const TargetedWriteBatch& targetedBatch, const BatchedErrorDetail& error ) { BatchedCommandResponse response; response.setOk( false ); cloneBatchErrorFrom( error, &response ); noteBatchResponse( targetedBatch, response, NULL ); }
void ForwardingCatalogManager::writeConfigServerDirect(const BatchedCommandRequest& request, BatchedCommandResponse* response) { retry([&] { BatchedCommandResponse theResponse; _actual->writeConfigServerDirect(request, &theResponse); theResponse.cloneTo(response); }); }
void ForwardingCatalogManager::writeConfigServerDirect(OperationContext* txn, const BatchedCommandRequest& request, BatchedCommandResponse* response) { retry([&] { BatchedCommandResponse theResponse; _actual->writeConfigServerDirect(txn, request, &theResponse); theResponse.cloneTo(response); return 1; }); }
void ShardingTestFixture::expectConfigCollectionInsert(const HostAndPort& configHost, StringData collName, Date_t timestamp, const std::string& what, const std::string& ns, const BSONObj& detail) { onCommand([&](const RemoteCommandRequest& request) { ASSERT_EQUALS(configHost, request.target); ASSERT_EQUALS("config", request.dbname); BatchedInsertRequest actualBatchedInsert; std::string errmsg; ASSERT_TRUE(actualBatchedInsert.parseBSON(request.dbname, request.cmdObj, &errmsg)); ASSERT_EQ("config", actualBatchedInsert.getNS().db()); ASSERT_EQ(collName, actualBatchedInsert.getNS().coll()); auto inserts = actualBatchedInsert.getDocuments(); ASSERT_EQUALS(1U, inserts.size()); const ChangeLogType& actualChangeLog = assertGet(ChangeLogType::fromBSON(inserts.front())); ASSERT_EQUALS(operationContext()->getClient()->clientAddress(true), actualChangeLog.getClientAddr()); ASSERT_EQUALS(detail, actualChangeLog.getDetails()); ASSERT_EQUALS(ns, actualChangeLog.getNS()); ASSERT_EQUALS(network()->getHostName(), actualChangeLog.getServer()); ASSERT_EQUALS(timestamp, actualChangeLog.getTime()); ASSERT_EQUALS(what, actualChangeLog.getWhat()); // Handle changeId specially because there's no way to know what OID was generated std::string changeId = actualChangeLog.getChangeId(); size_t firstDash = changeId.find("-"); size_t lastDash = changeId.rfind("-"); const std::string serverPiece = changeId.substr(0, firstDash); const std::string timePiece = changeId.substr(firstDash + 1, lastDash - firstDash - 1); const std::string oidPiece = changeId.substr(lastDash + 1); ASSERT_EQUALS(grid.getNetwork()->getHostName(), serverPiece); ASSERT_EQUALS(timestamp.toString(), timePiece); OID generatedOID; // Just make sure this doesn't throws and assume the OID is valid generatedOID.init(oidPiece); BatchedCommandResponse response; response.setOk(true); return response.toBSON(); }); }
Status AuthzManagerExternalStateMongos::remove( const NamespaceString& collectionName, const BSONObj& query, const BSONObj& writeConcern, int* numRemoved) { BatchedCommandResponse response; Status res = clusterDelete(collectionName, query, 0 /* limit */, writeConcern, &response); if (res.isOK()) { *numRemoved = response.getN(); } return res; }
bool WriteCmd::run(const string& dbName, BSONObj& cmdObj, int options, string& errMsg, BSONObjBuilder& result, bool fromRepl) { // Can't be run on secondaries (logTheOp() == false, slaveOk() == false). dassert( !fromRepl ); BatchedCommandRequest request( _writeType ); BatchedCommandResponse response; if ( !request.parseBSON( cmdObj, &errMsg ) || !request.isValid( &errMsg ) ) { return appendCommandStatus( result, Status( ErrorCodes::FailedToParse, errMsg ) ); } // Note that this is a runCommmand, and therefore, the database and the collection name // are in different parts of the grammar for the command. But it's more convenient to // work with a NamespaceString. We built it here and replace it in the parsed command. // Internally, everything work with the namespace string as opposed to just the // collection name. NamespaceString nss(dbName, request.getNS()); request.setNS(nss.ns()); Status status = userAllowedWriteNS( nss ); if ( !status.isOK() ) return appendCommandStatus( result, status ); BSONObj defaultWriteConcern; // This is really bad - it's only safe because we leak the defaults by overriding them with // new defaults and because we never reset to an empty default. // TODO: fix this for sane behavior where we query repl set object if ( getLastErrorDefault ) defaultWriteConcern = *getLastErrorDefault; if ( defaultWriteConcern.isEmpty() ) { BSONObjBuilder b; b.append( "w", 1 ); defaultWriteConcern = b.obj(); } WriteBatchExecutor writeBatchExecutor(defaultWriteConcern, &cc(), &globalOpCounters, lastError.get()); writeBatchExecutor.executeBatch( request, &response ); result.appendElements( response.toBSON() ); return response.getOk(); }
void Strategy::writeOp(OperationContext* txn, int op, Request& request) { // make sure we have a last error dassert(&LastError::get(cc())); OwnedPointerVector<BatchedCommandRequest> commandRequestsOwned; vector<BatchedCommandRequest*>& commandRequests = commandRequestsOwned.mutableVector(); msgToBatchRequests(request.m(), &commandRequests); for (vector<BatchedCommandRequest*>::iterator it = commandRequests.begin(); it != commandRequests.end(); ++it) { // Multiple commands registered to last error as multiple requests if (it != commandRequests.begin()) LastError::get(cc()).startRequest(); BatchedCommandRequest* commandRequest = *it; // Adjust namespaces for command NamespaceString fullNS(commandRequest->getNS()); string cmdNS = fullNS.getCommandNS(); // We only pass in collection name to command commandRequest->setNS(fullNS); BSONObjBuilder builder; BSONObj requestBSON = commandRequest->toBSON(); { // Disable the last error object for the duration of the write cmd LastError::Disabled disableLastError(&LastError::get(cc())); Command::runAgainstRegistered(txn, cmdNS.c_str(), requestBSON, builder, 0); } BatchedCommandResponse commandResponse; bool parsed = commandResponse.parseBSON(builder.done(), NULL); (void)parsed; // for compile dassert(parsed && commandResponse.isValid(NULL)); // Populate the lastError object based on the write response LastError::get(cc()).reset(); bool hadError = batchErrorToLastError(*commandRequest, commandResponse, &LastError::get(cc())); // Check if this is an ordered batch and we had an error which should stop processing if (commandRequest->getOrdered() && hadError) break; } }
Status SessionsCollectionSharded::refreshSessions(OperationContext* opCtx, const LogicalSessionRecordSet& sessions) { auto send = [&](BSONObj toSend) { auto opMsg = OpMsgRequest::fromDBAndBody(NamespaceString::kLogicalSessionsNamespace.db(), toSend); auto request = BatchedCommandRequest::parseUpdate(opMsg); BatchedCommandResponse response; BatchWriteExecStats stats; ClusterWriter::write(opCtx, request, &stats, &response); return response.toStatus(); }; return doRefresh(NamespaceString::kLogicalSessionsNamespace, sessions, send); }
Status DistLockCatalogImpl::unlockAll(OperationContext* opCtx, const std::string& processID) { BatchedCommandRequest request([&] { write_ops::Update updateOp(_locksNS); updateOp.setUpdates({[&] { write_ops::UpdateOpEntry entry; entry.setQ(BSON(LocksType::process(processID))); entry.setU(BSON("$set" << BSON(LocksType::state(LocksType::UNLOCKED)))); entry.setUpsert(false); entry.setMulti(true); return entry; }()}); return updateOp; }()); request.setWriteConcern(kLocalWriteConcern.toBSON()); BSONObj cmdObj = request.toBSON(); auto const shardRegistry = Grid::get(opCtx)->shardRegistry(); auto response = shardRegistry->getConfigShard()->runCommandWithFixedRetryAttempts( opCtx, ReadPreferenceSetting{ReadPreference::PrimaryOnly}, _locksNS.db().toString(), cmdObj, Shard::kDefaultConfigCommandTimeout, Shard::RetryPolicy::kIdempotent); if (!response.isOK()) { return response.getStatus(); } if (!response.getValue().commandStatus.isOK()) { return response.getValue().commandStatus; } if (!response.getValue().writeConcernStatus.isOK()) { return response.getValue().writeConcernStatus; } BatchedCommandResponse batchResponse; std::string errmsg; if (!batchResponse.parseBSON(response.getValue().response, &errmsg)) { return Status(ErrorCodes::FailedToParse, str::stream() << "Failed to parse config server response to batch request for " "unlocking existing distributed locks" << causedBy(errmsg)); } return batchResponse.toStatus(); }
Status DistLockCatalogImpl::unlockAll(OperationContext* txn, const std::string& processID) { std::unique_ptr<BatchedUpdateDocument> updateDoc(new BatchedUpdateDocument()); updateDoc->setQuery(BSON(LocksType::process(processID))); updateDoc->setUpdateExpr(BSON("$set" << BSON(LocksType::state(LocksType::UNLOCKED)))); updateDoc->setUpsert(false); updateDoc->setMulti(true); std::unique_ptr<BatchedUpdateRequest> updateRequest(new BatchedUpdateRequest()); updateRequest->addToUpdates(updateDoc.release()); BatchedCommandRequest request(updateRequest.release()); request.setNS(_locksNS); request.setWriteConcern(kLocalWriteConcern.toBSON()); BSONObj cmdObj = request.toBSON(); auto response = _client->getConfigShard()->runCommandWithFixedRetryAttempts( txn, ReadPreferenceSetting{ReadPreference::PrimaryOnly}, _locksNS.db().toString(), cmdObj, Shard::kDefaultConfigCommandTimeout, Shard::RetryPolicy::kIdempotent); if (!response.isOK()) { return response.getStatus(); } if (!response.getValue().commandStatus.isOK()) { return response.getValue().commandStatus; } if (!response.getValue().writeConcernStatus.isOK()) { return response.getValue().writeConcernStatus; } BatchedCommandResponse batchResponse; std::string errmsg; if (!batchResponse.parseBSON(response.getValue().response, &errmsg)) { return Status(ErrorCodes::FailedToParse, str::stream() << "Failed to parse config server response to batch request for " "unlocking existing distributed locks" << causedBy(errmsg)); } return batchResponse.toStatus(); }
Status CatalogManager::updateDatabase(const std::string& dbName, const DatabaseType& db) { fassert(28616, db.validate()); BatchedCommandResponse response; Status status = update(DatabaseType::ConfigNS, BSON(DatabaseType::name(dbName)), db.toBSON(), true, // upsert false, // multi &response); if (!status.isOK()) { return Status(status.code(), str::stream() << "database metadata write failed: " << response.toBSON() << "; status: " << status.toString()); } return Status::OK(); }
Status CatalogManager::updateCollection(const std::string& collNs, const CollectionType& coll) { fassert(28634, coll.validate()); BatchedCommandResponse response; Status status = update(CollectionType::ConfigNS, BSON(CollectionType::fullNs(collNs)), coll.toBSON(), true, // upsert false, // multi &response); if (!status.isOK()) { return Status(status.code(), str::stream() << "collection metadata write failed: " << response.toBSON() << "; status: " << status.toString()); } return Status::OK(); }
SessionsCollection::SendBatchFn SessionsCollection::makeSendFnForBatchWrite( const NamespaceString& ns, DBClientBase* client) { auto send = [client, ns](BSONObj batch) -> Status { BSONObj res; if (!client->runCommand(ns.db().toString(), batch, res)) { return getStatusFromCommandResult(res); } BatchedCommandResponse response; std::string errmsg; if (!response.parseBSON(res, &errmsg)) { return {ErrorCodes::FailedToParse, errmsg}; } return response.toStatus(); }; return send; }
Status clusterCreateIndex(OperationContext* txn, const string& ns, BSONObj keys, bool unique) { const NamespaceString nss(ns); const std::string dbName = nss.db().toString(); BSONObj indexDoc = createIndexDoc(ns, keys, unique); // Go through the shard insert path std::unique_ptr<BatchedInsertRequest> insert(new BatchedInsertRequest()); insert->addToDocuments(indexDoc); BatchedCommandRequest request(insert.release()); request.setNS(NamespaceString(nss.getSystemIndexesCollection())); request.setWriteConcern(WriteConcernOptions::Acknowledged); BatchedCommandResponse response; ClusterWriter writer(false, 0); writer.write(txn, request, &response); return response.toStatus(); }
static void incBatchStats( BatchedCommandRequest::BatchType batchType, const BatchedCommandResponse& response, BatchWriteStats* stats ) { if ( batchType == BatchedCommandRequest::BatchType_Insert) { stats->numInserted += response.getN(); } else if ( batchType == BatchedCommandRequest::BatchType_Update ) { int numUpserted = 0; if( response.isUpsertDetailsSet() ) { numUpserted = response.sizeUpsertDetails(); } stats->numMatched += ( response.getN() - numUpserted ); long long numModified = response.getNModified(); if (numModified >= 0) stats->numModified += numModified; else stats->numModified = -1; // sentinel used to indicate we omit the field downstream stats->numUpserted += numUpserted; } else { dassert( batchType == BatchedCommandRequest::BatchType_Delete ); stats->numDeleted += response.getN(); } }
bool WriteCmd::run(const string& dbName, BSONObj& cmdObj, int options, string& errMsg, BSONObjBuilder& result, bool fromRepl) { // Can't be run on secondaries (logTheOp() == false, slaveOk() == false). dassert( !fromRepl ); BatchedCommandRequest request( _writeType ); BatchedCommandResponse response; if ( !request.parseBSON( cmdObj, &errMsg ) || !request.isValid( &errMsg ) ) { // Batch parse failure response.setOk( false ); response.setErrCode( 99999 ); response.setErrMessage( errMsg ); result.appendElements( response.toBSON() ); // TODO // There's a pending issue about how to report response here. If we use // the command infra-structure, we should reuse the 'errmsg' field. But // we have already filed that message inside the BatchCommandResponse. // return response.getOk(); return true; } // Note that this is a runCommmand, and therefore, the database and the collection name // are in different parts of the grammar for the command. But it's more convenient to // work with a NamespaceString. We built it here and replace it in the parsed command. // Internally, everything work with the namespace string as opposed to just the // collection name. NamespaceString nss(dbName, request.getNS()); request.setNS(nss.ns()); { // Commands with locktype == NONE need to acquire a Context in order to set // CurOp::_ns. Setting a CurOp's namespace is necessary for higher-level // functionality (e.g. profiling) to operate on the correct database (note that // WriteBatchExecutor doesn't do this for us, since its job is to create child CurOp // objects and operate on them). // // Acquire ReadContext momentarily, for satisfying this purpose. Client::ReadContext ctx( dbName + ".$cmd" ); } WriteBatchExecutor writeBatchExecutor(&cc(), &globalOpCounters, lastError.get()); writeBatchExecutor.executeBatch( request, &response ); result.appendElements( response.toBSON() ); // TODO // There's a pending issue about how to report response here. If we use // the command infra-structure, we should reuse the 'errmsg' field. But // we have already filed that message inside the BatchCommandResponse. // return response.getOk(); return true; }
Status SessionsCollectionSharded::removeRecords(OperationContext* opCtx, const LogicalSessionIdSet& sessions) { auto send = [&](BSONObj toSend) { auto opMsg = OpMsgRequest::fromDBAndBody(SessionsCollection::kSessionsDb, toSend); auto request = BatchedCommandRequest::parseDelete(opMsg); BatchedCommandResponse response; BatchWriteExecStats stats; ClusterWriter::write(opCtx, request, &stats, &response); if (response.getOk()) { return Status::OK(); } auto error = response.isErrCodeSet() ? ErrorCodes::fromInt(response.getErrCode()) : ErrorCodes::UnknownError; return Status(error, response.getErrMessage()); }; return doRemove(sessions, send); }
Status AuthzManagerExternalStateMongos::update(const NamespaceString& collectionName, const BSONObj& query, const BSONObj& updatePattern, bool upsert, bool multi, const BSONObj& writeConcern, int* nMatched) { BatchedCommandResponse response; Status res = clusterUpdate(collectionName, query, updatePattern, upsert, multi, writeConcern, &response); if (res.isOK()) { *nMatched = response.getN(); } return res; }
bool WriteCmd::run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, int options, string& errMsg, BSONObjBuilder& result) { // Can't be run on secondaries. dassert(txn->writesAreReplicated()); BatchedCommandRequest request( _writeType ); BatchedCommandResponse response; if ( !request.parseBSON( cmdObj, &errMsg ) || !request.isValid( &errMsg ) ) { return appendCommandStatus( result, Status( ErrorCodes::FailedToParse, errMsg ) ); } // Note that this is a runCommmand, and therefore, the database and the collection name // are in different parts of the grammar for the command. But it's more convenient to // work with a NamespaceString. We built it here and replace it in the parsed command. // Internally, everything work with the namespace string as opposed to just the // collection name. NamespaceString nss(dbName, request.getNS()); request.setNSS(nss); StatusWith<WriteConcernOptions> wcStatus = extractWriteConcern(cmdObj); if (!wcStatus.isOK()) { return appendCommandStatus(result, wcStatus.getStatus()); } txn->setWriteConcern(wcStatus.getValue()); WriteBatchExecutor writeBatchExecutor(txn, &globalOpCounters, &LastError::get(txn->getClient())); writeBatchExecutor.executeBatch( request, &response ); result.appendElements( response.toBSON() ); return response.getOk(); }
static bool areResponsesEqual( const BatchedCommandResponse& responseA, const BatchedCommandResponse& responseB ) { // TODO: Better reporting of why not equal if ( responseA.getOk() != responseB.getOk() ) return false; if ( responseA.getN() != responseB.getN() ) return false; if ( responseA.isUpsertDetailsSet() != responseB.isUpsertDetailsSet() ) return false; if ( responseA.isUpsertDetailsSet() ) { // TODO: } if ( responseA.getOk() ) return true; // TODO: Compare errors here return true; }
bool ClusterWriteCmd::run( const string& dbName, BSONObj& cmdObj, int options, string& errMsg, BSONObjBuilder& result, bool ) { BatchedCommandRequest request( _writeType ); BatchedCommandResponse response; // TODO: if we do namespace parsing, push this to the type if ( !request.parseBSON( cmdObj, &errMsg ) || !request.isValid( &errMsg ) ) { // Batch parse failure response.setOk( false ); response.setErrCode( ErrorCodes::FailedToParse ); response.setErrMessage( errMsg ); dassert( response.isValid( &errMsg ) ); } else { // Fixup the namespace to be a full ns internally NamespaceString nss( dbName, request.getNS() ); request.setNS( nss.ns() ); clusterWrite( request, &response, true /* autosplit */ ); } // Populate the lastError object based on the write dassert( response.isValid( NULL ) ); LastError* lastErrorForRequest = lastError.get( true /* create */ ); dassert( lastErrorForRequest ); lastErrorForRequest->reset(); batchErrorToLastError( request, response, lastErrorForRequest ); // TODO // There's a pending issue about how to report response here. If we use // the command infra-structure, we should reuse the 'errmsg' field. But // we have already filed that message inside the BatchCommandResponse. // return response.getOk(); result.appendElements( response.toBSON() ); return true; }
static Status getStatus(const BatchedCommandResponse& response) { if (response.getOk() != 1) { return Status(static_cast<ErrorCodes::Error>(response.getErrCode()), response.getErrMessage()); } if (response.isErrDetailsSet()) { const WriteErrorDetail* errDetail = response.getErrDetails().front(); return Status(static_cast<ErrorCodes::Error>(errDetail->getErrCode()), errDetail->getErrMessage()); } if (response.isWriteConcernErrorSet()) { const WCErrorDetail* errDetail = response.getWriteConcernError(); return Status(static_cast<ErrorCodes::Error>(errDetail->getErrCode()), errDetail->getErrMessage()); } return Status::OK(); }
bool ClusterWriteCmd::run( const string& dbName, BSONObj& cmdObj, int options, string& errMsg, BSONObjBuilder& result, bool ) { BatchedCommandRequest request( _writeType ); BatchedCommandResponse response; ClusterWriter writer( true /* autosplit */, 0 /* timeout */ ); // TODO: if we do namespace parsing, push this to the type if ( !request.parseBSON( cmdObj, &errMsg ) || !request.isValid( &errMsg ) ) { // Batch parse failure response.setOk( false ); response.setErrCode( ErrorCodes::FailedToParse ); response.setErrMessage( errMsg ); } else { // Fixup the namespace to be a full ns internally NamespaceString nss( dbName, request.getNS() ); request.setNS( nss.ns() ); writer.write( request, &response ); } dassert( response.isValid( NULL ) ); // Save the last opTimes written on each shard for this client, to allow GLE to work if ( ClientInfo::exists() && writer.getStats().hasShardStats() ) { ClientInfo* clientInfo = ClientInfo::get( NULL ); clientInfo->addHostOpTimes( writer.getStats().getShardStats().getWriteOpTimes() ); } // TODO // There's a pending issue about how to report response here. If we use // the command infra-structure, we should reuse the 'errmsg' field. But // we have already filed that message inside the BatchCommandResponse. // return response.getOk(); result.appendElements( response.toBSON() ); return true; }
static bool areResponsesEqual( const BatchedCommandResponse& responseA, const BatchedCommandResponse& responseB ) { // Note: This needs to also take into account comparing responses from legacy writes // and write commands. // TODO: Better reporting of why not equal if ( responseA.getOk() != responseB.getOk() ) return false; if ( responseA.getN() != responseB.getN() ) return false; if ( responseA.isUpsertDetailsSet() ) { // TODO: } if ( responseA.getOk() ) return true; // TODO: Compare errors here return true; }
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()) { unique_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); } } }
static void cloneCommandErrorTo(const BatchedCommandResponse& batchResp, WriteErrorDetail* details) { details->setErrCode(batchResp.getErrCode()); details->setErrMessage(batchResp.getErrMessage()); }