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; } }
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; }
bool ClusterWriteCmd::run(OperationContext* txn, const string& dbName, BSONObj& cmdObj, int options, string& errMsg, BSONObjBuilder& result, bool ) { BatchedCommandRequest request( _writeType ); BatchedCommandResponse response; ClusterWriter writer( true /* autosplit */, 0 /* timeout */ ); // NOTE: Sometimes this command is invoked with LE disabled for legacy writes LastError* cmdLastError = lastError.get( false ); { // Disable the last error object for the duration of the write LastError::Disabled disableLastError( cmdLastError ); // 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 ) ); } if ( cmdLastError ) { // Populate the lastError object based on the write response cmdLastError->reset(); batchErrorToLastError( request, response, cmdLastError ); } size_t numAttempts; if ( !response.getOk() ) { numAttempts = 0; } else if ( request.getOrdered() && response.isErrDetailsSet() ) { numAttempts = response.getErrDetailsAt(0)->getIndex() + 1; // Add one failed attempt } else { numAttempts = request.sizeWriteOps(); } // TODO: increase opcounters by more than one if ( _writeType == BatchedCommandRequest::BatchType_Insert ) { for( size_t i = 0; i < numAttempts; ++i ) { globalOpCounters.gotInsert(); } } else if ( _writeType == BatchedCommandRequest::BatchType_Update ) { for( size_t i = 0; i < numAttempts; ++i ) { globalOpCounters.gotUpdate(); } } else if ( _writeType == BatchedCommandRequest::BatchType_Delete ) { for( size_t i = 0; i < numAttempts; ++i ) { globalOpCounters.gotDelete(); } } // 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; }