1
0
Fork 0

Agency compiling again

This commit is contained in:
Kaveh Vahedipour 2016-08-11 12:05:53 +02:00
parent 39ca341097
commit 4759b2a434
10 changed files with 167 additions and 149 deletions

View File

@ -70,7 +70,7 @@ void AgencyFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
new UInt64Parameter(&_size));
options->addOption("--agency.pool-size", "number of agent pool",
new UInt64Parameter(&_pool_size));
new UInt64Parameter(&_poolSize));
options->addOption(
"--agency.election-timeout-min",
@ -122,7 +122,7 @@ void AgencyFeature::validateOptions(std::shared_ptr<ProgramOptions> options) {
}
// Agency size
if (_pool_size < _size) {
if (_poolSize < _size) {
LOG_TOPIC(FATAL, Logger::AGENCY)
<< "AGENCY: agency pool size must be larger than agency size.";
FATAL_ERROR_EXIT();
@ -191,7 +191,7 @@ void AgencyFeature::start() {
_agent.reset(
new consensus::Agent(
consensus::config_t(
std::string(), _minElectionTimeout, _maxElectionTimeout, endpoint,
_size, _poolSize, _minElectionTimeout, _maxElectionTimeout, endpoint,
_agencyEndpoints, _supervision, _waitForSync, _supervisionFrequency,
_compactionStepSize)));

View File

@ -45,15 +45,16 @@ class AgencyFeature : virtual public application_features::ApplicationFeature {
private:
bool _activated;
uint64_t _size; // agency size (default: 5)
uint64_t _poolSize;
std::string _agentId;
double _minElectionTimeout; // min election timeout
double _maxElectionTimeout; // max election timeout
std::map<std::string, std::string> _agencyEndpoints; // agency adresses
bool _notify; // interval between retry to slaves
bool _supervision;
bool _waitForSync;
double _supervisionFrequency;
uint64_t _compactionStepSize;
std::vector<std::string> _agencyEndpoints;
public:
consensus::Agent* agent() const { return _agent.get(); }

View File

@ -22,6 +22,7 @@
////////////////////////////////////////////////////////////////////////////////
#include "Agent.h"
#include "GossipCallback.h"
#include "Basics/ConditionLocker.h"
#include "RestServer/DatabaseFeature.h"
@ -48,19 +49,31 @@ Agent::Agent(config_t const& config)
_lastCommitIndex(0),
_spearhead(this),
_readDB(this),
_serveActiveAgents(false),
_serveActiveAgent(false),
_nextCompationAfter(_config.compactionStepSize) {
_state.configure(this);
_constituent.configure(this);
// _confirmed.resize(size(), 0); // agency's size and reset to 0
//_lastHighest.resize(size(), 0);
//_lastSent.resize(size());
}
/// This agent's id
arangodb::consensus::id_t Agent::id() const {
return _config.id;
return config().id;
}
/// Agent's id is set once from state machine
bool Agent::id(arangodb::consensus::id_t const& id) {
MUTEX_LOCKER(mutexLocker, _cfgLock);
_config.id = id;
return true;
}
/// Merge command line and persisted comfigurations
bool Agent::mergeConfiguration(VPackSlice const& persisted) {
MUTEX_LOCKER(mutexLocker, _cfgLock);
return _config.merge(persisted);
}
@ -111,8 +124,9 @@ priv_rpc_ret_t Agent::requestVote(
}
/// Get configuration
config_t const& Agent::config() const {
/// Get copy of momentary configuration
config_t const Agent::config() const {
MUTEX_LOCKER(mutexLocker, _cfgLock);
return _config;
}
@ -318,7 +332,7 @@ priv_rpc_ret_t Agent::sendAppendEntriesRPC(
// Send request
arangodb::ClusterComm::instance()->asyncRequest(
"1", 1, _config.endpoints[follower_id],
"1", 1, _config.pool[follower_id],
arangodb::GeneralRequest::RequestType::POST, path.str(),
std::make_shared<std::string>(builder.toJson()), headerFields,
std::make_shared<AgentCallback>(this, follower_id, highest),
@ -445,7 +459,7 @@ void Agent::run() {
}
// Append entries to followers
for (arangodb::consensus::id_t i = 0; i < size(); ++i) {
for (auto const& i : _config.active) {
if (i != id()) {
sendAppendEntriesRPC(i);
}
@ -580,7 +594,7 @@ void Agent::gossip() {
std::map<std::string, std::string> pool;
{
MUTEX_LOCKER(mutexLocker, _cfgLock);
pool = _config.pool
pool = _config.pool;
}
if (!pool.empty()) {
@ -607,7 +621,7 @@ void Agent::gossip() {
for (auto const& agent : pool) {
arangodb::ClusterComm::instance()->asyncRequest(
"1", 1, pool.second, GeneralRequest::RequestType::POST, path,
"1", 1, agent.second, GeneralRequest::RequestType::POST, path,
std::make_shared<std::string>(word->toString()), headerFields,
std::make_shared<GossipCallback>(), 1.0, true);
}
@ -621,16 +635,16 @@ void Agent::gossip() {
/// Handle incomint gossip
void Agent::gossip(query_t const& word) {
TRI_ASSERT(word->isObject());
TRI_ASSERT(word->slice().isObject());
MUTEX_LOCKER(mutexLocker, _cfgLock);
for (auto const& ent : VPackObjectIterator(word)) {
for (auto const& ent : VPackObjectIterator(word->slice())) {
TRI_ASSERT(ent.value.isString());
auto it = _config.pool.find(ent.key);
auto it = _config.pool.find(ent.key.copyString());
if (it != _config.pool.end()) {
TRI_ASSERT(it->second == ent.value.copyString());
} else {
_config.pool[ent.key] = ent.value;
_config.pool[ent.key.copyString()] = ent.value.copyString();
}
}
@ -645,6 +659,7 @@ void Agent::gossip(query_t const& word) {
bool Agent::activeAgency() {
std::map<std::string, std::string> pool;
std::vector<std::string> active;
{
MUTEX_LOCKER(mutexLocker, _cfgLock);
pool = _config.pool;
@ -655,22 +670,21 @@ bool Agent::activeAgency() {
}
return false;
}
inline static query_t getResponse(
std::string const& endpoint, std::sting const& path, double timeout = 1.0) {
std::string const& endpoint, std::string const& path, double timeout = 1.0) {
query_t ret = nullptr;
auto headerFields =
std::make_unique<std::unordered_map<std::string, std::string>>();
std::unordered_map<std::string, std::string> headerFields;
auto res = arangodb::ClusterComm::instance()->syncRequest(
"1", 1, endpoint, GeneralRequest::RequestType::GET, path, std::string(),
headerFields, 1.0);
if (res.status == CL_COMM_RECEIVED) {
ret = res.result->getBodyVelocyPack();
if (res->status == CL_COMM_RECEIVED) {
ret = res->result->getBodyVelocyPack();
}
return ret;
@ -685,13 +699,33 @@ bool Agent::persistedAgents() {
active = _config.active;
}
auto const& it = active.find(config.id);
// 1 min timeout
std::chrono::seconds timeout(60);
auto const& it = find(active.begin(), active.end(), _config.id);
auto start = std::chrono::system_clock::now();
if (it != active.end()) { // Serve /_api/agency/activeAgents
if (it != active.end()) {
{
MUTEX_LOCKER(mutexLocker, _cfgLock);
_serveActiveAgent = true; // Serve /_api/agency/activeAgents
}
_serveActiveAgents = true;
while (true) {
if (std::chrono::system_clock::now() - start > timeout) {
{
MUTEX_LOCKER(mutexLocker, _cfgLock);
_config.active.clear();
}
return false;
}
}
{
MUTEX_LOCKER(mutexLocker, _cfgLock);
_serveActiveAgent = false; // Unserve /_api/agency/activeAgents
}
} else {
@ -710,27 +744,39 @@ bool Agent::persistedAgents() {
<< "Got response from a persisted agent with configuration "
<< slice.toJson();
MUTEX_LOCKER(mutexLocker, _cfgLock);
_config.override(res);
_config.override(slice);
}
}
if (60.0e9 > std::chrono::system_clock::now() - start) { // 1 min timeout
if (std::chrono::system_clock::now() - start > timeout) {
{
MUTEX_LOCKER(mutexLocker, _cfgLock);
_config.active.clear();
}
return false;
}
}
}
_serveActiveAgents = false;
return false;
}
/// Do we serve agentStartupSequence
bool Agent::serveActiveAgent() {
return _serveActiveAgent;
}
/// Initial inception of the agency world
void Agent::inception() {
auto start = std::chrono::system_clock::now(); //
std::chrono::seconds timeout(300);
if (persistedAgents()) {
return;
@ -738,13 +784,19 @@ void Agent::inception() {
CONDITION_LOCKER(guard, _configCV);
while (booting) {
while (booting()) {
if (activeAgency()) {
return;
}
_configCV.wait(1000);
if (std::chrono::system_clock::now() - start > timeout) {
LOG_TOPIC(ERR, Logger::AGENCY) <<
"Failed to find complete pool of agents.";
FATAL_ERROR_EXIT();
}
}

View File

@ -56,7 +56,7 @@ class Agent : public arangodb::Thread {
index_t, query_t const&);
/// @brief Provide configuration
config_t const& config() const;
config_t const config() const;
/// @brief Start thread
bool start();
@ -106,13 +106,16 @@ class Agent : public arangodb::Thread {
void run() override final;
/// @brief Are we still booting?
void booting();
bool booting();
/// @brief Gossip out
void gossip();
/// @brief Gossip in
void gossip(query_t const& word);
/// @brief Persisted agents
bool persistedAgents();
/// @brief Gossip in
bool activeAgency();
@ -147,11 +150,22 @@ class Agent : public arangodb::Thread {
/// @brief Get spearhead store
Store const& spearhead() const;
bool serveActiveAgent();
/// State reads persisted state and prepares the agent
friend class State;
private:
Agent& operator=(VPackSlice const&);
/// @brief Get current term
bool id(arangodb::consensus::id_t const&);
/// @brief Get current term
bool mergeConfiguration(VPackSlice const&);
/// @brief Leader ID
void lastCommitted(arangodb::consensus::index_t);
@ -200,10 +214,10 @@ class Agent : public arangodb::Thread {
std::map<std::string, index_t> _lastHighest;
std::map<std::string, TimePoint> _lastSent;
arangodb::Mutex _ioLock; /**< @brief Read/Write lock */
arangodb::Mutex _cfgLock; /**< @brief configuration gossip lock */
mutable arangodb::Mutex _cfgLock; /**< @brief configuration gossip lock */
/// @brief Server active agents rest handler
bool _serveActiveAgents;
bool _serveActiveAgent;
/// @brief Next compaction after
arangodb::consensus::index_t _nextCompationAfter;

View File

@ -211,7 +211,7 @@ struct config_t {
/// @brief merge from persisted configuration
void merge (VPackSlice const& conf) {
bool merge (VPackSlice const& conf) {
id = conf.get(idStr).copyString(); // I get my id
pool[id] = endpoint; // Register my endpoint with it
@ -349,6 +349,8 @@ struct config_t {
}
LOG_TOPIC(DEBUG, Logger::AGENCY) << ss.str();
return true;
}
};

View File

@ -43,7 +43,6 @@
#include "Utils/StandaloneTransactionContext.h"
#include "VocBase/collection.h"
#include "VocBase/vocbase.h"
#include "Agency/NotifierThread.h"
using namespace arangodb::consensus;
using namespace arangodb::rest;
@ -82,8 +81,8 @@ Constituent::Constituent()
_id(0),
_role(FOLLOWER),
_agent(nullptr),
_votedFor(NO_LEADER),
_notifier(nullptr) {}
_votedFor(NO_LEADER)
{}
/// Shutdown if not already
@ -92,12 +91,6 @@ Constituent::~Constituent() {
}
/// Configuration
config_t const& Constituent::config() const {
return _agent->config();
}
/// Wait for sync
bool Constituent::waitForSync() const {
return _agent->config().waitForSync;
@ -140,7 +133,7 @@ void Constituent::term(term_t t) {
i_str << std::setw(20) << std::setfill('0') << t;
body.add("_key", Value(i_str.str()));
body.add("term", Value(t));
body.add("voted_for", Value((uint32_t)_votedFor));
body.add("voted_for", Value(_votedFor));
body.close();
TRI_ASSERT(_vocbase != nullptr);
@ -191,7 +184,7 @@ void Constituent::follow(term_t t) {
/// Become leader
void Constituent::lead(std::vector<bool> const& votes) {
void Constituent::lead(std::map<arangodb::consensus::id_t, bool> const& votes) {
{
MUTEX_LOCKER(guard, _castLock);
@ -207,7 +200,7 @@ void Constituent::lead(std::vector<bool> const& votes) {
std::stringstream ss;
ss << _id << ": Converted to leader in term " << _term << " with votes (";
for (auto const& vote : votes) {
ss << vote;
ss << vote.second;
}
ss << ")";
@ -260,54 +253,16 @@ arangodb::consensus::id_t Constituent::leaderID() const {
/// Agency size
size_t Constituent::size() const {
return config().size();
return _agent->config().size();
}
/// Get endpoint to an id
std::string const& Constituent::endpoint(arangodb::consensus::id_t id) const {
return config().endpoints[id];
return _agent->config().pool.at(id);
}
/// Get all endpoints
std::vector<std::string> const& Constituent::endpoints() const {
return config().endpoints;
}
/// Notify peers of updated endpoints
void Constituent::notifyAll() {
// Send request to all but myself
std::vector<std::string> toNotify;
for (arangodb::consensus::id_t i = 0; i < size(); ++i) {
if (i != _id) {
toNotify.push_back(endpoint(i));
}
}
// Body contains endpoints list
auto body = std::make_shared<VPackBuilder>();
body->openObject();
body->add("endpoints", VPackValue(VPackValueType::Array));
for (auto const& i : endpoints()) {
body->add(Value(i));
}
body->close();
body->close();
// Last process notifies everyone
std::stringstream path;
path << "/_api/agency_priv/notifyAll?term=" << _term << "&agencyId=" << _id;
_notifier = std::make_unique<NotifierThread>(path.str(), body, toNotify);
_notifier->start();
}
/// @brief Vote
bool Constituent::vote(term_t term, arangodb::consensus::id_t id,
index_t prevLogIndex, term_t prevLogTerm,
@ -351,49 +306,53 @@ bool Constituent::vote(term_t term, arangodb::consensus::id_t id,
/// @brief Call to election
void Constituent::callElection() {
std::vector<bool> votes(size(), false);
std::map<arangodb::consensus::id_t,bool> votes;
std::vector<std::string> active = _agent->config().active; // Get copy of active
votes.at(_id) = true; // vote for myself
votes[_id] = true; // vote for myself
_cast = true;
_votedFor = _id;
_leaderID = NO_LEADER;
this->term(_term + 1); // raise my term
std::string body;
std::vector<OperationID> operationIDs(config().endpoints.size());
std::map<arangodb::consensus::id_t,OperationID> operationIDs;
std::stringstream path;
path << "/_api/agency_priv/requestVote?term=" << _term
<< "&candidateId=" << _id << "&prevLogIndex=" << _agent->lastLog().index
<< "&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 (arangodb::consensus::id_t i = 0; i < config().endpoints.size(); ++i) {
if (i != _id && endpoint(i) != "") {
for (auto const& i : active) {
if (i != _id) {
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, 0.75*config().minPing, true, 0.5*config().minPing);
std::make_unique<std::unordered_map<std::string, std::string>>();
operationIDs[i] = ClusterComm::instance()->asyncRequest(
"1", 1, _agent->config().pool.at(i),
GeneralRequest::RequestType::GET, path.str(),
std::make_shared<std::string>(body), headerFields,
nullptr, respTimeout, true, initTimeout);
}
}
// Wait randomized timeout
std::this_thread::sleep_for(
sleepFor(.5 * config().minPing, .8 * config().minPing));
std::this_thread::sleep_for(sleepFor(initTimeout, respTimeout));
// 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) != "") {
for (const auto& i : active) {
if (i != _id) {
ClusterCommResult res =
arangodb::ClusterComm::instance()->enquire(operationIDs[i]);
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");
res = ClusterComm::instance()->wait("1", 1, operationIDs[i], "1");
std::shared_ptr<Builder> body = res.result->getBodyVelocyPack();
if (body->isEmpty()) { // body empty
continue;
@ -406,7 +365,7 @@ void Constituent::callElection() {
follow(t);
break;
}
votes[i] = slc.get("voteGranted").getBool(); // Get vote
votes[i] = slc.get("voteGranted").getBool(); // Get vote
}
}
}
@ -415,11 +374,11 @@ void Constituent::callElection() {
}
}
}
// Count votes
size_t yea = 0;
for (size_t i = 0; i < size(); ++i) {
if (votes[i]) {
for (auto const& i : votes) {
if (i.second) {
yea++;
}
}
@ -437,12 +396,11 @@ void Constituent::callElection() {
/// Start clean shutdown
void Constituent::beginShutdown() {
_notifier.reset();
Thread::beginShutdown();
CONDITION_LOCKER(guard, _cv);
guard.broadcast();
}
@ -482,11 +440,10 @@ void Constituent::run() {
for (auto const& i : VPackArrayIterator(result)) {
try {
_term = i.get("term").getUInt();
_votedFor =
static_cast<decltype(_votedFor)>(i.get("voted_for").getUInt());
_votedFor = i.get("voted_for").copyString();
} catch (std::exception const&) {
LOG_TOPIC(ERR, Logger::AGENCY)
<< "Persisted election entries corrupt! Defaulting term,vote (0,0)";
<< "Persisted election entries corrupt! Defaulting term,vote (0,0)";
}
}
}
@ -496,15 +453,16 @@ void Constituent::run() {
if (_role == FOLLOWER) {
bool cast = false;
{
MUTEX_LOCKER(guard, _castLock);
_cast = false; // New round set not cast vote
}
int32_t left = static_cast<int32_t>(1000000.0 * config().minPing), right = static_cast<int32_t>(1000000.0 * config().maxPing);
int32_t left = static_cast<int32_t>(1000000.0 * _agent->config().minPing),
right = static_cast<int32_t>(1000000.0 * _agent->config().maxPing);
long rand_wait = static_cast<long>(RandomGenerator::interval(left, right));
{
CONDITION_LOCKER(guardv, _cv);
_cv.wait(rand_wait);
@ -522,7 +480,7 @@ void Constituent::run() {
} else if (_role == CANDIDATE) {
callElection(); // Run for office
} else {
int32_t left = static_cast<int32_t>(100000.0 * config().minPing);
int32_t left = static_cast<int32_t>(100000.0 * _agent->config().minPing);
long rand_wait = static_cast<long>(left);
{
CONDITION_LOCKER(guardv, _cv);

View File

@ -26,7 +26,6 @@
#include "AgencyCommon.h"
#include "AgentConfiguration.h"
#include "NotifierThread.h"
#include "Basics/Common.h"
#include "Basics/Thread.h"
@ -109,7 +108,7 @@ class Constituent : public arangodb::Thread {
void candidate();
/// @brief Become leader
void lead(std::vector<bool> const&);
void lead(std::map<arangodb::consensus::id_t,bool> const&);
/// @brief Call for vote (by leader or candidates after timeout)
void callElection();
@ -120,11 +119,6 @@ class Constituent : public arangodb::Thread {
/// @brief Wait for sync
bool waitForSync() const;
/// @brief Notify everyone, that we are good to go.
/// This is the task of the last process starting up.
/// Will be taken care of by gossip
void notifyAll();
/// @brief Sleep for how long
duration_t sleepFor(double, double);
@ -141,8 +135,6 @@ class Constituent : public arangodb::Thread {
Agent* _agent; /**< @brief My boss */
arangodb::consensus::id_t _votedFor;
std::unique_ptr<NotifierThread> _notifier;
arangodb::basics::ConditionVariable _cv; // agency callbacks
mutable arangodb::Mutex _castLock;
};

View File

@ -74,7 +74,7 @@ inline RestHandler::status RestAgencyHandler::reportUnknownMethod() {
void RestAgencyHandler::redirectRequest(arangodb::consensus::id_t leaderId) {
try {
std::string url =
Endpoint::uriForm(_agent->config().pool[leaderId]) +
Endpoint::uriForm(_agent->config().pool.at(leaderId)) +
_request->requestPath();
setResponseCode(GeneralResponse::ResponseCode::TEMPORARY_REDIRECT);
_response->setHeaderNC(StaticStrings::Location, url);
@ -140,7 +140,7 @@ RestHandler::status RestAgencyHandler::handleWrite() {
return status::DONE;
}
while(_agent->size() > 1 && _agent->leaderID() = NO_LEADER) {
while(_agent->size() > 1 && _agent->leaderID() == "") {
std::this_thread::sleep_for(duration_t(100));
}
@ -215,7 +215,7 @@ inline RestHandler::status RestAgencyHandler::handleRead() {
return status::DONE;
}
while(_agent->size() > 1 && _agent->leaderID() > 100) {
while(_agent->size() > 1 && _agent->leaderID() == "") {
std::this_thread::sleep_for(duration_t(100));
}

View File

@ -130,11 +130,11 @@ RestHandler::status RestAgencyPrivHandler::execute() {
return reportBadQuery(); // bad query
}
} else if (_request->suffix()[0] == "gossip") {
if (_agent->activeAgentStartupSequence()) {
if (_agent->serveActiveAgent()) { // only during startup (see Agent)
arangodb::velocypack::Options options;
query_t query = _request->toVelocyPackBuilderPtr(&options);
_agent->gossip(query);
} else {
} else { // Gone!
return reportGone();
}
}else {

View File

@ -505,9 +505,9 @@ bool State::loadOrPersistConfiguration() {
if (result.isArray() && result.length()) { // We already have a persisted conf
try (config.merge(result)) {
0;
} catch (std::exception const& e) {
try {
_agent->mergeConfiguration(result);
} catch (std::exception const& e) {
LOG_TOPIC(ERR, Logger::AGENCY)
<< "Failed to merge persisted configuration into runtime configuration:"
<< e.what();
@ -519,7 +519,7 @@ bool State::loadOrPersistConfiguration() {
LOG_TOPIC(DEBUG, Logger::AGENCY) << "New agency!";
TRI_ASSERT(_agent != nullptr);
_agent->config().id = to_string(boost::uuids::random_generator()());
_agent->id(to_string(boost::uuids::random_generator()()));
auto transactionContext =
std::make_shared<StandaloneTransactionContext>(_vocbase);
@ -535,7 +535,7 @@ bool State::loadOrPersistConfiguration() {
try {
result = trx.insert(
"configuration", _agent.config().toBuilder().slice(), _options);
"configuration", _agent->config().toBuilder()->slice(), _options);
} catch (std::exception const& e) {
LOG_TOPIC(ERR, Logger::AGENCY)
<< "Failed to persist configuration entry:" << e.what();
@ -549,7 +549,6 @@ bool State::loadOrPersistConfiguration() {
}
return true;
}