/** * 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, bool fsyncCheck ) { NamespaceString nss( clientRequest.getNS() ); dassert( nss.db() == "config" || nss.db() == "admin" ); dassert( clientRequest.sizeWriteOps() == 1u ); if ( fsyncCheck ) { // // Sanity check that all configs are still reachable using fsync, preserving legacy // behavior // OwnedPointerVector<ConfigFsyncResponse> fsyncResponsesOwned; vector<ConfigFsyncResponse*>& fsyncResponses = fsyncResponsesOwned.mutableVector(); // // Send side // for ( vector<ConnectionString>::iterator it = _configHosts.begin(); it != _configHosts.end(); ++it ) { ConnectionString& configHost = *it; FsyncRequest fsyncRequest; _dispatcher->addCommand( configHost, "admin", fsyncRequest ); } _dispatcher->sendAll(); // // Recv side // bool fsyncError = false; while ( _dispatcher->numPending() > 0 ) { fsyncResponses.push_back( new ConfigFsyncResponse() ); ConfigFsyncResponse& fsyncResponse = *fsyncResponses.back(); Status dispatchStatus = _dispatcher->recvAny( &fsyncResponse.configHost, &fsyncResponse.response ); // We've got to recv everything, no matter what if ( !dispatchStatus.isOK() ) { fsyncError = true; buildFsyncErrorFrom( dispatchStatus, &fsyncResponse.response ); } else if ( !fsyncResponse.response.getOk() ) { fsyncError = true; } } if ( fsyncError ) { combineFsyncErrors( fsyncResponses, clientResponse ); return; } else { fsyncResponsesOwned.clear(); } } // // 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>::iterator it = _configHosts.begin(); it != _configHosts.end(); ++it ) { 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 ); }
/** * 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); }