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; }
Status enforceLegacyWriteConcern( MultiCommandDispatch* dispatcher, StringData dbName, const BSONObj& options, const HostOpTimeMap& hostOpTimes, vector<LegacyWCResponse>* legacyWCResponses ) { if ( hostOpTimes.empty() ) { return Status::OK(); } for ( HostOpTimeMap::const_iterator it = hostOpTimes.begin(); it != hostOpTimes.end(); ++it ) { const ConnectionString& shardEndpoint = it->first; const HostOpTime hot = it->second; const OpTime& opTime = hot.opTime; const OID& electionId = hot.electionId; LOG( 3 ) << "enforcing write concern " << options << " on " << shardEndpoint.toString() << " at opTime " << opTime.toStringPretty() << " with electionID " << electionId; BSONObj gleCmd = buildGLECmdWithOpTime( options, opTime, electionId ); RawBSONSerializable gleCmdSerial( gleCmd ); dispatcher->addCommand( shardEndpoint, dbName, gleCmdSerial ); } dispatcher->sendAll(); vector<Status> failedStatuses; while ( dispatcher->numPending() > 0 ) { ConnectionString shardEndpoint; RawBSONSerializable gleResponseSerial; Status dispatchStatus = dispatcher->recvAny( &shardEndpoint, &gleResponseSerial ); if ( !dispatchStatus.isOK() ) { // We need to get all responses before returning failedStatuses.push_back( dispatchStatus ); continue; } BSONObj gleResponse = stripNonWCInfo( gleResponseSerial.toBSON() ); // Use the downconversion tools to determine if this GLE response is ok, a // write concern error, or an unknown error we should immediately abort for. GLEErrors errors; Status extractStatus = extractGLEErrors( gleResponse, &errors ); if ( !extractStatus.isOK() ) { failedStatuses.push_back( extractStatus ); continue; } LegacyWCResponse wcResponse; wcResponse.shardHost = shardEndpoint.toString(); wcResponse.gleResponse = gleResponse; if ( errors.wcError.get() ) { wcResponse.errToReport = errors.wcError->getErrMessage(); } legacyWCResponses->push_back( wcResponse ); } if ( failedStatuses.empty() ) { return Status::OK(); } StringBuilder builder; builder << "could not enforce write concern"; for ( vector<Status>::const_iterator it = failedStatuses.begin(); it != failedStatuses.end(); ++it ) { const Status& failedStatus = *it; if ( it == failedStatuses.begin() ) { builder << causedBy( failedStatus.toString() ); } else { builder << ":: and ::" << failedStatus.toString(); } } return Status( failedStatuses.size() == 1u ? failedStatuses.front().code() : ErrorCodes::MultipleErrorsOccurred, builder.str() ); }
/** * The core config write functionality. * * Config writes run in two passes - the first is a quick check to ensure the config servers * are all reachable, the second runs the actual write. * * TODO: Upgrade and move this logic to the config servers, a state machine implementation * is probably the next step. */ void ConfigCoordinator::executeBatch(const BatchedCommandRequest& clientRequest, BatchedCommandResponse* clientResponse) { const NamespaceString nss(clientRequest.getNS()); // Should never use it for anything other than DBs residing on the config server dassert(nss.db() == "config" || nss.db() == "admin"); dassert(clientRequest.sizeWriteOps() == 1u); // This is an opportunistic check that all config servers look healthy by calling // getLastError on each one of them. If there was some form of write/journaling error, get // last error would fail. { for (vector<ConnectionString>::iterator it = _configHosts.begin(); it != _configHosts.end(); ++it) { _dispatcher->addCommand(*it, "admin", RawBSONSerializable(BSON("getLastError" << true << "fsync" << true))); } _dispatcher->sendAll(); bool error = false; while (_dispatcher->numPending()) { ConnectionString host; RawBSONSerializable response; Status status = _dispatcher->recvAny(&host, &response); if (status.isOK()) { BSONObj obj = response.toBSON(); LOG(3) << "Response " << obj.toString(); // If the ok field is anything other than 1, count it as error if (!obj["ok"].trueValue()) { error = true; log() << "Config server check for host " << host << " returned error: " << response; } } else { error = true; log() << "Config server check for host " << host << " failed with status: " << status; } } // All responses should have been gathered by this point if (error) { clientResponse->setOk(false); clientResponse->setErrCode(ErrorCodes::RemoteValidationError); clientResponse->setErrMessage("Could not verify that config servers were active" " and reachable before write"); return; } } if (!_checkConfigString(clientResponse)) { return; } // // Do the actual writes // BatchedCommandRequest configRequest( clientRequest.getBatchType() ); clientRequest.cloneTo( &configRequest ); configRequest.setNS( nss.coll() ); OwnedPointerVector<ConfigResponse> responsesOwned; vector<ConfigResponse*>& responses = responsesOwned.mutableVector(); // // Send the actual config writes // // Get as many batches as we can at once for (vector<ConnectionString>::const_iterator it = _configHosts.begin(); it != _configHosts.end(); ++it) { const ConnectionString& configHost = *it; _dispatcher->addCommand(configHost, nss.db(), configRequest); } // Send them all out _dispatcher->sendAll(); // // Recv side // while (_dispatcher->numPending() > 0) { // Get the response responses.push_back(new ConfigResponse()); ConfigResponse& configResponse = *responses.back(); Status dispatchStatus = _dispatcher->recvAny(&configResponse.configHost, &configResponse.response); if (!dispatchStatus.isOK()) { buildErrorFrom(dispatchStatus, &configResponse.response); } } combineResponses(responses, clientResponse); }