mirror of https://gitee.com/bigwinds/arangodb
Agency compiling again
This commit is contained in:
parent
39ca341097
commit
4759b2a434
|
@ -70,7 +70,7 @@ void AgencyFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
|
||||||
new UInt64Parameter(&_size));
|
new UInt64Parameter(&_size));
|
||||||
|
|
||||||
options->addOption("--agency.pool-size", "number of agent pool",
|
options->addOption("--agency.pool-size", "number of agent pool",
|
||||||
new UInt64Parameter(&_pool_size));
|
new UInt64Parameter(&_poolSize));
|
||||||
|
|
||||||
options->addOption(
|
options->addOption(
|
||||||
"--agency.election-timeout-min",
|
"--agency.election-timeout-min",
|
||||||
|
@ -122,7 +122,7 @@ void AgencyFeature::validateOptions(std::shared_ptr<ProgramOptions> options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Agency size
|
// Agency size
|
||||||
if (_pool_size < _size) {
|
if (_poolSize < _size) {
|
||||||
LOG_TOPIC(FATAL, Logger::AGENCY)
|
LOG_TOPIC(FATAL, Logger::AGENCY)
|
||||||
<< "AGENCY: agency pool size must be larger than agency size.";
|
<< "AGENCY: agency pool size must be larger than agency size.";
|
||||||
FATAL_ERROR_EXIT();
|
FATAL_ERROR_EXIT();
|
||||||
|
@ -191,7 +191,7 @@ void AgencyFeature::start() {
|
||||||
_agent.reset(
|
_agent.reset(
|
||||||
new consensus::Agent(
|
new consensus::Agent(
|
||||||
consensus::config_t(
|
consensus::config_t(
|
||||||
std::string(), _minElectionTimeout, _maxElectionTimeout, endpoint,
|
_size, _poolSize, _minElectionTimeout, _maxElectionTimeout, endpoint,
|
||||||
_agencyEndpoints, _supervision, _waitForSync, _supervisionFrequency,
|
_agencyEndpoints, _supervision, _waitForSync, _supervisionFrequency,
|
||||||
_compactionStepSize)));
|
_compactionStepSize)));
|
||||||
|
|
||||||
|
|
|
@ -45,15 +45,16 @@ class AgencyFeature : virtual public application_features::ApplicationFeature {
|
||||||
private:
|
private:
|
||||||
bool _activated;
|
bool _activated;
|
||||||
uint64_t _size; // agency size (default: 5)
|
uint64_t _size; // agency size (default: 5)
|
||||||
|
uint64_t _poolSize;
|
||||||
std::string _agentId;
|
std::string _agentId;
|
||||||
double _minElectionTimeout; // min election timeout
|
double _minElectionTimeout; // min election timeout
|
||||||
double _maxElectionTimeout; // max election timeout
|
double _maxElectionTimeout; // max election timeout
|
||||||
std::map<std::string, std::string> _agencyEndpoints; // agency adresses
|
|
||||||
bool _notify; // interval between retry to slaves
|
bool _notify; // interval between retry to slaves
|
||||||
bool _supervision;
|
bool _supervision;
|
||||||
bool _waitForSync;
|
bool _waitForSync;
|
||||||
double _supervisionFrequency;
|
double _supervisionFrequency;
|
||||||
uint64_t _compactionStepSize;
|
uint64_t _compactionStepSize;
|
||||||
|
std::vector<std::string> _agencyEndpoints;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
consensus::Agent* agent() const { return _agent.get(); }
|
consensus::Agent* agent() const { return _agent.get(); }
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "Agent.h"
|
#include "Agent.h"
|
||||||
|
#include "GossipCallback.h"
|
||||||
|
|
||||||
#include "Basics/ConditionLocker.h"
|
#include "Basics/ConditionLocker.h"
|
||||||
#include "RestServer/DatabaseFeature.h"
|
#include "RestServer/DatabaseFeature.h"
|
||||||
|
@ -48,19 +49,31 @@ Agent::Agent(config_t const& config)
|
||||||
_lastCommitIndex(0),
|
_lastCommitIndex(0),
|
||||||
_spearhead(this),
|
_spearhead(this),
|
||||||
_readDB(this),
|
_readDB(this),
|
||||||
_serveActiveAgents(false),
|
_serveActiveAgent(false),
|
||||||
_nextCompationAfter(_config.compactionStepSize) {
|
_nextCompationAfter(_config.compactionStepSize) {
|
||||||
_state.configure(this);
|
_state.configure(this);
|
||||||
_constituent.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
|
/// This agent's id
|
||||||
arangodb::consensus::id_t Agent::id() const {
|
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
|
/// Get copy of momentary configuration
|
||||||
config_t const& Agent::config() const {
|
config_t const Agent::config() const {
|
||||||
|
MUTEX_LOCKER(mutexLocker, _cfgLock);
|
||||||
return _config;
|
return _config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,7 +332,7 @@ priv_rpc_ret_t Agent::sendAppendEntriesRPC(
|
||||||
|
|
||||||
// Send request
|
// Send request
|
||||||
arangodb::ClusterComm::instance()->asyncRequest(
|
arangodb::ClusterComm::instance()->asyncRequest(
|
||||||
"1", 1, _config.endpoints[follower_id],
|
"1", 1, _config.pool[follower_id],
|
||||||
arangodb::GeneralRequest::RequestType::POST, path.str(),
|
arangodb::GeneralRequest::RequestType::POST, path.str(),
|
||||||
std::make_shared<std::string>(builder.toJson()), headerFields,
|
std::make_shared<std::string>(builder.toJson()), headerFields,
|
||||||
std::make_shared<AgentCallback>(this, follower_id, highest),
|
std::make_shared<AgentCallback>(this, follower_id, highest),
|
||||||
|
@ -445,7 +459,7 @@ void Agent::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append entries to followers
|
// Append entries to followers
|
||||||
for (arangodb::consensus::id_t i = 0; i < size(); ++i) {
|
for (auto const& i : _config.active) {
|
||||||
if (i != id()) {
|
if (i != id()) {
|
||||||
sendAppendEntriesRPC(i);
|
sendAppendEntriesRPC(i);
|
||||||
}
|
}
|
||||||
|
@ -580,7 +594,7 @@ void Agent::gossip() {
|
||||||
std::map<std::string, std::string> pool;
|
std::map<std::string, std::string> pool;
|
||||||
{
|
{
|
||||||
MUTEX_LOCKER(mutexLocker, _cfgLock);
|
MUTEX_LOCKER(mutexLocker, _cfgLock);
|
||||||
pool = _config.pool
|
pool = _config.pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pool.empty()) {
|
if (!pool.empty()) {
|
||||||
|
@ -607,7 +621,7 @@ void Agent::gossip() {
|
||||||
|
|
||||||
for (auto const& agent : pool) {
|
for (auto const& agent : pool) {
|
||||||
arangodb::ClusterComm::instance()->asyncRequest(
|
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<std::string>(word->toString()), headerFields,
|
||||||
std::make_shared<GossipCallback>(), 1.0, true);
|
std::make_shared<GossipCallback>(), 1.0, true);
|
||||||
}
|
}
|
||||||
|
@ -621,16 +635,16 @@ void Agent::gossip() {
|
||||||
/// Handle incomint gossip
|
/// Handle incomint gossip
|
||||||
void Agent::gossip(query_t const& word) {
|
void Agent::gossip(query_t const& word) {
|
||||||
|
|
||||||
TRI_ASSERT(word->isObject());
|
TRI_ASSERT(word->slice().isObject());
|
||||||
|
|
||||||
MUTEX_LOCKER(mutexLocker, _cfgLock);
|
MUTEX_LOCKER(mutexLocker, _cfgLock);
|
||||||
for (auto const& ent : VPackObjectIterator(word)) {
|
for (auto const& ent : VPackObjectIterator(word->slice())) {
|
||||||
TRI_ASSERT(ent.value.isString());
|
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()) {
|
if (it != _config.pool.end()) {
|
||||||
TRI_ASSERT(it->second == ent.value.copyString());
|
TRI_ASSERT(it->second == ent.value.copyString());
|
||||||
} else {
|
} 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() {
|
bool Agent::activeAgency() {
|
||||||
|
|
||||||
std::map<std::string, std::string> pool;
|
std::map<std::string, std::string> pool;
|
||||||
|
std::vector<std::string> active;
|
||||||
{
|
{
|
||||||
MUTEX_LOCKER(mutexLocker, _cfgLock);
|
MUTEX_LOCKER(mutexLocker, _cfgLock);
|
||||||
pool = _config.pool;
|
pool = _config.pool;
|
||||||
|
@ -655,22 +670,21 @@ bool Agent::activeAgency() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static query_t getResponse(
|
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;
|
query_t ret = nullptr;
|
||||||
|
std::unordered_map<std::string, std::string> headerFields;
|
||||||
auto headerFields =
|
|
||||||
std::make_unique<std::unordered_map<std::string, std::string>>();
|
|
||||||
|
|
||||||
auto res = arangodb::ClusterComm::instance()->syncRequest(
|
auto res = arangodb::ClusterComm::instance()->syncRequest(
|
||||||
"1", 1, endpoint, GeneralRequest::RequestType::GET, path, std::string(),
|
"1", 1, endpoint, GeneralRequest::RequestType::GET, path, std::string(),
|
||||||
headerFields, 1.0);
|
headerFields, 1.0);
|
||||||
|
|
||||||
if (res.status == CL_COMM_RECEIVED) {
|
if (res->status == CL_COMM_RECEIVED) {
|
||||||
ret = res.result->getBodyVelocyPack();
|
ret = res->result->getBodyVelocyPack();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -685,13 +699,33 @@ bool Agent::persistedAgents() {
|
||||||
active = _config.active;
|
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();
|
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) {
|
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 {
|
} else {
|
||||||
|
@ -710,27 +744,39 @@ bool Agent::persistedAgents() {
|
||||||
<< "Got response from a persisted agent with configuration "
|
<< "Got response from a persisted agent with configuration "
|
||||||
<< slice.toJson();
|
<< slice.toJson();
|
||||||
MUTEX_LOCKER(mutexLocker, _cfgLock);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_serveActiveAgents = false;
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Do we serve agentStartupSequence
|
||||||
|
bool Agent::serveActiveAgent() {
|
||||||
|
return _serveActiveAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Initial inception of the agency world
|
/// Initial inception of the agency world
|
||||||
void Agent::inception() {
|
void Agent::inception() {
|
||||||
|
|
||||||
auto start = std::chrono::system_clock::now(); //
|
auto start = std::chrono::system_clock::now(); //
|
||||||
|
std::chrono::seconds timeout(300);
|
||||||
|
|
||||||
if (persistedAgents()) {
|
if (persistedAgents()) {
|
||||||
return;
|
return;
|
||||||
|
@ -738,7 +784,7 @@ void Agent::inception() {
|
||||||
|
|
||||||
CONDITION_LOCKER(guard, _configCV);
|
CONDITION_LOCKER(guard, _configCV);
|
||||||
|
|
||||||
while (booting) {
|
while (booting()) {
|
||||||
|
|
||||||
if (activeAgency()) {
|
if (activeAgency()) {
|
||||||
return;
|
return;
|
||||||
|
@ -746,6 +792,12 @@ void Agent::inception() {
|
||||||
|
|
||||||
_configCV.wait(1000);
|
_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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ class Agent : public arangodb::Thread {
|
||||||
index_t, query_t const&);
|
index_t, query_t const&);
|
||||||
|
|
||||||
/// @brief Provide configuration
|
/// @brief Provide configuration
|
||||||
config_t const& config() const;
|
config_t const config() const;
|
||||||
|
|
||||||
/// @brief Start thread
|
/// @brief Start thread
|
||||||
bool start();
|
bool start();
|
||||||
|
@ -106,7 +106,7 @@ class Agent : public arangodb::Thread {
|
||||||
void run() override final;
|
void run() override final;
|
||||||
|
|
||||||
/// @brief Are we still booting?
|
/// @brief Are we still booting?
|
||||||
void booting();
|
bool booting();
|
||||||
|
|
||||||
/// @brief Gossip out
|
/// @brief Gossip out
|
||||||
void gossip();
|
void gossip();
|
||||||
|
@ -114,6 +114,9 @@ class Agent : public arangodb::Thread {
|
||||||
/// @brief Gossip in
|
/// @brief Gossip in
|
||||||
void gossip(query_t const& word);
|
void gossip(query_t const& word);
|
||||||
|
|
||||||
|
/// @brief Persisted agents
|
||||||
|
bool persistedAgents();
|
||||||
|
|
||||||
/// @brief Gossip in
|
/// @brief Gossip in
|
||||||
bool activeAgency();
|
bool activeAgency();
|
||||||
|
|
||||||
|
@ -147,11 +150,22 @@ class Agent : public arangodb::Thread {
|
||||||
/// @brief Get spearhead store
|
/// @brief Get spearhead store
|
||||||
Store const& spearhead() const;
|
Store const& spearhead() const;
|
||||||
|
|
||||||
|
bool serveActiveAgent();
|
||||||
|
|
||||||
|
/// State reads persisted state and prepares the agent
|
||||||
friend class State;
|
friend class State;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Agent& operator=(VPackSlice const&);
|
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
|
/// @brief Leader ID
|
||||||
void lastCommitted(arangodb::consensus::index_t);
|
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, index_t> _lastHighest;
|
||||||
std::map<std::string, TimePoint> _lastSent;
|
std::map<std::string, TimePoint> _lastSent;
|
||||||
arangodb::Mutex _ioLock; /**< @brief Read/Write lock */
|
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
|
/// @brief Server active agents rest handler
|
||||||
bool _serveActiveAgents;
|
bool _serveActiveAgent;
|
||||||
|
|
||||||
/// @brief Next compaction after
|
/// @brief Next compaction after
|
||||||
arangodb::consensus::index_t _nextCompationAfter;
|
arangodb::consensus::index_t _nextCompationAfter;
|
||||||
|
|
|
@ -211,7 +211,7 @@ struct config_t {
|
||||||
|
|
||||||
|
|
||||||
/// @brief merge from persisted configuration
|
/// @brief merge from persisted configuration
|
||||||
void merge (VPackSlice const& conf) {
|
bool merge (VPackSlice const& conf) {
|
||||||
|
|
||||||
id = conf.get(idStr).copyString(); // I get my id
|
id = conf.get(idStr).copyString(); // I get my id
|
||||||
pool[id] = endpoint; // Register my endpoint with it
|
pool[id] = endpoint; // Register my endpoint with it
|
||||||
|
@ -349,6 +349,8 @@ struct config_t {
|
||||||
}
|
}
|
||||||
LOG_TOPIC(DEBUG, Logger::AGENCY) << ss.str();
|
LOG_TOPIC(DEBUG, Logger::AGENCY) << ss.str();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,7 +43,6 @@
|
||||||
#include "Utils/StandaloneTransactionContext.h"
|
#include "Utils/StandaloneTransactionContext.h"
|
||||||
#include "VocBase/collection.h"
|
#include "VocBase/collection.h"
|
||||||
#include "VocBase/vocbase.h"
|
#include "VocBase/vocbase.h"
|
||||||
#include "Agency/NotifierThread.h"
|
|
||||||
|
|
||||||
using namespace arangodb::consensus;
|
using namespace arangodb::consensus;
|
||||||
using namespace arangodb::rest;
|
using namespace arangodb::rest;
|
||||||
|
@ -82,8 +81,8 @@ Constituent::Constituent()
|
||||||
_id(0),
|
_id(0),
|
||||||
_role(FOLLOWER),
|
_role(FOLLOWER),
|
||||||
_agent(nullptr),
|
_agent(nullptr),
|
||||||
_votedFor(NO_LEADER),
|
_votedFor(NO_LEADER)
|
||||||
_notifier(nullptr) {}
|
{}
|
||||||
|
|
||||||
|
|
||||||
/// Shutdown if not already
|
/// Shutdown if not already
|
||||||
|
@ -92,12 +91,6 @@ Constituent::~Constituent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Configuration
|
|
||||||
config_t const& Constituent::config() const {
|
|
||||||
return _agent->config();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Wait for sync
|
/// Wait for sync
|
||||||
bool Constituent::waitForSync() const {
|
bool Constituent::waitForSync() const {
|
||||||
return _agent->config().waitForSync;
|
return _agent->config().waitForSync;
|
||||||
|
@ -140,7 +133,7 @@ void Constituent::term(term_t t) {
|
||||||
i_str << std::setw(20) << std::setfill('0') << t;
|
i_str << std::setw(20) << std::setfill('0') << t;
|
||||||
body.add("_key", Value(i_str.str()));
|
body.add("_key", Value(i_str.str()));
|
||||||
body.add("term", Value(t));
|
body.add("term", Value(t));
|
||||||
body.add("voted_for", Value((uint32_t)_votedFor));
|
body.add("voted_for", Value(_votedFor));
|
||||||
body.close();
|
body.close();
|
||||||
|
|
||||||
TRI_ASSERT(_vocbase != nullptr);
|
TRI_ASSERT(_vocbase != nullptr);
|
||||||
|
@ -191,7 +184,7 @@ void Constituent::follow(term_t t) {
|
||||||
|
|
||||||
|
|
||||||
/// Become leader
|
/// 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);
|
MUTEX_LOCKER(guard, _castLock);
|
||||||
|
@ -207,7 +200,7 @@ void Constituent::lead(std::vector<bool> const& votes) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << _id << ": Converted to leader in term " << _term << " with votes (";
|
ss << _id << ": Converted to leader in term " << _term << " with votes (";
|
||||||
for (auto const& vote : votes) {
|
for (auto const& vote : votes) {
|
||||||
ss << vote;
|
ss << vote.second;
|
||||||
}
|
}
|
||||||
ss << ")";
|
ss << ")";
|
||||||
|
|
||||||
|
@ -260,54 +253,16 @@ arangodb::consensus::id_t Constituent::leaderID() const {
|
||||||
|
|
||||||
/// Agency size
|
/// Agency size
|
||||||
size_t Constituent::size() const {
|
size_t Constituent::size() const {
|
||||||
return config().size();
|
return _agent->config().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Get endpoint to an id
|
/// Get endpoint to an id
|
||||||
std::string const& Constituent::endpoint(arangodb::consensus::id_t id) const {
|
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
|
/// @brief Vote
|
||||||
bool Constituent::vote(term_t term, arangodb::consensus::id_t id,
|
bool Constituent::vote(term_t term, arangodb::consensus::id_t id,
|
||||||
index_t prevLogIndex, term_t prevLogTerm,
|
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
|
/// @brief Call to election
|
||||||
void Constituent::callElection() {
|
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;
|
_cast = true;
|
||||||
_votedFor = _id;
|
_votedFor = _id;
|
||||||
_leaderID = NO_LEADER;
|
_leaderID = NO_LEADER;
|
||||||
this->term(_term + 1); // raise my term
|
this->term(_term + 1); // raise my term
|
||||||
|
|
||||||
std::string body;
|
std::string body;
|
||||||
std::vector<OperationID> operationIDs(config().endpoints.size());
|
std::map<arangodb::consensus::id_t,OperationID> operationIDs;
|
||||||
std::stringstream path;
|
std::stringstream path;
|
||||||
|
|
||||||
path << "/_api/agency_priv/requestVote?term=" << _term
|
path << "/_api/agency_priv/requestVote?term=" << _term
|
||||||
<< "&candidateId=" << _id << "&prevLogIndex=" << _agent->lastLog().index
|
<< "&candidateId=" << _id
|
||||||
|
<< "&prevLogIndex=" << _agent->lastLog().index
|
||||||
<< "&prevLogTerm=" << _agent->lastLog().term;
|
<< "&prevLogTerm=" << _agent->lastLog().term;
|
||||||
|
|
||||||
|
double minPing = _agent->config().minPing;
|
||||||
|
|
||||||
|
double respTimeout = 0.9*minPing;
|
||||||
|
double initTimeout = 0.5*minPing;
|
||||||
|
|
||||||
// Ask everyone for their vote
|
// Ask everyone for their vote
|
||||||
for (arangodb::consensus::id_t i = 0; i < config().endpoints.size(); ++i) {
|
for (auto const& i : active) {
|
||||||
if (i != _id && endpoint(i) != "") {
|
if (i != _id) {
|
||||||
auto headerFields =
|
auto headerFields =
|
||||||
std::make_unique<std::unordered_map<std::string, std::string>>();
|
std::make_unique<std::unordered_map<std::string, std::string>>();
|
||||||
operationIDs[i] = arangodb::ClusterComm::instance()->asyncRequest(
|
operationIDs[i] = ClusterComm::instance()->asyncRequest(
|
||||||
"1", 1, config().endpoints[i], GeneralRequest::RequestType::GET,
|
"1", 1, _agent->config().pool.at(i),
|
||||||
path.str(), std::make_shared<std::string>(body), headerFields,
|
GeneralRequest::RequestType::GET, path.str(),
|
||||||
nullptr, 0.75*config().minPing, true, 0.5*config().minPing);
|
std::make_shared<std::string>(body), headerFields,
|
||||||
|
nullptr, respTimeout, true, initTimeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait randomized timeout
|
// Wait randomized timeout
|
||||||
std::this_thread::sleep_for(
|
std::this_thread::sleep_for(sleepFor(initTimeout, respTimeout));
|
||||||
sleepFor(.5 * config().minPing, .8 * config().minPing));
|
|
||||||
|
|
||||||
// Collect votes
|
// Collect votes
|
||||||
// FIXME: This code can be improved: One can wait for an arbitrary
|
for (const auto& i : active) {
|
||||||
// result by creating a coordinatorID and waiting for a pattern.
|
if (i != _id) {
|
||||||
for (arangodb::consensus::id_t i = 0; i < config().endpoints.size(); ++i) {
|
|
||||||
if (i != _id && endpoint(i) != "") {
|
|
||||||
ClusterCommResult res =
|
ClusterCommResult res =
|
||||||
arangodb::ClusterComm::instance()->enquire(operationIDs[i]);
|
arangodb::ClusterComm::instance()->enquire(operationIDs[i]);
|
||||||
|
|
||||||
if (res.status == CL_COMM_SENT) { // Request successfully sent
|
if (res.status == CL_COMM_SENT) { // Request successfully sent
|
||||||
res = arangodb::ClusterComm::instance()->wait(
|
res = ClusterComm::instance()->wait("1", 1, operationIDs[i], "1");
|
||||||
"1", 1, operationIDs[i], "1");
|
|
||||||
std::shared_ptr<Builder> body = res.result->getBodyVelocyPack();
|
std::shared_ptr<Builder> body = res.result->getBodyVelocyPack();
|
||||||
if (body->isEmpty()) { // body empty
|
if (body->isEmpty()) { // body empty
|
||||||
continue;
|
continue;
|
||||||
|
@ -406,7 +365,7 @@ void Constituent::callElection() {
|
||||||
follow(t);
|
follow(t);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
votes[i] = slc.get("voteGranted").getBool(); // Get vote
|
votes[i] = slc.get("voteGranted").getBool(); // Get vote
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -418,8 +377,8 @@ void Constituent::callElection() {
|
||||||
|
|
||||||
// Count votes
|
// Count votes
|
||||||
size_t yea = 0;
|
size_t yea = 0;
|
||||||
for (size_t i = 0; i < size(); ++i) {
|
for (auto const& i : votes) {
|
||||||
if (votes[i]) {
|
if (i.second) {
|
||||||
yea++;
|
yea++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -437,7 +396,6 @@ void Constituent::callElection() {
|
||||||
/// Start clean shutdown
|
/// Start clean shutdown
|
||||||
void Constituent::beginShutdown() {
|
void Constituent::beginShutdown() {
|
||||||
|
|
||||||
_notifier.reset();
|
|
||||||
Thread::beginShutdown();
|
Thread::beginShutdown();
|
||||||
|
|
||||||
CONDITION_LOCKER(guard, _cv);
|
CONDITION_LOCKER(guard, _cv);
|
||||||
|
@ -482,11 +440,10 @@ void Constituent::run() {
|
||||||
for (auto const& i : VPackArrayIterator(result)) {
|
for (auto const& i : VPackArrayIterator(result)) {
|
||||||
try {
|
try {
|
||||||
_term = i.get("term").getUInt();
|
_term = i.get("term").getUInt();
|
||||||
_votedFor =
|
_votedFor = i.get("voted_for").copyString();
|
||||||
static_cast<decltype(_votedFor)>(i.get("voted_for").getUInt());
|
|
||||||
} catch (std::exception const&) {
|
} catch (std::exception const&) {
|
||||||
LOG_TOPIC(ERR, Logger::AGENCY)
|
LOG_TOPIC(ERR, Logger::AGENCY)
|
||||||
<< "Persisted election entries corrupt! Defaulting term,vote (0,0)";
|
<< "Persisted election entries corrupt! Defaulting term,vote (0,0)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -502,7 +459,8 @@ void Constituent::run() {
|
||||||
_cast = false; // New round set not cast vote
|
_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));
|
long rand_wait = static_cast<long>(RandomGenerator::interval(left, right));
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -522,7 +480,7 @@ void Constituent::run() {
|
||||||
} else if (_role == CANDIDATE) {
|
} else if (_role == CANDIDATE) {
|
||||||
callElection(); // Run for office
|
callElection(); // Run for office
|
||||||
} else {
|
} 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);
|
long rand_wait = static_cast<long>(left);
|
||||||
{
|
{
|
||||||
CONDITION_LOCKER(guardv, _cv);
|
CONDITION_LOCKER(guardv, _cv);
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
|
|
||||||
#include "AgencyCommon.h"
|
#include "AgencyCommon.h"
|
||||||
#include "AgentConfiguration.h"
|
#include "AgentConfiguration.h"
|
||||||
#include "NotifierThread.h"
|
|
||||||
|
|
||||||
#include "Basics/Common.h"
|
#include "Basics/Common.h"
|
||||||
#include "Basics/Thread.h"
|
#include "Basics/Thread.h"
|
||||||
|
@ -109,7 +108,7 @@ class Constituent : public arangodb::Thread {
|
||||||
void candidate();
|
void candidate();
|
||||||
|
|
||||||
/// @brief Become leader
|
/// @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)
|
/// @brief Call for vote (by leader or candidates after timeout)
|
||||||
void callElection();
|
void callElection();
|
||||||
|
@ -120,11 +119,6 @@ class Constituent : public arangodb::Thread {
|
||||||
/// @brief Wait for sync
|
/// @brief Wait for sync
|
||||||
bool waitForSync() const;
|
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
|
/// @brief Sleep for how long
|
||||||
duration_t sleepFor(double, double);
|
duration_t sleepFor(double, double);
|
||||||
|
|
||||||
|
@ -141,8 +135,6 @@ class Constituent : public arangodb::Thread {
|
||||||
Agent* _agent; /**< @brief My boss */
|
Agent* _agent; /**< @brief My boss */
|
||||||
arangodb::consensus::id_t _votedFor;
|
arangodb::consensus::id_t _votedFor;
|
||||||
|
|
||||||
std::unique_ptr<NotifierThread> _notifier;
|
|
||||||
|
|
||||||
arangodb::basics::ConditionVariable _cv; // agency callbacks
|
arangodb::basics::ConditionVariable _cv; // agency callbacks
|
||||||
mutable arangodb::Mutex _castLock;
|
mutable arangodb::Mutex _castLock;
|
||||||
};
|
};
|
||||||
|
|
|
@ -74,7 +74,7 @@ inline RestHandler::status RestAgencyHandler::reportUnknownMethod() {
|
||||||
void RestAgencyHandler::redirectRequest(arangodb::consensus::id_t leaderId) {
|
void RestAgencyHandler::redirectRequest(arangodb::consensus::id_t leaderId) {
|
||||||
try {
|
try {
|
||||||
std::string url =
|
std::string url =
|
||||||
Endpoint::uriForm(_agent->config().pool[leaderId]) +
|
Endpoint::uriForm(_agent->config().pool.at(leaderId)) +
|
||||||
_request->requestPath();
|
_request->requestPath();
|
||||||
setResponseCode(GeneralResponse::ResponseCode::TEMPORARY_REDIRECT);
|
setResponseCode(GeneralResponse::ResponseCode::TEMPORARY_REDIRECT);
|
||||||
_response->setHeaderNC(StaticStrings::Location, url);
|
_response->setHeaderNC(StaticStrings::Location, url);
|
||||||
|
@ -140,7 +140,7 @@ RestHandler::status RestAgencyHandler::handleWrite() {
|
||||||
return status::DONE;
|
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));
|
std::this_thread::sleep_for(duration_t(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +215,7 @@ inline RestHandler::status RestAgencyHandler::handleRead() {
|
||||||
return status::DONE;
|
return status::DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(_agent->size() > 1 && _agent->leaderID() > 100) {
|
while(_agent->size() > 1 && _agent->leaderID() == "") {
|
||||||
std::this_thread::sleep_for(duration_t(100));
|
std::this_thread::sleep_for(duration_t(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,11 +130,11 @@ RestHandler::status RestAgencyPrivHandler::execute() {
|
||||||
return reportBadQuery(); // bad query
|
return reportBadQuery(); // bad query
|
||||||
}
|
}
|
||||||
} else if (_request->suffix()[0] == "gossip") {
|
} else if (_request->suffix()[0] == "gossip") {
|
||||||
if (_agent->activeAgentStartupSequence()) {
|
if (_agent->serveActiveAgent()) { // only during startup (see Agent)
|
||||||
arangodb::velocypack::Options options;
|
arangodb::velocypack::Options options;
|
||||||
query_t query = _request->toVelocyPackBuilderPtr(&options);
|
query_t query = _request->toVelocyPackBuilderPtr(&options);
|
||||||
_agent->gossip(query);
|
_agent->gossip(query);
|
||||||
} else {
|
} else { // Gone!
|
||||||
return reportGone();
|
return reportGone();
|
||||||
}
|
}
|
||||||
}else {
|
}else {
|
||||||
|
|
|
@ -505,9 +505,9 @@ bool State::loadOrPersistConfiguration() {
|
||||||
|
|
||||||
if (result.isArray() && result.length()) { // We already have a persisted conf
|
if (result.isArray() && result.length()) { // We already have a persisted conf
|
||||||
|
|
||||||
try (config.merge(result)) {
|
try {
|
||||||
0;
|
_agent->mergeConfiguration(result);
|
||||||
} catch (std::exception const& e) {
|
} catch (std::exception const& e) {
|
||||||
LOG_TOPIC(ERR, Logger::AGENCY)
|
LOG_TOPIC(ERR, Logger::AGENCY)
|
||||||
<< "Failed to merge persisted configuration into runtime configuration:"
|
<< "Failed to merge persisted configuration into runtime configuration:"
|
||||||
<< e.what();
|
<< e.what();
|
||||||
|
@ -519,7 +519,7 @@ bool State::loadOrPersistConfiguration() {
|
||||||
LOG_TOPIC(DEBUG, Logger::AGENCY) << "New agency!";
|
LOG_TOPIC(DEBUG, Logger::AGENCY) << "New agency!";
|
||||||
|
|
||||||
TRI_ASSERT(_agent != nullptr);
|
TRI_ASSERT(_agent != nullptr);
|
||||||
_agent->config().id = to_string(boost::uuids::random_generator()());
|
_agent->id(to_string(boost::uuids::random_generator()()));
|
||||||
|
|
||||||
auto transactionContext =
|
auto transactionContext =
|
||||||
std::make_shared<StandaloneTransactionContext>(_vocbase);
|
std::make_shared<StandaloneTransactionContext>(_vocbase);
|
||||||
|
@ -535,7 +535,7 @@ bool State::loadOrPersistConfiguration() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = trx.insert(
|
result = trx.insert(
|
||||||
"configuration", _agent.config().toBuilder().slice(), _options);
|
"configuration", _agent->config().toBuilder()->slice(), _options);
|
||||||
} catch (std::exception const& e) {
|
} catch (std::exception const& e) {
|
||||||
LOG_TOPIC(ERR, Logger::AGENCY)
|
LOG_TOPIC(ERR, Logger::AGENCY)
|
||||||
<< "Failed to persist configuration entry:" << e.what();
|
<< "Failed to persist configuration entry:" << e.what();
|
||||||
|
@ -550,7 +550,6 @@ bool State::loadOrPersistConfiguration() {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue