void BatchWriteOp::buildBatchRequest(const TargetedWriteBatch& targetedBatch, BatchedCommandRequest* request) const { request->setNS(_clientRequest->getNS()); request->setShouldBypassValidation(_clientRequest->shouldBypassValidation()); const vector<TargetedWrite*>& targetedWrites = targetedBatch.getWrites(); for (vector<TargetedWrite*>::const_iterator it = targetedWrites.begin(); it != targetedWrites.end(); ++it) { const WriteOpRef& writeOpRef = (*it)->writeOpRef; BatchedCommandRequest::BatchType batchType = _clientRequest->getBatchType(); // NOTE: We copy the batch items themselves here from the client request // TODO: This could be inefficient, maybe we want to just reference in the future if (batchType == BatchedCommandRequest::BatchType_Insert) { BatchedInsertRequest* clientInsertRequest = _clientRequest->getInsertRequest(); BSONObj insertDoc = clientInsertRequest->getDocumentsAt(writeOpRef.first); request->getInsertRequest()->addToDocuments(insertDoc); } else if (batchType == BatchedCommandRequest::BatchType_Update) { BatchedUpdateRequest* clientUpdateRequest = _clientRequest->getUpdateRequest(); BatchedUpdateDocument* updateDoc = new BatchedUpdateDocument; clientUpdateRequest->getUpdatesAt(writeOpRef.first)->cloneTo(updateDoc); request->getUpdateRequest()->addToUpdates(updateDoc); } else { dassert(batchType == BatchedCommandRequest::BatchType_Delete); BatchedDeleteRequest* clientDeleteRequest = _clientRequest->getDeleteRequest(); BatchedDeleteDocument* deleteDoc = new BatchedDeleteDocument; clientDeleteRequest->getDeletesAt(writeOpRef.first)->cloneTo(deleteDoc); request->getDeleteRequest()->addToDeletes(deleteDoc); } // TODO: We can add logic here to allow aborting individual ops // if ( NULL == response ) { // ->responses.erase( it++ ); // continue; //} } if (_clientRequest->isWriteConcernSet()) { if (_clientRequest->isVerboseWC()) { request->setWriteConcern(_clientRequest->getWriteConcern()); } else { // Mongos needs to send to the shard with w > 0 so it will be able to // see the writeErrors. request->setWriteConcern(upgradeWriteConcern(_clientRequest->getWriteConcern())); } } if (!request->isOrderedSet()) { request->setOrdered(_clientRequest->getOrdered()); } unique_ptr<BatchedRequestMetadata> requestMetadata(new BatchedRequestMetadata()); requestMetadata->setShardVersion( ChunkVersionAndOpTime(targetedBatch.getEndpoint().shardVersion)); requestMetadata->setSession(0); request->setMetadata(requestMetadata.release()); }
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(); }); }
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(); }); }
void BatchWriteOp::buildBatchRequest( const TargetedWriteBatch& targetedBatch, BatchedCommandRequest* request ) const { request->setNS( _clientRequest->getNS() ); request->setShardVersion( targetedBatch.getEndpoint().shardVersion ); const vector<TargetedWrite*>& targetedWrites = targetedBatch.getWrites(); for ( vector<TargetedWrite*>::const_iterator it = targetedWrites.begin(); it != targetedWrites.end(); ++it ) { const WriteOpRef& writeOpRef = ( *it )->writeOpRef; BatchedCommandRequest::BatchType batchType = _clientRequest->getBatchType(); // NOTE: We copy the batch items themselves here from the client request // TODO: This could be inefficient, maybe we want to just reference in the future if ( batchType == BatchedCommandRequest::BatchType_Insert ) { BatchedInsertRequest* clientInsertRequest = _clientRequest->getInsertRequest(); BSONObj insertDoc = clientInsertRequest->getDocumentsAt( writeOpRef.first ); request->getInsertRequest()->addToDocuments( insertDoc ); } else if ( batchType == BatchedCommandRequest::BatchType_Update ) { BatchedUpdateRequest* clientUpdateRequest = _clientRequest->getUpdateRequest(); BatchedUpdateDocument* updateDoc = new BatchedUpdateDocument; clientUpdateRequest->getUpdatesAt( writeOpRef.first )->cloneTo( updateDoc ); request->getUpdateRequest()->addToUpdates( updateDoc ); } else { dassert( batchType == BatchedCommandRequest::BatchType_Delete ); BatchedDeleteRequest* clientDeleteRequest = _clientRequest->getDeleteRequest(); BatchedDeleteDocument* deleteDoc = new BatchedDeleteDocument; clientDeleteRequest->getDeletesAt( writeOpRef.first )->cloneTo( deleteDoc ); request->getDeleteRequest()->addToDeletes( deleteDoc ); } // TODO: We can add logic here to allow aborting individual ops //if ( NULL == response ) { // ->responses.erase( it++ ); // continue; //} } if ( _clientRequest->isWriteConcernSet() ) { request->setWriteConcern( _clientRequest->getWriteConcern() ); } if ( _clientRequest->isContinueOnErrorSet() ) { request->setContinueOnError( _clientRequest->getContinueOnError() ); } request->setSession( 0 ); }
/** * Generates a new request with insert _ids if required. Otherwise returns NULL. */ BatchedCommandRequest* // BatchedCommandRequest::cloneWithIds(const BatchedCommandRequest& origCmdRequest) { if (origCmdRequest.getBatchType() != BatchedCommandRequest::BatchType_Insert || origCmdRequest.isInsertIndexRequest()) return NULL; auto_ptr<BatchedInsertRequest> idRequest; BatchedInsertRequest* origRequest = origCmdRequest.getInsertRequest(); const vector<BSONObj>& inserts = origRequest->getDocuments(); size_t i = 0u; for (vector<BSONObj>::const_iterator it = inserts.begin(); it != inserts.end(); ++it, ++i) { const BSONObj& insert = *it; BSONObj idInsert; if (insert["_id"].eoo()) { BSONObjBuilder idInsertB; idInsertB.append("_id", OID::gen()); idInsertB.appendElements(insert); idInsert = idInsertB.obj(); } if (NULL == idRequest.get() && !idInsert.isEmpty()) { idRequest.reset(new BatchedInsertRequest); origRequest->cloneTo(idRequest.get()); } if (!idInsert.isEmpty()) { idRequest->setDocumentAt(i, idInsert); } } if (NULL == idRequest.get()) return NULL; // Command request owns idRequest return new BatchedCommandRequest(idRequest.release()); }
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.setN( 0 ); response.setErrCode( ErrorCodes::FailedToParse ); response.setErrMessage( errMsg ); dassert( response.isValid( &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()); Status status = userAllowedWriteNS( nss ); if ( !status.isOK() ) return appendCommandStatus( result, status ); if ( cc().curop() ) cc().curop()->setNS( nss.ns() ); if ( request.getBatchType() == BatchedCommandRequest::BatchType_Insert ) { // check all docs BatchedInsertRequest* insertRequest = request.getInsertRequest(); vector<BSONObj>& docsToInsert = insertRequest->getDocuments(); for ( size_t i = 0; i < docsToInsert.size(); i++ ) { StatusWith<BSONObj> fixed = fixDocumentForInsert( docsToInsert[i] ); if ( !fixed.isOK() ) { // we don't return early since each doc can be handled independantly continue; } if ( fixed.getValue().isEmpty() ) { continue; } docsToInsert[i] = fixed.getValue(); } } 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() ); // 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; }