/// @brief remaining int64_t RemoteBlock::remaining() { DEBUG_BEGIN_BLOCK(); // For every call we simply forward via HTTP std::unique_ptr<ClusterCommResult> res = sendRequest( rest::RequestType::GET, "/_api/aql/remaining/", std::string()); throwExceptionAfterBadSyncRequest(res.get(), false); // If we get here, then res->result is the response which will be // a serialized AqlItemBlock: StringBuffer const& responseBodyBuf(res->result->getBody()); std::shared_ptr<VPackBuilder> builder = VPackParser::fromJson(responseBodyBuf.c_str(), responseBodyBuf.length()); VPackSlice slice = builder->slice(); if (!slice.hasKey("error") || slice.get("error").getBoolean()) { THROW_ARANGO_EXCEPTION(TRI_ERROR_CLUSTER_AQL_COMMUNICATION); } int64_t remaining = 0; if (slice.hasKey("remaining")) { remaining = slice.get("remaining").getNumericValue<int64_t>(); } return remaining; // cppcheck-suppress style DEBUG_END_BLOCK(); }
AuthJwtResult AuthInfo::validateJwtBody(std::string const& body) { std::shared_ptr<VPackBuilder> bodyBuilder = parseJson(StringUtils::decodeBase64(body), "jwt body"); AuthJwtResult authResult; if (bodyBuilder.get() == nullptr) { return authResult; } VPackSlice const bodySlice = bodyBuilder->slice(); if (!bodySlice.isObject()) { return authResult; } VPackSlice const issSlice = bodySlice.get("iss"); if (!issSlice.isString()) { return authResult; } if (issSlice.copyString() != "arangodb") { return authResult; } if (bodySlice.hasKey("preferred_username")) { VPackSlice const usernameSlice = bodySlice.get("preferred_username"); if (!usernameSlice.isString()) { return authResult; } authResult._username = usernameSlice.copyString(); } else if (bodySlice.hasKey("server_id")) { // mop: hmm...nothing to do here :D // authResult._username = "******"; } else { return authResult; } // mop: optional exp (cluster currently uses non expiring jwts) if (bodySlice.hasKey("exp")) { VPackSlice const expSlice = bodySlice.get("exp"); if (!expSlice.isNumber()) { return authResult; } std::chrono::system_clock::time_point expires( std::chrono::seconds(expSlice.getNumber<uint64_t>())); std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); if (now >= expires) { return authResult; } authResult._expires = true; authResult._expireTime = expires; } authResult._authorized = true; return authResult; }
int RestoreFeature::sendRestoreCollection(VPackSlice const& slice, std::string const& name, std::string& errorMsg) { std::string url = "/_api/replication/restore-collection" "?overwrite=" + std::string(_overwrite ? "true" : "false") + "&recycleIds=" + std::string(_recycleIds ? "true" : "false") + "&force=" + std::string(_force ? "true" : "false"); if (_clusterMode) { if (!slice.hasKey(std::vector<std::string>({"parameters", "shards"})) && !slice.hasKey( std::vector<std::string>({"parameters", "numberOfShards"}))) { // no "shards" and no "numberOfShards" attribute present. now assume // default value from --default-number-of-shards std::cerr << "# no sharding information specified for collection '" << name << "', using default number of shards " << _defaultNumberOfShards << std::endl; url += "&numberOfShards=" + std::to_string(_defaultNumberOfShards); } if (!slice.hasKey(std::vector<std::string>({"parameters", "replicationFactor"}))) { // No replication factor given, so take the default: std::cerr << "# no replication information specified for collection '" << name << "', using default replication factor " << _defaultReplicationFactor << std::endl; url += "&replicationFactor=" + std::to_string(_defaultReplicationFactor); } } std::string const body = slice.toJson(); std::unique_ptr<SimpleHttpResult> response(_httpClient->request( rest::RequestType::PUT, url, body.c_str(), body.size())); if (response == nullptr || !response->isComplete()) { errorMsg = "got invalid response from server: " + _httpClient->getErrorMessage(); return TRI_ERROR_INTERNAL; } if (response->wasHttpError()) { int err; errorMsg = getHttpErrorMessage(response.get(), &err); if (err != TRI_ERROR_NO_ERROR) { return err; } return TRI_ERROR_INTERNAL; } return TRI_ERROR_NO_ERROR; }
/// @brief initialize int RemoteBlock::initialize() { DEBUG_BEGIN_BLOCK(); if (!_isResponsibleForInitializeCursor) { // do nothing... return TRI_ERROR_NO_ERROR; } std::unique_ptr<ClusterCommResult> res = sendRequest(rest::RequestType::PUT, "/_api/aql/initialize/", "{}"); throwExceptionAfterBadSyncRequest(res.get(), false); // If we get here, then res->result is the response which will be // a serialized AqlItemBlock: StringBuffer const& responseBodyBuf(res->result->getBody()); std::shared_ptr<VPackBuilder> builder = VPackParser::fromJson(responseBodyBuf.c_str(), responseBodyBuf.length()); VPackSlice slice = builder->slice(); if (slice.hasKey("code")) { return slice.get("code").getNumericValue<int>(); } return TRI_ERROR_INTERNAL; // cppcheck-suppress style DEBUG_END_BLOCK(); }
bool Node::handle<SET>(VPackSlice const& slice) { Slice val = slice.get("new"); if (val.isObject()) { if (val.hasKey("op")) { // No longer a keyword but a regular key "op" if (_children.find("op") == _children.end()) { _children["op"] = std::make_shared<Node>("op", this); } *(_children["op"]) = val.get("op"); } else { // Deeper down this->applies(val); } } else { *this = val; } if (slice.hasKey("ttl")) { VPackSlice ttl_v = slice.get("ttl"); if (ttl_v.isNumber()) { long ttl = 1000l * ((ttl_v.isDouble()) ? static_cast<long>(slice.get("ttl").getDouble()) : static_cast<long>(slice.get("ttl").getInt())); addTimeToLive(ttl); } else { LOG_TOPIC(WARN, Logger::AGENCY) << "Non-number value assigned to ttl: " << ttl_v.toJson(); } } return true; }
ShortestPathOptions::ShortestPathOptions(VPackSlice const& slice) { VPackSlice obj = slice.get("shortestpathFlags"); weightAttribute = ""; if (obj.hasKey("weightAttribute")) { VPackSlice v = obj.get("weightAttribute"); if (v.isString()) { weightAttribute = v.copyString(); } } defaultWeight = 1; if (obj.hasKey("defaultWeight")) { VPackSlice v = obj.get("defaultWeight"); if (v.isNumber()) { defaultWeight = v.getNumericValue<double>(); } } }
/// @brief initializeCursor, could be called multiple times int RemoteBlock::initializeCursor(AqlItemBlock* items, size_t pos) { DEBUG_BEGIN_BLOCK(); // For every call we simply forward via HTTP if (!_isResponsibleForInitializeCursor) { // do nothing... return TRI_ERROR_NO_ERROR; } VPackBuilder builder; builder.openObject(); if (items == nullptr) { // first call, items is still a nullptr builder.add("exhausted", VPackValue(true)); builder.add("error", VPackValue(false)); } else { builder.add("exhausted", VPackValue(false)); builder.add("error", VPackValue(false)); builder.add("pos", VPackValue(pos)); builder.add(VPackValue("items")); builder.openObject(); items->toVelocyPack(_engine->getQuery()->trx(), builder); builder.close(); } builder.close(); std::string bodyString(builder.slice().toJson()); std::unique_ptr<ClusterCommResult> res = sendRequest( rest::RequestType::PUT, "/_api/aql/initializeCursor/", bodyString); throwExceptionAfterBadSyncRequest(res.get(), false); // If we get here, then res->result is the response which will be // a serialized AqlItemBlock: StringBuffer const& responseBodyBuf(res->result->getBody()); { std::shared_ptr<VPackBuilder> builder = VPackParser::fromJson( responseBodyBuf.c_str(), responseBodyBuf.length()); VPackSlice slice = builder->slice(); if (slice.hasKey("code")) { return slice.get("code").getNumericValue<int>(); } return TRI_ERROR_INTERNAL; } // cppcheck-suppress style DEBUG_END_BLOCK(); }
bool VelocyPackHelper::getBooleanValue(VPackSlice const& slice, char const* name, bool defaultValue) { TRI_ASSERT(slice.isObject()); if (!slice.hasKey(name)) { return defaultValue; } VPackSlice const& sub = slice.get(name); if (sub.isBoolean()) { return sub.getBool(); } return defaultValue; }
/// @brief skipSome size_t RemoteBlock::skipSome(size_t atLeast, size_t atMost) { DEBUG_BEGIN_BLOCK(); // For every call we simply forward via HTTP VPackBuilder builder; builder.openObject(); builder.add("atLeast", VPackValue(atLeast)); builder.add("atMost", VPackValue(atMost)); builder.close(); std::string bodyString(builder.slice().toJson()); std::unique_ptr<ClusterCommResult> res = sendRequest(rest::RequestType::PUT, "/_api/aql/skipSome/", bodyString); throwExceptionAfterBadSyncRequest(res.get(), false); // If we get here, then res->result is the response which will be // a serialized AqlItemBlock: StringBuffer const& responseBodyBuf(res->result->getBody()); { std::shared_ptr<VPackBuilder> builder = VPackParser::fromJson( responseBodyBuf.c_str(), responseBodyBuf.length()); VPackSlice slice = builder->slice(); if (!slice.hasKey("error") || slice.get("error").getBoolean()) { THROW_ARANGO_EXCEPTION(TRI_ERROR_CLUSTER_AQL_COMMUNICATION); } size_t skipped = 0; if (slice.hasKey("skipped")) { skipped = slice.get("skipped").getNumericValue<size_t>(); } return skipped; } // cppcheck-suppress style DEBUG_END_BLOCK(); }
/// @brief shutdown, will be called exactly once for the whole query int RemoteBlock::shutdown(int errorCode) { DEBUG_BEGIN_BLOCK(); if (!_isResponsibleForInitializeCursor) { // do nothing... return TRI_ERROR_NO_ERROR; } // For every call we simply forward via HTTP std::unique_ptr<ClusterCommResult> res = sendRequest(rest::RequestType::PUT, "/_api/aql/shutdown/", std::string("{\"code\":" + std::to_string(errorCode) + "}")); if (throwExceptionAfterBadSyncRequest(res.get(), true)) { // artificially ignore error in case query was not found during shutdown return TRI_ERROR_NO_ERROR; } StringBuffer const& responseBodyBuf(res->result->getBody()); std::shared_ptr<VPackBuilder> builder = VPackParser::fromJson(responseBodyBuf.c_str(), responseBodyBuf.length()); VPackSlice slice = builder->slice(); // read "warnings" attribute if present and add it to our query if (slice.isObject()) { VPackSlice warnings = slice.get("warnings"); if (warnings.isArray()) { auto query = _engine->getQuery(); for (auto const& it : VPackArrayIterator(warnings)) { if (it.isObject()) { VPackSlice code = it.get("code"); VPackSlice message = it.get("message"); if (code.isNumber() && message.isString()) { query->registerWarning(code.getNumericValue<int>(), message.copyString().c_str()); } } } } } if (slice.hasKey("code")) { return slice.get("code").getNumericValue<int>(); } return TRI_ERROR_INTERNAL; // cppcheck-suppress style DEBUG_END_BLOCK(); }
bool Node::handle<PREPEND>(VPackSlice const& slice) { if (!slice.hasKey("new")) { LOG_TOPIC(WARN, Logger::AGENCY) << "Operator prepend without new value: " << slice.toJson(); return false; } Builder tmp; tmp.openArray(); tmp.add(slice.get("new")); if (this->slice().isArray()) { for (auto const& old : VPackArrayIterator(this->slice())) tmp.add(old); } tmp.close(); *this = tmp.slice(); return true; }
bool Node::handle<OBSERVE>(VPackSlice const& slice) { if (!slice.hasKey("url")) return false; if (!slice.get("url").isString()) return false; std::string url(slice.get("url").copyString()), uri(this->uri()); // check if such entry exists if (!observedBy(url)) { store().observerTable().emplace( std::pair<std::string, std::string>(url, uri)); store().observedTable().emplace( std::pair<std::string, std::string>(uri, url)); return true; } return false; }
std::string VelocyPackHelper::getStringValue(VPackSlice slice, std::string const& name, std::string const& defaultValue) { if (slice.isExternal()) { slice = VPackSlice(slice.getExternal()); } TRI_ASSERT(slice.isObject()); if (!slice.hasKey(name)) { return defaultValue; } VPackSlice const sub = slice.get(name); if (!sub.isString()) { return defaultValue; } return sub.copyString(); }
std::string VelocyPackHelper::checkAndGetStringValue(VPackSlice const& slice, std::string const& name) { TRI_ASSERT(slice.isObject()); if (!slice.hasKey(name)) { std::string msg = "The attribute '" + name + "' was not found."; THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER, msg); } VPackSlice const sub = slice.get(name); if (!sub.isString()) { std::string msg = "The attribute '" + name + "' is not a string."; THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER, msg); } return sub.copyString(); }
bool Node::handle<UNOBSERVE>(VPackSlice const& slice) { if (!slice.hasKey("url")) return false; if (!slice.get("url").isString()) return false; std::string url(slice.get("url").copyString()), uri(this->uri()); // delete in both cases a single entry (ensured above) // breaking the iterators is fine then auto ret = store().observerTable().equal_range(url); for (auto it = ret.first; it != ret.second; ++it) { if (it->second == uri) { store().observerTable().erase(it); break; } } ret = store().observedTable().equal_range(uri); for (auto it = ret.first; it != ret.second; ++it) { if (it->second == url) { store().observedTable().erase(it); return true; } } return false; }
/// @brief sendToClient: for each row of the incoming AqlItemBlock use the /// attributes <shardKeys> of the Aql value <val> to determine to which shard /// the row should be sent and return its clientId size_t DistributeBlock::sendToClient(AqlItemBlock* cur) { DEBUG_BEGIN_BLOCK(); // inspect cur in row _pos and check to which shard it should be sent . . AqlValue val = cur->getValueReference(_pos, _regId); VPackSlice input = val.slice(); // will throw when wrong type bool usedAlternativeRegId = false; if (input.isNull() && _alternativeRegId != ExecutionNode::MaxRegisterId) { // value is set, but null // check if there is a second input register available (UPSERT makes use of // two input registers, // one for the search document, the other for the insert document) val = cur->getValueReference(_pos, _alternativeRegId); input = val.slice(); // will throw when wrong type usedAlternativeRegId = true; } VPackSlice value = input; VPackBuilder builder; VPackBuilder builder2; bool hasCreatedKeyAttribute = false; if (input.isString() && static_cast<DistributeNode const*>(_exeNode) ->_allowKeyConversionToObject) { builder.openObject(); builder.add(StaticStrings::KeyString, input); builder.close(); // clear the previous value cur->destroyValue(_pos, _regId); // overwrite with new value cur->setValue(_pos, _regId, AqlValue(builder)); value = builder.slice(); hasCreatedKeyAttribute = true; } else if (!input.isObject()) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } TRI_ASSERT(value.isObject()); if (static_cast<DistributeNode const*>(_exeNode)->_createKeys) { // we are responsible for creating keys if none present if (_usesDefaultSharding) { // the collection is sharded by _key... if (!hasCreatedKeyAttribute && !value.hasKey(StaticStrings::KeyString)) { // there is no _key attribute present, so we are responsible for // creating one VPackBuilder temp; temp.openObject(); temp.add(StaticStrings::KeyString, VPackValue(createKey(value))); temp.close(); builder2 = VPackCollection::merge(input, temp.slice(), true); // clear the previous value and overwrite with new value: if (usedAlternativeRegId) { cur->destroyValue(_pos, _alternativeRegId); cur->setValue(_pos, _alternativeRegId, AqlValue(builder2)); } else { cur->destroyValue(_pos, _regId); cur->setValue(_pos, _regId, AqlValue(builder2)); } value = builder2.slice(); } } else { // the collection is not sharded by _key if (hasCreatedKeyAttribute || value.hasKey(StaticStrings::KeyString)) { // a _key was given, but user is not allowed to specify _key if (usedAlternativeRegId || !_allowSpecifiedKeys) { THROW_ARANGO_EXCEPTION(TRI_ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY); } } else { VPackBuilder temp; temp.openObject(); temp.add(StaticStrings::KeyString, VPackValue(createKey(value))); temp.close(); builder2 = VPackCollection::merge(input, temp.slice(), true); // clear the previous value and overwrite with new value: if (usedAlternativeRegId) { cur->destroyValue(_pos, _alternativeRegId); cur->setValue(_pos, _alternativeRegId, AqlValue(builder2.slice())); } else { cur->destroyValue(_pos, _regId); cur->setValue(_pos, _regId, AqlValue(builder2.slice())); } value = builder2.slice(); } } } std::string shardId; bool usesDefaultShardingAttributes; auto clusterInfo = arangodb::ClusterInfo::instance(); auto collInfo = _collection->getCollection(); int res = clusterInfo->getResponsibleShard(collInfo.get(), value, true, shardId, usesDefaultShardingAttributes); // std::cout << "SHARDID: " << shardId << "\n"; if (res != TRI_ERROR_NO_ERROR) { THROW_ARANGO_EXCEPTION(res); } TRI_ASSERT(!shardId.empty()); return getClientId(shardId); // cppcheck-suppress style DEBUG_END_BLOCK(); }
void RestWalHandler::properties() { auto l = arangodb::wal::LogfileManager::instance(); if (_request->requestType() == rest::RequestType::PUT) { std::shared_ptr<VPackBuilder> parsedRequest; VPackSlice slice; try { slice = _request->payload(); } catch (...) { generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "invalid body value. expecting object"); return; } if (!slice.isObject()) { generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, "invalid body value. expecting object"); } if (slice.hasKey("allowOversizeEntries")) { bool value = slice.get("allowOversizeEntries").getBoolean(); l->allowOversizeEntries(value); } if (slice.hasKey("logfileSize")) { uint32_t value = slice.get("logfileSize").getNumericValue<uint32_t>(); l->filesize(value); } if (slice.hasKey("historicLogfiles")) { uint32_t value = slice.get("historicLogfiles").getNumericValue<uint32_t>(); l->historicLogfiles(value); } if (slice.hasKey("reserveLogfiles")) { uint32_t value = slice.get("reserveLogfiles").getNumericValue<uint32_t>(); l->reserveLogfiles(value); } if (slice.hasKey("throttleWait")) { uint64_t value = slice.get("throttleWait").getNumericValue<uint64_t>(); l->maxThrottleWait(value); } if (slice.hasKey("throttleWhenPending")) { uint64_t value = slice.get("throttleWhenPending").getNumericValue<uint64_t>(); l->throttleWhenPending(value); } } VPackBuilder builder; builder.openObject(); builder.add("allowOversizeEntries", VPackValue(l->allowOversizeEntries())); builder.add("logfileSize", VPackValue(l->filesize())); builder.add("historicLogfiles", VPackValue(l->historicLogfiles())); builder.add("reserveLogfiles", VPackValue(l->reserveLogfiles())); builder.add("syncInterval", VPackValue(l->syncInterval())); builder.add("throttleWait", VPackValue(l->maxThrottleWait())); builder.add("throttleWhenPending", VPackValue(l->throttleWhenPending())); builder.close(); generateResult(rest::ResponseCode::OK, builder.slice()); }
/// @brief local helper to throw an exception if a HTTP request went wrong static bool throwExceptionAfterBadSyncRequest(ClusterCommResult* res, bool isShutdown) { DEBUG_BEGIN_BLOCK(); if (res->status == CL_COMM_TIMEOUT || res->status == CL_COMM_BACKEND_UNAVAILABLE) { THROW_ARANGO_EXCEPTION_MESSAGE(res->getErrorCode(), res->stringifyErrorMessage()); } if (res->status == CL_COMM_ERROR) { std::string errorMessage; TRI_ASSERT(nullptr != res->result); arangodb::basics::StringBuffer const& responseBodyBuf(res->result->getBody()); // extract error number and message from response int errorNum = TRI_ERROR_NO_ERROR; std::shared_ptr<VPackBuilder> builder = VPackParser::fromJson( responseBodyBuf.c_str(), responseBodyBuf.length()); VPackSlice slice = builder->slice(); if (!slice.hasKey("error") || slice.get("error").getBoolean()) { errorNum = TRI_ERROR_INTERNAL; errorMessage = std::string("Error message received from shard '") + std::string(res->shardID) + std::string("' on cluster node '") + std::string(res->serverID) + std::string("': "); } if (slice.isObject()) { VPackSlice v = slice.get("errorNum"); if (v.isNumber()) { if (v.getNumericValue<int>() != TRI_ERROR_NO_ERROR) { /* if we've got an error num, error has to be true. */ TRI_ASSERT(errorNum == TRI_ERROR_INTERNAL); errorNum = v.getNumericValue<int>(); } } v = slice.get("errorMessage"); if (v.isString()) { errorMessage += v.copyString(); } else { errorMessage += std::string("(no valid error in response)"); } } else { errorMessage += std::string("(no valid response)"); } if (isShutdown && errorNum == TRI_ERROR_QUERY_NOT_FOUND) { // this error may happen on shutdown and is thus tolerated // pass the info to the caller who can opt to ignore this error return true; } // In this case a proper HTTP error was reported by the DBserver, if (errorNum > 0 && !errorMessage.empty()) { THROW_ARANGO_EXCEPTION_MESSAGE(errorNum, errorMessage); } // default error THROW_ARANGO_EXCEPTION(TRI_ERROR_CLUSTER_AQL_COMMUNICATION); } return false; // cppcheck-suppress style DEBUG_END_BLOCK(); }
/// @brief Call to election void Constituent::callElection() { std::vector<bool> votes(size(), false); votes.at(_id) = true; // vote for myself _cast = true; if (_role == CANDIDATE) { this->term(_term + 1); // raise my term } std::string body; std::vector<OperationID> operationIDs(config().endpoints.size()); std::stringstream path; path << "/_api/agency_priv/requestVote?term=" << _term << "&candidateId=" << _id << "&prevLogIndex=" << _agent->lastLog().index << "&prevLogTerm=" << _agent->lastLog().term; // Ask everyone for their vote for (arangodb::consensus::id_t i = 0; i < config().endpoints.size(); ++i) { if (i != _id && endpoint(i) != "") { auto headerFields = std::make_unique<std::unordered_map<std::string, std::string>>(); operationIDs[i] = arangodb::ClusterComm::instance()->asyncRequest( "1", 1, config().endpoints[i], GeneralRequest::RequestType::GET, path.str(), std::make_shared<std::string>(body), headerFields, nullptr, config().minPing, true); } } // Wait randomized timeout std::this_thread::sleep_for( sleepFor(.5 * config().minPing, .8 * config().minPing)); // Collect votes // FIXME: This code can be improved: One can wait for an arbitrary // result by creating a coordinatorID and waiting for a pattern. for (arangodb::consensus::id_t i = 0; i < config().endpoints.size(); ++i) { if (i != _id && endpoint(i) != "") { ClusterCommResult res = arangodb::ClusterComm::instance()->enquire(operationIDs[i]); if (res.status == CL_COMM_SENT) { // Request successfully sent res = arangodb::ClusterComm::instance()->wait( "1", 1, operationIDs[i], "1"); std::shared_ptr<Builder> body = res.result->getBodyVelocyPack(); if (body->isEmpty()) { // body empty continue; } else { if (body->slice().isObject()) { // body VPackSlice slc = body->slice(); if (slc.hasKey("term") && slc.hasKey("voteGranted")) { // OK term_t t = slc.get("term").getUInt(); if (t > _term) { // follow? follow(t); break; } votes[i] = slc.get("voteGranted").getBool(); // Get vote } } } } else { // Request failed votes[i] = false; } } } // Count votes size_t yea = 0; for (size_t i = 0; i < size(); ++i) { if (votes[i]) { yea++; } } // Evaluate election results if (yea > size() / 2) { lead(votes); } else { follow(_term); } }
/// @brief Call to election void Constituent::callElection() { std::map<std::string, bool> votes; std::vector<std::string> active = _agent->config().active(); // Get copy of active votes[_id] = true; // vote for myself term_t savedTerm; { MUTEX_LOCKER(locker, _castLock); this->termNoLock(_term + 1); // raise my term _cast = true; _votedFor = _id; savedTerm = _term; LOG_TOPIC(DEBUG, Logger::AGENCY) << "Set _leaderID to NO_LEADER" << " in term " << _term; _leaderID = NO_LEADER; } std::string body; std::map<std::string, OperationID> operationIDs; std::stringstream path; path << "/_api/agency_priv/requestVote?term=" << savedTerm << "&candidateId=" << _id << "&prevLogIndex=" << _agent->lastLog().index << "&prevLogTerm=" << _agent->lastLog().term; double minPing = _agent->config().minPing(); double respTimeout = 0.9 * minPing; double initTimeout = 0.5 * minPing; // Ask everyone for their vote for (auto const& i : active) { if (i != _id) { auto headerFields = std::make_unique<std::unordered_map<std::string, std::string>>(); operationIDs[i] = ClusterComm::instance()->asyncRequest( "1", 1, _agent->config().poolAt(i), rest::RequestType::GET, path.str(), std::make_shared<std::string>(body), headerFields, nullptr, respTimeout, true, initTimeout); } } // Wait randomized timeout std::this_thread::sleep_for(sleepFor(initTimeout, respTimeout)); // Collect votes for (const auto& i : active) { if (i != _id) { ClusterCommResult res = arangodb::ClusterComm::instance()->enquire(operationIDs[i]); if (res.status == CL_COMM_SENT) { // Request successfully sent res = ClusterComm::instance()->wait("1", 1, operationIDs[i], "1"); std::shared_ptr<Builder> body = res.result->getBodyVelocyPack(); if (body->isEmpty()) { // body empty continue; } else { if (body->slice().isObject()) { // body VPackSlice slc = body->slice(); if (slc.hasKey("term") && slc.hasKey("voteGranted")) { // OK term_t t = slc.get("term").getUInt(); if (t > _term) { // follow? follow(t); break; } votes[i] = slc.get("voteGranted").getBool(); // Get vote } } } } else { // Request failed votes[i] = false; } } } // Count votes size_t yea = 0; for (auto const& i : votes) { if (i.second) { ++yea; } } { MUTEX_LOCKER(locker, _castLock); if (savedTerm != _term) { followNoLock(_term); return; } } // Evaluate election results if (yea > size() / 2) { lead(savedTerm, votes); } else { follow(_term); } }