mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'agency' of https://github.com/arangodb/arangodb into devel
This commit is contained in:
commit
4f152db27f
|
@ -161,7 +161,7 @@ class Builder {
|
|||
|
||||
explicit Builder(Buffer<uint8_t>& buffer,
|
||||
Options const* options = &Options::Defaults)
|
||||
: _pos(0), _keyWritten(false), options(options) {
|
||||
: _pos(buffer.size()), _keyWritten(false), options(options) {
|
||||
_buffer.reset(&buffer, BufferNonDeleter<uint8_t>());
|
||||
_start = _buffer->data();
|
||||
_size = _buffer->size();
|
||||
|
|
|
@ -729,6 +729,7 @@ class Slice {
|
|||
static ValueType const TypeMap[256];
|
||||
static unsigned int const WidthMap[32];
|
||||
static unsigned int const FirstSubMap[32];
|
||||
static char const* NullStr;
|
||||
};
|
||||
|
||||
// a class for keeping Slice allocations in scope
|
||||
|
|
|
@ -213,6 +213,8 @@ unsigned int const Slice::FirstSubMap[32] = {
|
|||
8, // 0x12, object with unsorted index table
|
||||
0};
|
||||
|
||||
static char const* NullStr = "0x18";
|
||||
|
||||
// creates a Slice from Json and adds it to a scope
|
||||
Slice Slice::fromJson(SliceScope& scope, std::string const& json,
|
||||
Options const* options) {
|
||||
|
|
|
@ -724,3 +724,15 @@ add_subdirectory(arangosh)
|
|||
add_subdirectory(arangod)
|
||||
add_subdirectory(UnitTests)
|
||||
add_subdirectory(Documentation)
|
||||
|
||||
add_dependencies(arangob zlibstatic v8_build)
|
||||
add_dependencies(arangod ev zlibstatic v8_build)
|
||||
add_dependencies(arangodump zlibstatic v8_build)
|
||||
add_dependencies(arangoimp zlibstatic v8_build)
|
||||
add_dependencies(arangorestore zlibstatic v8_build)
|
||||
add_dependencies(arangosh zlibstatic v8_build)
|
||||
#if (USE_MAINTAINER_MODE)
|
||||
# add_dependencies(basics_suite v8_build)
|
||||
# add_dependencies(geo_suite v8_build)
|
||||
#endif()
|
||||
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __ARANGODB_CONSENSUS_AGENCY_COMMON__
|
||||
#define __ARANGODB_CONSENSUS_AGENCY_COMMON__
|
||||
|
||||
#include <Logger/Logger.h>
|
||||
#include <Basics/VelocyPackHelper.h>
|
||||
|
||||
#include <velocypack/Buffer.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <initializer_list>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace arangodb {
|
||||
namespace consensus {
|
||||
|
||||
typedef enum AGENCY_STATUS {
|
||||
OK = 0,
|
||||
RETRACTED_CANDIDACY_FOR_HIGHER_TERM, // Vote for higher term candidate
|
||||
// while running. Strange!
|
||||
RESIGNED_LEADERSHIP_FOR_HIGHER_TERM, // Vote for higher term candidate
|
||||
// while leading. Very bad!
|
||||
LOWER_TERM_APPEND_ENTRIES_RPC,
|
||||
NO_MATCHING_PREVLOG
|
||||
} status_t;
|
||||
|
||||
typedef uint64_t term_t; // Term type
|
||||
typedef uint32_t id_t; // Id type
|
||||
|
||||
enum role_t { // Role
|
||||
FOLLOWER, CANDIDATE, LEADER
|
||||
};
|
||||
|
||||
enum AGENT_FAILURE {
|
||||
PERSISTENCE,
|
||||
TIMEOUT,
|
||||
UNAVAILABLE,
|
||||
PRECONDITION
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Agent configuration
|
||||
*/
|
||||
template<class T>
|
||||
inline std::ostream& operator<< (std::ostream& l, std::vector<T> const& v) {
|
||||
for (auto const& i : v)
|
||||
l << i << "|";
|
||||
return l;
|
||||
}
|
||||
template<typename T>
|
||||
inline std::ostream& operator<< (std::ostream& os, std::list<T> const& l) {
|
||||
for (auto const& i : l)
|
||||
os << i << "|";
|
||||
return os;
|
||||
}
|
||||
|
||||
struct AgentConfiguration {
|
||||
id_t id;
|
||||
float min_ping;
|
||||
float max_ping;
|
||||
float election_timeout;
|
||||
float append_entries_retry_interval;
|
||||
std::vector<std::string> end_points;
|
||||
std::string end_point_persist;
|
||||
bool notify;
|
||||
AgentConfiguration () : min_ping(.15), max_ping(.3) {};
|
||||
AgentConfiguration (uint32_t i, float min_p, float max_p, float appent_i,
|
||||
std::vector<std::string> const& end_p, bool n = false) :
|
||||
id(i), min_ping(min_p), max_ping(max_p),
|
||||
append_entries_retry_interval(appent_i), end_points(end_p), notify(n) {
|
||||
end_point_persist = end_points[id];
|
||||
}
|
||||
inline size_t size() const {return end_points.size();}
|
||||
/* inline std::string constituen toString() const {
|
||||
std::stringstream out;
|
||||
out << "Configuration\n";
|
||||
out << " " << "id (" << id << ") min_ping(" << min_ping << ") max_ping(" << max_ping << ")\n";
|
||||
out << " " << "endpoints(" << end_points << ")";
|
||||
return out.str();
|
||||
}*/
|
||||
friend std::ostream& operator<< (std::ostream& out, AgentConfiguration const& c) {
|
||||
out << "Configuration\n";
|
||||
out << " " << "id (" << c.id << ") min_ping(" << c.min_ping
|
||||
<< ") max_ping(" << c.max_ping << ")\n";
|
||||
out << " endpoints(" << c.end_points << ")";
|
||||
return out;
|
||||
}
|
||||
inline std::string const toString() const {
|
||||
std::stringstream s;
|
||||
s << *this;
|
||||
return s.str();
|
||||
}
|
||||
};
|
||||
typedef AgentConfiguration config_t;
|
||||
|
||||
struct constituent_t { // Constituent type
|
||||
id_t id;
|
||||
std::string endpoint;
|
||||
};
|
||||
|
||||
typedef std::vector<constituent_t> constituency_t; // Constituency type
|
||||
typedef uint32_t state_t; // State type
|
||||
typedef std::chrono::duration<double> duration_t; // Duration type
|
||||
|
||||
using query_t = std::shared_ptr<arangodb::velocypack::Builder>;
|
||||
|
||||
struct vote_ret_t {
|
||||
query_t result;
|
||||
vote_ret_t (query_t res) : result(res) {}
|
||||
};
|
||||
|
||||
struct read_ret_t {
|
||||
bool accepted; // Query processed
|
||||
id_t redirect; // Otherwise redirect to
|
||||
query_t result; // Result
|
||||
read_ret_t (bool a, id_t id, query_t res = nullptr) :
|
||||
accepted(a), redirect(id), result(res) {}
|
||||
};
|
||||
|
||||
typedef uint64_t index_t;
|
||||
|
||||
typedef std::initializer_list<index_t> index_list_t;
|
||||
struct write_ret_t {
|
||||
bool accepted; // Query processed
|
||||
id_t redirect; // Otherwise redirect to
|
||||
std::vector<bool> applied;
|
||||
std::vector<index_t> indices; // Indices of log entries (if any) to wait for
|
||||
write_ret_t () : accepted(false), redirect(0) {}
|
||||
write_ret_t (bool a, id_t id) : accepted(a), redirect(id) {}
|
||||
write_ret_t (bool a, id_t id, std::vector<bool> const& app,
|
||||
std::vector<index_t> const& idx) :
|
||||
accepted(a), redirect(id), applied(app), indices(idx) {}
|
||||
};
|
||||
|
||||
using namespace std::chrono;
|
||||
using buffer_t = std::shared_ptr<arangodb::velocypack::Buffer<uint8_t>>;
|
||||
/**
|
||||
* @brief State entry
|
||||
*/
|
||||
struct log_t {
|
||||
index_t index;
|
||||
term_t term;
|
||||
id_t leaderId;
|
||||
//std::string entry;
|
||||
buffer_t entry;
|
||||
milliseconds timestamp;
|
||||
// log_t (index_t idx, term_t t, id_t lid, std::string const& e) :
|
||||
log_t (index_t idx, term_t t, id_t lid, buffer_t const& e) :
|
||||
index(idx), term(t), leaderId(lid), entry(e), timestamp (
|
||||
duration_cast<milliseconds>(system_clock::now().time_since_epoch())) {}
|
||||
friend std::ostream& operator<< (std::ostream& o, log_t const& l) {
|
||||
o << l.index << " " << l.term << " " << l.leaderId << " "
|
||||
<< l.entry->toString() << " " << l.timestamp.count();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
enum agencyException {
|
||||
QUERY_NOT_APPLICABLE
|
||||
};
|
||||
|
||||
struct append_entries_t {
|
||||
term_t term;
|
||||
bool success;
|
||||
append_entries_t (term_t t, bool s) : term(t), success(s) {}
|
||||
};
|
||||
|
||||
struct collect_ret_t {
|
||||
index_t prev_log_index;
|
||||
term_t prev_log_term;
|
||||
std::vector<index_t> indices;
|
||||
collect_ret_t () : prev_log_index(0), prev_log_term(0) {}
|
||||
collect_ret_t (index_t pli, term_t plt, std::vector<index_t> const& idx) :
|
||||
prev_log_index(pli), prev_log_term(plt), indices(idx) {}
|
||||
size_t size() const {return indices.size();}
|
||||
};
|
||||
|
||||
struct priv_rpc_ret_t {
|
||||
bool success;
|
||||
term_t term;
|
||||
priv_rpc_ret_t (bool s, term_t t) : success(s), term(t) {}
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif // __ARANGODB_CONSENSUS_AGENT__
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Agent.h"
|
||||
#include "Basics/ConditionLocker.h"
|
||||
|
||||
#include <velocypack/Iterator.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb::velocypack;
|
||||
|
||||
namespace arangodb {
|
||||
namespace consensus {
|
||||
|
||||
Agent::Agent () : Thread ("Agent"), _stopping(false) {}
|
||||
|
||||
Agent::Agent (config_t const& config) : Thread ("Agent"), _config(config) {
|
||||
_state.setEndPoint(_config.end_points[this->id()]);
|
||||
_constituent.configure(this);
|
||||
_confirmed.resize(size(),0);
|
||||
}
|
||||
|
||||
id_t Agent::id() const { return _config.id;}
|
||||
|
||||
Agent::~Agent () {
|
||||
// shutdown();
|
||||
}
|
||||
|
||||
void Agent::start() {
|
||||
_constituent.start();
|
||||
_spear_head.start();
|
||||
}
|
||||
|
||||
term_t Agent::term () const {
|
||||
return _constituent.term();
|
||||
}
|
||||
|
||||
inline size_t Agent::size() const {
|
||||
return _config.size();
|
||||
}
|
||||
|
||||
priv_rpc_ret_t Agent::requestVote(term_t t, id_t id, index_t lastLogIndex,
|
||||
index_t lastLogTerm, query_t const& query) {
|
||||
|
||||
if (query != nullptr) {
|
||||
if (query->slice().isArray() || query->slice().isObject()) {
|
||||
size_t j = 0;
|
||||
for (auto const& i : VPackObjectIterator(query->slice())) {
|
||||
std::string const key(i.key.copyString());
|
||||
std::string const value(i.value.copyString());
|
||||
if (key == "endpoint")
|
||||
_config.end_points[j] = value;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
LOG(WARN) << _config;
|
||||
}
|
||||
|
||||
return priv_rpc_ret_t(
|
||||
_constituent.vote(id, t, lastLogIndex, lastLogTerm), this->term());
|
||||
}
|
||||
|
||||
config_t const& Agent::config () const {
|
||||
return _config;
|
||||
}
|
||||
|
||||
void Agent::print (arangodb::LoggerStream& logger) const {
|
||||
//logger << _config;
|
||||
}
|
||||
|
||||
void Agent::report(status_t status) {
|
||||
//_status = status;
|
||||
}
|
||||
|
||||
id_t Agent::leaderID () const {
|
||||
return _constituent.leaderID();
|
||||
}
|
||||
|
||||
void Agent::catchUpReadDB() {}; // TODO
|
||||
|
||||
bool Agent::waitFor (index_t index, duration_t timeout) {
|
||||
|
||||
if (size() == 1) // single host agency
|
||||
return true;
|
||||
|
||||
CONDITION_LOCKER(guard, _rest_cv);
|
||||
auto start = std::chrono::system_clock::now();
|
||||
|
||||
while (true) {
|
||||
|
||||
_rest_cv.wait();
|
||||
|
||||
// shutting down
|
||||
if (this->isStopping()) {
|
||||
return false;
|
||||
}
|
||||
// timeout?
|
||||
if (std::chrono::system_clock::now() - start > timeout) {
|
||||
return false;
|
||||
}
|
||||
if (_last_commit_index > index) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// We should never get here
|
||||
TRI_ASSERT(false);
|
||||
}
|
||||
|
||||
void Agent::reportIn (id_t id, index_t index) {
|
||||
MUTEX_LOCKER(mutexLocker, _confirmedLock);
|
||||
if (index > _confirmed[id]) // progress this follower?
|
||||
_confirmed[id] = index;
|
||||
|
||||
if(index > _last_commit_index) { // progress last commit?
|
||||
size_t n = 0;
|
||||
for (size_t i = 0; i < size(); ++i) {
|
||||
n += (_confirmed[i]>index);
|
||||
}
|
||||
if (n>size()/2) { // enough confirms?
|
||||
_last_commit_index = index;
|
||||
}
|
||||
}
|
||||
_rest_cv.broadcast(); // wake up REST handlers
|
||||
}
|
||||
|
||||
priv_rpc_ret_t Agent::recvAppendEntriesRPC (term_t term, id_t leaderId, index_t prevIndex,
|
||||
term_t prevTerm, index_t leaderCommitIndex, query_t const& queries) {
|
||||
|
||||
// Update commit index
|
||||
_last_commit_index = leaderCommitIndex;
|
||||
|
||||
// Sanity
|
||||
if (this->term() > term)
|
||||
throw LOWER_TERM_APPEND_ENTRIES_RPC; // (§5.1)
|
||||
if (!_state.findit(prevIndex, prevTerm))
|
||||
throw NO_MATCHING_PREVLOG; // (§5.3)
|
||||
|
||||
// Delete conflits and append (§5.3)
|
||||
//for (size_t i = 0; i < queries->slice().length()/2; i+=2) {
|
||||
// _state.log (queries->slice()[i ].toString(),
|
||||
// queries->slice()[i+1].getUInt(), term, leaderId);
|
||||
//}
|
||||
|
||||
return priv_rpc_ret_t(true, this->term());
|
||||
}
|
||||
|
||||
append_entries_t Agent::sendAppendEntriesRPC (
|
||||
id_t slave_id, collect_ret_t const& entries) {
|
||||
|
||||
// RPC path
|
||||
std::stringstream path;
|
||||
path << "/_api/agency_priv/appendEntries?term=" << term() << "&leaderId="
|
||||
<< id() << "&prevLogIndex=" << entries.prev_log_index << "&prevLogTerm="
|
||||
<< entries.prev_log_term << "&leaderCommit=" << _last_commit_index;
|
||||
|
||||
// Headers
|
||||
std::unique_ptr<std::map<std::string, std::string>> headerFields =
|
||||
std::make_unique<std::map<std::string, std::string> >();
|
||||
|
||||
// Body
|
||||
Builder builder;
|
||||
for (size_t i = 0; i < entries.size(); ++i) {
|
||||
builder.add ("index", Value(std::to_string(entries.indices[i])));
|
||||
builder.add ("query", Builder(*_state[entries.indices[i]].entry).slice());
|
||||
}
|
||||
builder.close();
|
||||
|
||||
// Send
|
||||
arangodb::ClusterComm::instance()->asyncRequest
|
||||
("1", 1, _config.end_points[slave_id],
|
||||
rest::HttpRequest::HTTP_REQUEST_GET,
|
||||
path.str(), std::make_shared<std::string>(builder.toString()), headerFields,
|
||||
std::make_shared<AgentCallback>(this),
|
||||
1.0, true);
|
||||
|
||||
return append_entries_t(this->term(), true);
|
||||
|
||||
}
|
||||
|
||||
bool Agent::load () {
|
||||
LOG(INFO) << "Loading persistent state.";
|
||||
if (!_state.load())
|
||||
LOG(FATAL) << "Failed to load persistent state on statup.";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
write_ret_t Agent::write (query_t const& query) {
|
||||
|
||||
if (_constituent.leading()) { // Leading
|
||||
MUTEX_LOCKER(mutexLocker, _confirmedLock);
|
||||
std::vector<bool> applied = _spear_head.apply(query); // Apply to spearhead
|
||||
std::vector<index_t> indices =
|
||||
_state.log (query, applied, term(), id()); // Append to log w/ indicies
|
||||
for (size_t i = 0; i < applied.size(); ++i) {
|
||||
if (applied[i]) {
|
||||
_confirmed[id()] = indices[i]; // Confirm myself
|
||||
}
|
||||
}
|
||||
_cv.signal(); // Wake up run
|
||||
return write_ret_t(true,id(),applied,indices); // Indices to wait for to rest
|
||||
} else { // Leading else redirect
|
||||
return write_ret_t(false,_constituent.leaderID());
|
||||
}
|
||||
}
|
||||
|
||||
read_ret_t Agent::read (query_t const& query) const {
|
||||
if (_constituent.leading()) { // We are leading
|
||||
auto result = (_config.size() == 1) ?
|
||||
_spear_head.read(query) : _read_db.read (query);
|
||||
return read_ret_t(true,_constituent.leaderID(),result);//(query); //TODO:
|
||||
} else { // We redirect
|
||||
return read_ret_t(false,_constituent.leaderID());
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::run() {
|
||||
|
||||
CONDITION_LOCKER(guard, _cv);
|
||||
|
||||
while (!this->isStopping()) {
|
||||
|
||||
_cv.wait(100000);
|
||||
|
||||
std::vector<collect_ret_t> work(size());
|
||||
|
||||
// Collect all unacknowledged
|
||||
for (size_t i = 0; i < size(); ++i) {
|
||||
if (i != id()) {
|
||||
work[i] = _state.collectFrom(_confirmed[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// (re-)attempt RPCs
|
||||
for (size_t j = 0; j < size(); ++j) {
|
||||
if (j != id() && work[j].size()) {
|
||||
sendAppendEntriesRPC(j, work[j]);
|
||||
}
|
||||
}
|
||||
|
||||
// catch up read db
|
||||
catchUpReadDB();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::beginShutdown() {
|
||||
Thread::beginShutdown();
|
||||
_constituent.beginShutdown();
|
||||
// Stop callbacks
|
||||
//_agent_callback.shutdown();
|
||||
// wake up all blocked rest handlers
|
||||
CONDITION_LOCKER(guard, _cv);
|
||||
//guard.broadcast();
|
||||
}
|
||||
|
||||
bool Agent::lead () {
|
||||
rebuildDBs();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Agent::rebuildDBs() {
|
||||
MUTEX_LOCKER(mutexLocker, _dbLock);
|
||||
return true;
|
||||
}
|
||||
|
||||
log_t const& Agent::lastLog() const {
|
||||
return _state.lastLog();
|
||||
}
|
||||
|
||||
|
||||
}}
|
|
@ -0,0 +1,194 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __ARANGODB_CONSENSUS_AGENT__
|
||||
#define __ARANGODB_CONSENSUS_AGENT__
|
||||
|
||||
#include "AgencyCommon.h"
|
||||
#include "AgentCallback.h"
|
||||
#include "Constituent.h"
|
||||
#include "State.h"
|
||||
#include "Store.h"
|
||||
|
||||
namespace arangodb {
|
||||
namespace consensus {
|
||||
|
||||
class Agent : public arangodb::Thread {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Default ctor
|
||||
*/
|
||||
Agent ();
|
||||
|
||||
/**
|
||||
* @brief Construct with program options
|
||||
*/
|
||||
Agent (config_t const&);
|
||||
|
||||
/**
|
||||
* @brief Clean up
|
||||
*/
|
||||
virtual ~Agent();
|
||||
|
||||
/**
|
||||
* @brief Get current term
|
||||
*/
|
||||
term_t term() const;
|
||||
|
||||
/**
|
||||
* @brief Get current term
|
||||
*/
|
||||
id_t id() const;
|
||||
|
||||
/**
|
||||
* @brief Vote request
|
||||
*/
|
||||
priv_rpc_ret_t requestVote(term_t , id_t, index_t, index_t, query_t const&);
|
||||
|
||||
/**
|
||||
* @brief Provide configuration
|
||||
*/
|
||||
config_t const& config () const;
|
||||
|
||||
/**
|
||||
* @brief Start thread
|
||||
*/
|
||||
void start ();
|
||||
|
||||
/**
|
||||
* @brief Verbose print of myself
|
||||
*/
|
||||
void print (arangodb::LoggerStream&) const;
|
||||
|
||||
/**
|
||||
* @brief Are we fit to run?
|
||||
*/
|
||||
bool fitness () const;
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*/
|
||||
void report (status_t);
|
||||
|
||||
/**
|
||||
* @brief Leader ID
|
||||
*/
|
||||
id_t leaderID () const;
|
||||
|
||||
bool lead ();
|
||||
|
||||
bool load ();
|
||||
|
||||
/**
|
||||
* @brief Attempt write
|
||||
*/
|
||||
write_ret_t write (query_t const&);
|
||||
|
||||
/**
|
||||
* @brief Read from agency
|
||||
*/
|
||||
read_ret_t read (query_t const&) const;
|
||||
|
||||
/**
|
||||
* @brief Received by followers to replicate log entries (§5.3);
|
||||
* also used as heartbeat (§5.2).
|
||||
*/
|
||||
priv_rpc_ret_t recvAppendEntriesRPC (term_t term, id_t leaderId, index_t prevIndex,
|
||||
term_t prevTerm, index_t lastCommitIndex, query_t const& queries);
|
||||
|
||||
/**
|
||||
* @brief Invoked by leader to replicate log entries (§5.3);
|
||||
* also used as heartbeat (§5.2).
|
||||
*/
|
||||
append_entries_t sendAppendEntriesRPC (id_t slave_id,
|
||||
collect_ret_t const& entries);
|
||||
|
||||
/**
|
||||
* @brief 1. Deal with appendEntries to slaves.
|
||||
* 2. Report success of write processes.
|
||||
*/
|
||||
void run () override final;
|
||||
void beginShutdown () override;
|
||||
|
||||
/**
|
||||
* @brief Report appended entries from AgentCallback
|
||||
*/
|
||||
void reportIn (id_t id, index_t idx);
|
||||
|
||||
/**
|
||||
* @brief Wait for slaves to confirm appended entries
|
||||
*/
|
||||
bool waitFor (index_t last_entry, duration_t timeout = duration_t(2.0));
|
||||
|
||||
/**
|
||||
* @brief Convencience size of agency
|
||||
*/
|
||||
size_t size() const;
|
||||
|
||||
/**
|
||||
* @brief Catch up read db to _last_commit_index
|
||||
*/
|
||||
void catchUpReadDB();
|
||||
|
||||
/**
|
||||
* @brief Rebuild DBs by applying state log to empty DB
|
||||
*/
|
||||
bool rebuildDBs();
|
||||
|
||||
/**
|
||||
* @brief Last log entry
|
||||
*/
|
||||
log_t const& lastLog () const;
|
||||
|
||||
private:
|
||||
|
||||
Constituent _constituent; /**< @brief Leader election delegate */
|
||||
State _state; /**< @brief Log replica */
|
||||
config_t _config; /**< @brief Command line arguments */
|
||||
|
||||
std::atomic<index_t> _last_commit_index;
|
||||
|
||||
arangodb::Mutex _uncommitedLock;
|
||||
|
||||
Store _spear_head;
|
||||
Store _read_db;
|
||||
|
||||
AgentCallback _agent_callback;
|
||||
|
||||
arangodb::basics::ConditionVariable _cv; // agency callbacks
|
||||
arangodb::basics::ConditionVariable _rest_cv; // rest handler
|
||||
|
||||
|
||||
std::atomic<bool> _stopping;
|
||||
|
||||
std::vector<index_t> _confirmed;
|
||||
arangodb::Mutex _confirmedLock; /**< @brief Mutex for modifying _confirmed */
|
||||
arangodb::Mutex _dbLock;
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,41 @@
|
|||
#include "AgentCallback.h"
|
||||
#include "AgencyCommon.h"
|
||||
#include "Agent.h"
|
||||
|
||||
using namespace arangodb::consensus;
|
||||
using namespace arangodb::velocypack;
|
||||
|
||||
AgentCallback::AgentCallback() : _agent(0) {}
|
||||
|
||||
AgentCallback::AgentCallback(Agent* agent) : _agent(agent) {}
|
||||
|
||||
void AgentCallback::shutdown() {
|
||||
_agent = 0;
|
||||
}
|
||||
|
||||
bool AgentCallback::operator()(arangodb::ClusterCommResult* res) {
|
||||
|
||||
if (res->status == CL_COMM_RECEIVED) {
|
||||
id_t agent_id;
|
||||
std::vector<index_t> idx;
|
||||
std::shared_ptr<VPackBuilder> builder = res->result->getBodyVelocyPack();
|
||||
if (builder->hasKey("agent_id")) {
|
||||
agent_id = builder->getKey("agent_id").getUInt();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
if (builder->hasKey("indices")) {
|
||||
builder->getKey("indices");
|
||||
if (builder->getKey("indices").isArray()) {
|
||||
for (size_t i = 0; i < builder->getKey("indices").length(); ++i) {
|
||||
idx.push_back(builder->getKey("indices")[i].getUInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
if(_agent) {
|
||||
_agent->reportIn (agent_id, idx.back());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __ARANGODB_CONSENSUS_AGENT_CALLBACK__
|
||||
#define __ARANGODB_CONSENSUS_AGENT_CALLBACK__
|
||||
|
||||
#include "Cluster/ClusterComm.h"
|
||||
|
||||
namespace arangodb {
|
||||
namespace consensus {
|
||||
|
||||
class Agent;
|
||||
|
||||
class AgentCallback : public arangodb::ClusterCommCallback {
|
||||
|
||||
public:
|
||||
|
||||
AgentCallback();
|
||||
explicit AgentCallback(Agent* agent);
|
||||
|
||||
virtual bool operator()(arangodb::ClusterCommResult*);
|
||||
|
||||
void shutdown();
|
||||
|
||||
private:
|
||||
Agent* _agent;
|
||||
|
||||
};
|
||||
|
||||
}} // namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,145 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Basics/win-utils.h"
|
||||
#endif
|
||||
|
||||
#include "Logger/Logger.h"
|
||||
#include "Scheduler/PeriodicTask.h"
|
||||
|
||||
#include "ApplicationAgency.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace arangodb::basics;
|
||||
using namespace arangodb::rest;
|
||||
|
||||
ApplicationAgency::ApplicationAgency()
|
||||
: ApplicationFeature("agency"), _size(1), _min_election_timeout(.5),
|
||||
_max_election_timeout(2.0), _election_call_rate_mul(2.5),
|
||||
_append_entries_retry_interval(1.0),
|
||||
_agent_id(std::numeric_limits<uint32_t>::max()) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
ApplicationAgency::~ApplicationAgency() {}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief sets the processor affinity
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ApplicationAgency::setupOptions(
|
||||
std::map<std::string, ProgramOptionsDescription>& options) {
|
||||
options["Agency Options:help-agency"]("agency.size", &_size, "Agency size")
|
||||
("agency.id", &_agent_id, "This agent's id")
|
||||
("agency.election-timeout-min", &_min_election_timeout, "Minimum "
|
||||
"timeout before an agent calls for new election [s]")
|
||||
("agency.election-timeout-max", &_max_election_timeout, "Minimum "
|
||||
"timeout before an agent calls for new election [s]")
|
||||
("agency.endpoint", &_agency_endpoints, "Agency endpoints")
|
||||
("agency.election_call_rate_mul [au]", &_election_call_rate_mul,
|
||||
"Multiplier (<1.0) defining how long the election timeout is with respect "
|
||||
"to the minumum election timeout")
|
||||
("agency.append_entries_retry_interval [s]", &_append_entries_retry_interval,
|
||||
"Interval at which appendEntries are attempted on unresponsive slaves"
|
||||
"in seconds")
|
||||
("agency.notify", &_notify, "Notify others [beta :)]");
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ApplicationAgency::prepare() {
|
||||
|
||||
if (_disabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_size < 1)
|
||||
LOG(FATAL) << "agency must have size greater 0";
|
||||
|
||||
if (_agent_id == std::numeric_limits<uint32_t>::max())
|
||||
LOG(FATAL) << "agency.id must be specified";
|
||||
|
||||
if (_min_election_timeout <= 0.) {
|
||||
LOG(FATAL) << "agency.election-timeout-min must not be negative!";
|
||||
} else if (_min_election_timeout < .15) {
|
||||
LOG(WARN) << "very short agency.election-timeout-min!";
|
||||
}
|
||||
|
||||
if (_max_election_timeout <= _min_election_timeout) {
|
||||
LOG(FATAL) << "agency.election-timeout-max must not be shorter than or"
|
||||
<< "equal to agency.election-timeout-min.";
|
||||
}
|
||||
|
||||
if (_max_election_timeout <= 2*_min_election_timeout) {
|
||||
LOG(WARN) << "agency.election-timeout-max should probably be chosen longer!";
|
||||
}
|
||||
|
||||
_agency_endpoints.resize(_size);
|
||||
std::iter_swap(_agency_endpoints.begin(),
|
||||
_agency_endpoints.begin() + _agent_id);
|
||||
|
||||
_agent = std::unique_ptr<agent_t>(
|
||||
new agent_t(arangodb::consensus::config_t(
|
||||
_agent_id, _min_election_timeout, _max_election_timeout,
|
||||
_append_entries_retry_interval, _agency_endpoints, _notify)));
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool ApplicationAgency::start() {
|
||||
if (_disabled) {
|
||||
return true;
|
||||
}
|
||||
_agent->start();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ApplicationAgency::open() { return true; }
|
||||
|
||||
|
||||
void ApplicationAgency::close() {
|
||||
if (_disabled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ApplicationAgency::stop() {
|
||||
if (_disabled) {
|
||||
return;
|
||||
}
|
||||
_agent->beginShutdown();
|
||||
}
|
||||
|
||||
agent_t* ApplicationAgency::agent () const {
|
||||
return _agent.get();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGOD_AGENCY_APPLICATION_AGENCY_H
|
||||
#define ARANGOD_AGENCY_APPLICATION_AGENCY_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
|
||||
#include "ApplicationServer/ApplicationFeature.h"
|
||||
#include "Agency/Agent.h"
|
||||
|
||||
|
||||
namespace arangodb {
|
||||
namespace rest {
|
||||
class Task;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief application server with agency
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
using agent_t = consensus::Agent;
|
||||
|
||||
|
||||
class ApplicationAgency : virtual public arangodb::rest::ApplicationFeature {
|
||||
private:
|
||||
ApplicationAgency(ApplicationAgency const&);
|
||||
ApplicationAgency& operator=(ApplicationAgency const&);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
ApplicationAgency();
|
||||
|
||||
|
||||
~ApplicationAgency();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief builds the dispatcher queue
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void buildStandardQueue(size_t nrThreads, size_t maxSize);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief builds the additional AQL dispatcher queue
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void buildAQLQueue(size_t nrThreads, size_t maxSize);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief builds an additional dispatcher queue
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void buildExtraQueue(size_t name, size_t nrThreads, size_t maxSize);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief returns the number of used threads
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
size_t size();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief sets the processor affinity
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setProcessorAffinity(std::vector<size_t> const& cores);
|
||||
|
||||
void stop () override;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
void setupOptions(std::map<std::string,
|
||||
arangodb::basics::ProgramOptionsDescription>&);
|
||||
|
||||
bool prepare();
|
||||
bool start();
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
agent_t* agent() const;
|
||||
|
||||
private:
|
||||
|
||||
uint64_t _size; /**< @brief: agency size (default: 5)*/
|
||||
double _min_election_timeout; /**< @brief: min election timeout */
|
||||
double _max_election_timeout; /**< @brief: max election timeout */
|
||||
double _election_call_rate_mul; /**< @brief: */
|
||||
double _append_entries_retry_interval;
|
||||
bool _notify;
|
||||
/**< @brief interval between retry to slaves*/
|
||||
std::vector<std::string> _agency_endpoints; /**< @brief agency adresses */
|
||||
std::unique_ptr<agent_t> _agent;
|
||||
uint32_t _agent_id;
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,300 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Cluster/ClusterComm.h"
|
||||
#include "Logger/Logger.h"
|
||||
#include "Basics/ConditionLocker.h"
|
||||
|
||||
#include "Constituent.h"
|
||||
#include "Agent.h"
|
||||
|
||||
#include <velocypack/Iterator.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
using namespace arangodb::consensus;
|
||||
using namespace arangodb::rest;
|
||||
using namespace arangodb::velocypack;
|
||||
|
||||
void Constituent::configure(Agent* agent) {
|
||||
_agent = agent;
|
||||
if (size() == 1) {
|
||||
_role = LEADER;
|
||||
} else {
|
||||
_votes.resize(size());
|
||||
_id = _agent->config().id;
|
||||
LOG(WARN) << " +++ my id is " << _id << "agency size is " << size();
|
||||
if (_agent->config().notify) {// (notify everyone)
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Constituent::Constituent() : Thread("Constituent"), _term(0), _id(0),
|
||||
_gen(std::random_device()()), _role(FOLLOWER), _agent(0) {}
|
||||
|
||||
Constituent::~Constituent() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
duration_t Constituent::sleepFor (double min_t, double max_t) {
|
||||
dist_t dis(min_t, max_t);
|
||||
return duration_t(dis(_gen));
|
||||
}
|
||||
|
||||
double Constituent::sleepFord (double min_t, double max_t) {
|
||||
dist_t dis(min_t, max_t);
|
||||
return dis(_gen);
|
||||
}
|
||||
|
||||
term_t Constituent::term() const {
|
||||
return _term;
|
||||
}
|
||||
|
||||
role_t Constituent::role () const {
|
||||
return _role;
|
||||
}
|
||||
|
||||
void Constituent::follow (term_t term) {
|
||||
if (_role > FOLLOWER)
|
||||
LOG(WARN) << "Converted to follower in term " << _term ;
|
||||
_term = term;
|
||||
_votes.assign(_votes.size(),false); // void all votes
|
||||
_role = FOLLOWER;
|
||||
}
|
||||
|
||||
void Constituent::lead () {
|
||||
if (_role < LEADER)
|
||||
LOG(WARN) << "Converted to leader in term " << _term ;
|
||||
_role = LEADER;
|
||||
_agent->lead(); // We need to rebuild spear_head and read_db;
|
||||
}
|
||||
|
||||
void Constituent::candidate () {
|
||||
if (_role != CANDIDATE)
|
||||
LOG(WARN) << "Converted to candidate in term " << _term ;
|
||||
_role = CANDIDATE;
|
||||
}
|
||||
|
||||
bool Constituent::leading () const {
|
||||
return _role == LEADER;
|
||||
}
|
||||
|
||||
bool Constituent::following () const {
|
||||
return _role == FOLLOWER;
|
||||
}
|
||||
|
||||
bool Constituent::running () const {
|
||||
return _role == CANDIDATE;
|
||||
}
|
||||
|
||||
id_t Constituent::leaderID () const {
|
||||
return _leader_id;
|
||||
}
|
||||
|
||||
size_t Constituent::size() const {
|
||||
return _agent->config().size();
|
||||
}
|
||||
|
||||
std::string const& Constituent::end_point(id_t id) const {
|
||||
return _agent->config().end_points[id];
|
||||
}
|
||||
|
||||
std::vector<std::string> const& Constituent::end_points() const {
|
||||
return _agent->config().end_points;
|
||||
}
|
||||
|
||||
size_t Constituent::notifyAll () {
|
||||
|
||||
// Last process notifies everyone
|
||||
std::vector<ClusterCommResult> results(_agent->config().end_points.size());
|
||||
std::stringstream path;
|
||||
|
||||
path << "/_api/agency_priv/notifyAll?term=" << _term << "&agencyId=" << _id;
|
||||
|
||||
// Body contains endpoints
|
||||
Builder body;
|
||||
body.add(VPackValue(VPackValueType::Object));
|
||||
for (auto const& i : end_points()) {
|
||||
body.add("endpoint", Value(i));
|
||||
}
|
||||
body.close();
|
||||
LOG(INFO) << body.toString();
|
||||
|
||||
// Send request to all but myself
|
||||
for (size_t i = 0; i < size(); ++i) {
|
||||
if (i != _id) {
|
||||
std::unique_ptr<std::map<std::string, std::string>> headerFields =
|
||||
std::make_unique<std::map<std::string, std::string> >();
|
||||
LOG(INFO) << i << " notify " << end_point(i) << path.str() ;
|
||||
results[i] = arangodb::ClusterComm::instance()->asyncRequest(
|
||||
"1", 1, end_point(i), rest::HttpRequest::HTTP_REQUEST_POST, path.str(),
|
||||
std::make_shared<std::string>(body.toString()), headerFields, nullptr,
|
||||
0.0, true);
|
||||
}
|
||||
}
|
||||
|
||||
return size()-1;
|
||||
}
|
||||
|
||||
bool Constituent::vote (
|
||||
term_t term, id_t leaderId, index_t prevLogIndex, term_t prevLogTerm) {
|
||||
|
||||
LOG(WARN) << "term (" << term << "," << _term << ")" ;
|
||||
|
||||
if (leaderId == _id) { // Won't vote for myself should never happen.
|
||||
return false; // TODO: Assertion?
|
||||
} else {
|
||||
if (term > _term || (_term==term&&_leader_id==leaderId)) {
|
||||
_term = term;
|
||||
_cast = true; // Note that I voted this time around.
|
||||
_leader_id = leaderId; // The guy I voted for I assume leader.
|
||||
if (_role>FOLLOWER)
|
||||
follow (term);
|
||||
_cv.signal();
|
||||
return true;
|
||||
} else { // Myself running or leading
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Constituent::gossip (const constituency_t& constituency) {
|
||||
// TODO: Replace lame notification by gossip protocol
|
||||
}
|
||||
|
||||
const constituency_t& Constituent::gossip () {
|
||||
// TODO: Replace lame notification by gossip protocol
|
||||
return _constituency;
|
||||
}
|
||||
|
||||
void Constituent::callElection() {
|
||||
|
||||
_votes[_id] = true; // vote for myself
|
||||
_cast = true;
|
||||
if(_role == CANDIDATE)
|
||||
_term++; // raise my term
|
||||
|
||||
std::string body;
|
||||
std::vector<ClusterCommResult> results(_agent->config().end_points.size());
|
||||
std::stringstream path;
|
||||
|
||||
path << "/_api/agency_priv/requestVote?term=" << _term << "&candidateId=" << _id
|
||||
<< "&prevLogIndex=" << _agent->lastLog().index << "&prevLogTerm="
|
||||
<< _agent->lastLog().term;
|
||||
|
||||
for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { // Ask everyone for their vote
|
||||
if (i != _id && end_point(i) != "") {
|
||||
std::unique_ptr<std::map<std::string, std::string>> headerFields =
|
||||
std::make_unique<std::map<std::string, std::string> >();
|
||||
results[i] = arangodb::ClusterComm::instance()->asyncRequest(
|
||||
"1", 1, _agent->config().end_points[i], rest::HttpRequest::HTTP_REQUEST_GET,
|
||||
path.str(), std::make_shared<std::string>(body), headerFields, nullptr,
|
||||
_agent->config().min_ping, true);
|
||||
LOG(INFO) << _agent->config().end_points[i];
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(sleepFor(0.0, .5*_agent->config().min_ping)); // Wait timeout
|
||||
|
||||
for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { // Collect votes
|
||||
if (i != _id && end_point(i) != "") {
|
||||
ClusterCommResult res = arangodb::ClusterComm::instance()->
|
||||
enquire(results[i].operationID);
|
||||
|
||||
if (res.status == CL_COMM_SENT) { // Request successfully sent
|
||||
res = arangodb::ClusterComm::instance()->wait("1", 1, results[i].operationID, "1");
|
||||
std::shared_ptr<Builder > body = res.result->getBodyVelocyPack();
|
||||
if (body->isEmpty()) {
|
||||
continue;
|
||||
} else {
|
||||
if (body->slice().isArray() || body->slice().isObject()) {
|
||||
for (auto const& it : VPackObjectIterator(body->slice())) {
|
||||
std::string const key(it.key.copyString());
|
||||
if (key == "term") {
|
||||
LOG(WARN) << key << " " <<it.value.getUInt();
|
||||
if (it.value.isUInt()) {
|
||||
if (it.value.getUInt() > _term) { // follow?
|
||||
follow(it.value.getUInt());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (key == "voteGranted") {
|
||||
if (it.value.isBool()) {
|
||||
_votes[i] = it.value.getBool();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG(WARN) << body->toJson();
|
||||
}
|
||||
} else { // Request failed
|
||||
_votes[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t yea = 0;
|
||||
for (size_t i = 0; i < size(); ++i) {
|
||||
if (_votes[i]){
|
||||
yea++;
|
||||
}
|
||||
}
|
||||
LOG(WARN) << "votes for me" << yea;
|
||||
if (yea > size()/2){
|
||||
lead();
|
||||
} else {
|
||||
candidate();
|
||||
}
|
||||
}
|
||||
|
||||
void Constituent::beginShutdown() {
|
||||
Thread::beginShutdown();
|
||||
}
|
||||
|
||||
void Constituent::run() {
|
||||
|
||||
// Always start off as follower
|
||||
while (!this->isStopping() && size() > 1) {
|
||||
if (_role == FOLLOWER) {
|
||||
bool cast;
|
||||
{
|
||||
CONDITION_LOCKER (guard, _cv);
|
||||
_cast = false; // New round set not cast vote
|
||||
_cv.wait( // Sleep for random time
|
||||
sleepFord(_agent->config().min_ping, _agent->config().max_ping)*1000000);
|
||||
cast = _cast;
|
||||
}
|
||||
if (!cast) {
|
||||
candidate(); // Next round, we are running
|
||||
}
|
||||
} else {
|
||||
callElection(); // Run for office
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __ARANGODB_CONSENSUS_CONSTITUENT__
|
||||
#define __ARANGODB_CONSENSUS_CONSTITUENT__
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
|
||||
|
||||
#include "AgencyCommon.h"
|
||||
#include "Basics/Thread.h"
|
||||
|
||||
namespace arangodb {
|
||||
namespace consensus {
|
||||
|
||||
class Agent;
|
||||
|
||||
/**
|
||||
* @brief Raft leader election
|
||||
*/
|
||||
class Constituent : public arangodb::Thread {
|
||||
|
||||
public:
|
||||
|
||||
typedef std::uniform_real_distribution<double> dist_t;
|
||||
|
||||
Constituent();
|
||||
|
||||
/**
|
||||
* @brief Clean up and exit election
|
||||
*/
|
||||
virtual ~Constituent();
|
||||
|
||||
void configure(Agent*);
|
||||
|
||||
term_t term() const;
|
||||
|
||||
void runForLeaderShip (bool b);
|
||||
|
||||
role_t role() const;
|
||||
|
||||
/**
|
||||
* @brief Gossip protocol: listen
|
||||
*/
|
||||
void gossip(constituency_t const&);
|
||||
|
||||
/**
|
||||
* @brief Gossip protocol: talk
|
||||
*/
|
||||
const constituency_t& gossip();
|
||||
|
||||
bool leading() const;
|
||||
bool following() const;
|
||||
bool running() const;
|
||||
|
||||
/**
|
||||
* @brief Called by REST handler
|
||||
*/
|
||||
bool vote(term_t, id_t, index_t, term_t);
|
||||
|
||||
/**
|
||||
* @brief My daily business
|
||||
*/
|
||||
void run();
|
||||
|
||||
/**
|
||||
* @brief Who is leading
|
||||
*/
|
||||
id_t leaderID () const;
|
||||
|
||||
/**
|
||||
* @brief Become follower
|
||||
*/
|
||||
void follow(term_t);
|
||||
|
||||
/**
|
||||
* @brief Agency size
|
||||
*/
|
||||
size_t size() const;
|
||||
|
||||
void beginShutdown () override;
|
||||
|
||||
private:
|
||||
|
||||
std::vector<std::string> const& end_points() const;
|
||||
std::string const& end_point(id_t) const;
|
||||
|
||||
/**
|
||||
* @brief Run for leadership
|
||||
*/
|
||||
void candidate();
|
||||
|
||||
/**
|
||||
* @brief Become leader
|
||||
*/
|
||||
void lead();
|
||||
|
||||
/**
|
||||
* @brief Call for vote (by leader or candidates after timeout)
|
||||
*/
|
||||
void callElection();
|
||||
|
||||
/**
|
||||
* @brief Count my votes
|
||||
*/
|
||||
void countVotes();
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
size_t notifyAll();
|
||||
|
||||
/**
|
||||
* @brief Sleep for how long
|
||||
*/
|
||||
duration_t sleepFor(double, double);
|
||||
double sleepFord(double, double);
|
||||
|
||||
// mission critical
|
||||
term_t _term; /**< @brief term number */
|
||||
std::atomic<bool> _cast; /**< @brief cast a vote this term */
|
||||
std::atomic<state_t> _state; /**< @brief State (follower, candidate, leader)*/
|
||||
|
||||
// just critical
|
||||
id_t _leader_id; /**< @brief Current leader */
|
||||
id_t _id; /**< @brief My own id */
|
||||
constituency_t _constituency; /**< @brief List of consituents */
|
||||
std::mt19937 _gen; /**< @brief Random number generator */
|
||||
role_t _role; /**< @brief My role */
|
||||
std::vector<bool> _votes; /**< @brief My list of votes cast in my favour*/
|
||||
Agent* _agent; /**< @brief My boss */
|
||||
|
||||
arangodb::basics::ConditionVariable _cv; // agency callbacks
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif //__ARANGODB_CONSENSUS_CONSTITUENT__
|
|
@ -0,0 +1,128 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __ARANGODB_CONSENSUS_STATE__
|
||||
#define __ARANGODB_CONSENSUS_STATE__
|
||||
|
||||
|
||||
#include "AgencyCommon.h"
|
||||
#include "State.h"
|
||||
|
||||
#include <Basics/Thread.h>
|
||||
#include <Cluster/ClusterComm.h>
|
||||
#include <velocypack/vpack.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
|
||||
//using namespace arangodb::velocypack;
|
||||
|
||||
class Slice {};
|
||||
|
||||
namespace arangodb {
|
||||
namespace consensus {
|
||||
|
||||
typedef uint64_t index_t;
|
||||
|
||||
/**
|
||||
* @brief State entry
|
||||
*/
|
||||
struct log_t {
|
||||
term_t term;
|
||||
id_t leaderId;
|
||||
index_t index;
|
||||
std::string entry;
|
||||
};
|
||||
|
||||
class Agent;
|
||||
|
||||
/**
|
||||
* @brief State replica
|
||||
*/
|
||||
class State : public arangodb::Thread, public arangodb::ClusterCommCallback,
|
||||
std::enable_shared_from_this<State> {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
State ();
|
||||
|
||||
/**
|
||||
* @brief Default Destructor
|
||||
*/
|
||||
virtual ~State();
|
||||
|
||||
void configure(Agent* agent);
|
||||
|
||||
/**
|
||||
* @brief State
|
||||
*/
|
||||
template<typename T> id_t log (T const&);
|
||||
|
||||
/**
|
||||
* @brief Call back for log results from slaves
|
||||
*/
|
||||
virtual bool operator()(ClusterCommResult*);
|
||||
|
||||
/**
|
||||
* @brief My daily business
|
||||
*/
|
||||
void run();
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*/
|
||||
void respHandler (index_t);
|
||||
|
||||
/**
|
||||
* @brief Attempt write
|
||||
*/
|
||||
query_ret_t write (query_t const&) ;
|
||||
|
||||
/**
|
||||
* @brief Read from agency
|
||||
*/
|
||||
query_ret_t read (query_t const&) const;
|
||||
|
||||
/**
|
||||
* @brief Append entries
|
||||
*/
|
||||
bool appendEntries (query_t const&);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
State _state; /**< @brief State machine */
|
||||
State _spear_head; /**< @brief Spear head */
|
||||
Agent* _agent; /**< @brief My boss */
|
||||
log_t _log; /**< @brief State entries */
|
||||
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,239 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "State.h"
|
||||
|
||||
#include <velocypack/Buffer.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
using namespace arangodb::consensus;
|
||||
using namespace arangodb::velocypack;
|
||||
using namespace arangodb::rest;
|
||||
|
||||
State::State(std::string const& end_point) : _end_point(end_point), _dbs_checked(false) {
|
||||
if (!_log.size())
|
||||
_log.push_back(log_t(index_t(0), term_t(0), id_t(0),
|
||||
std::make_shared<Buffer<uint8_t>>()));
|
||||
}
|
||||
|
||||
State::~State() {}
|
||||
|
||||
bool State::save (arangodb::velocypack::Slice const& slice, index_t index,
|
||||
term_t term, double timeout) {
|
||||
|
||||
if (checkDBs()) {
|
||||
|
||||
static std::string const path = "/_api/document?collection=log";
|
||||
std::map<std::string, std::string> headerFields;
|
||||
|
||||
Builder body;
|
||||
body.add(VPackValue(VPackValueType::Object));
|
||||
body.add("_key",Value(std::to_string(index)));
|
||||
body.add("term",Value(std::to_string(term)));
|
||||
if (slice.length()==1) { // no precond
|
||||
body.add("request",slice[0]);
|
||||
} else if (slice.length()==2) { // precond
|
||||
body.add("pre_condition",Value(slice[0].toJson()));
|
||||
body.add("request",slice[1]);
|
||||
} else {
|
||||
body.close();
|
||||
LOG(FATAL) << "Empty or more than two part log?";
|
||||
return false;
|
||||
}
|
||||
body.close();
|
||||
|
||||
std::unique_ptr<arangodb::ClusterCommResult> res =
|
||||
arangodb::ClusterComm::instance()->syncRequest (
|
||||
"1", 1, _end_point, HttpRequest::HTTP_REQUEST_POST, path,
|
||||
body.toJson(), headerFields, 0.0);
|
||||
|
||||
if (res->status != CL_COMM_SENT) {
|
||||
//LOG(WARN) << res->errorMessage;
|
||||
}
|
||||
|
||||
return (res->status == CL_COMM_SENT); // TODO: More verbose result
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Leader
|
||||
std::vector<index_t> State::log (
|
||||
query_t const& query, std::vector<bool> const& appl, term_t term, id_t lid) {
|
||||
std::vector<index_t> idx(appl.size());
|
||||
std::vector<bool> good = appl;
|
||||
size_t j = 0;
|
||||
MUTEX_LOCKER(mutexLocker, _logLock); // log entries must stay in order
|
||||
for (auto const& i : VPackArrayIterator(query->slice())) {
|
||||
if (good[j]) {
|
||||
std::shared_ptr<Buffer<uint8_t>> buf = std::make_shared<Buffer<uint8_t>>();
|
||||
buf->append ((char const*)i.begin(), i.byteSize());
|
||||
idx[j] = _log.back().index+1;
|
||||
_log.push_back(log_t(idx[j], term, lid, buf)); // log to RAM
|
||||
save(i, idx[j], term); // log to disk
|
||||
++j;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
//Follower
|
||||
void State::log (query_t const& query, index_t index, term_t term, id_t lid) {
|
||||
MUTEX_LOCKER(mutexLocker, _logLock);
|
||||
std::shared_ptr<Buffer<uint8_t>> buf = std::make_shared<Buffer<uint8_t>>();
|
||||
buf->append ((char const*)query->slice().begin(), query->slice().byteSize());
|
||||
_log.push_back(log_t(index, term, lid, buf));
|
||||
//save (builder);
|
||||
}
|
||||
|
||||
bool State::findit (index_t index, term_t term) {
|
||||
MUTEX_LOCKER(mutexLocker, _logLock);
|
||||
auto i = std::begin(_log);
|
||||
while (i != std::end(_log)) { // Find entry matching index and term
|
||||
if ((*i).index == index) {
|
||||
if ((*i).term == term) {
|
||||
return true;
|
||||
} else if ((*i).term < term) {
|
||||
// If an existing entry conflicts with a new one (same index
|
||||
// but different terms), delete the existing entry and all that
|
||||
// follow it (§5.3)
|
||||
_log.erase(i, _log.end());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
log_t const& State::operator[](index_t index) const {
|
||||
MUTEX_LOCKER(mutexLocker, _logLock);
|
||||
return _log[index];
|
||||
}
|
||||
|
||||
log_t const& State::lastLog() const {
|
||||
MUTEX_LOCKER(mutexLocker, _logLock);
|
||||
return _log.back();
|
||||
}
|
||||
|
||||
collect_ret_t State::collectFrom (index_t index) {
|
||||
// Collect all from index on
|
||||
MUTEX_LOCKER(mutexLocker, _logLock);
|
||||
std::vector<index_t> work;
|
||||
id_t prev_log_term;
|
||||
index_t prev_log_index;
|
||||
prev_log_term = _log[index-1].term;
|
||||
prev_log_index = _log[index-1].index;
|
||||
for (index_t i = index; i < _log.size(); ++i) {
|
||||
work.push_back(_log[i].index);
|
||||
}
|
||||
return collect_ret_t(prev_log_index, prev_log_term, work);
|
||||
}
|
||||
|
||||
bool State::setEndPoint (std::string const& end_point) {
|
||||
_end_point = end_point;
|
||||
_dbs_checked = false;
|
||||
return true;
|
||||
};
|
||||
|
||||
bool State::checkDBs() {
|
||||
if (!_dbs_checked) {
|
||||
_dbs_checked = checkDB("log") && checkDB("election");
|
||||
}
|
||||
return _dbs_checked;
|
||||
}
|
||||
|
||||
bool State::checkDB (std::string const& name) {
|
||||
if (!_dbs_checked) {
|
||||
std::stringstream path;
|
||||
path << "/_api/collection/" << name << "/properties";
|
||||
std::map<std::string, std::string> headerFields;
|
||||
std::unique_ptr<arangodb::ClusterCommResult> res =
|
||||
arangodb::ClusterComm::instance()->syncRequest (
|
||||
"1", 1, _end_point, HttpRequest::HTTP_REQUEST_GET, path.str(),
|
||||
"", headerFields, 1.0);
|
||||
|
||||
if(res->result->wasHttpError()) {
|
||||
LOG(WARN) << "Creating collection " << name;
|
||||
return createCollection(name);
|
||||
}
|
||||
}
|
||||
return true; // TODO: All possible failures
|
||||
}
|
||||
|
||||
bool State::createCollection (std::string const& name) {
|
||||
static std::string const path = "/_api/collection";
|
||||
std::map<std::string, std::string> headerFields;
|
||||
Builder body;
|
||||
body.add(VPackValue(VPackValueType::Object));
|
||||
body.add("name", Value(name));
|
||||
body.close();
|
||||
std::unique_ptr<arangodb::ClusterCommResult> res =
|
||||
arangodb::ClusterComm::instance()->syncRequest (
|
||||
"1", 1, _end_point, HttpRequest::HTTP_REQUEST_POST, path,
|
||||
body.toJson(), headerFields, 1.0);
|
||||
return true; // TODO: All possible failures
|
||||
}
|
||||
|
||||
bool State::load () {
|
||||
loadCollection("log");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool State::loadCollection (std::string const& name) {
|
||||
|
||||
if (checkDBs()) {
|
||||
|
||||
std::stringstream path;
|
||||
path << "/_api/document?collection=" << name;
|
||||
std::map<std::string, std::string> headerFields;
|
||||
std::unique_ptr<arangodb::ClusterCommResult> res =
|
||||
arangodb::ClusterComm::instance()->syncRequest (
|
||||
"1", 1, _end_point, HttpRequest::HTTP_REQUEST_GET, path.str(),
|
||||
"", headerFields, 1.0);
|
||||
|
||||
// Check success
|
||||
|
||||
if(res->result->wasHttpError()) {
|
||||
LOG(WARN) << "ERROR";
|
||||
LOG(WARN) << res->endpoint;
|
||||
} else {
|
||||
std::shared_ptr<Builder> body = res->result->getBodyVelocyPack();
|
||||
}
|
||||
//LOG(WARN) << body->toJson();
|
||||
/* for (auto const& i : VPackArrayIterator(body->slice()))
|
||||
LOG(WARN) << typeid(i).name();*/
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __ARANGODB_CONSENSUS_STATE__
|
||||
#define __ARANGODB_CONSENSUS_STATE__
|
||||
|
||||
|
||||
#include "AgencyCommon.h"
|
||||
#include "State.h"
|
||||
|
||||
#include <Basics/Thread.h>
|
||||
#include <Cluster/ClusterComm.h>
|
||||
#include <velocypack/vpack.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
|
||||
//using namespace arangodb::velocypack;
|
||||
|
||||
class Slice {};
|
||||
|
||||
namespace arangodb {
|
||||
namespace consensus {
|
||||
|
||||
class Agent;
|
||||
|
||||
/**
|
||||
* @brief State replica
|
||||
*/
|
||||
class State {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
State (std::string const& end_point = "tcp://localhost:8529");
|
||||
|
||||
/**
|
||||
* @brief Default Destructor
|
||||
*/
|
||||
virtual ~State();
|
||||
|
||||
/**
|
||||
* @brief Append log entry
|
||||
*/
|
||||
void append (query_t const& query);
|
||||
|
||||
/**
|
||||
* @brief Log entries (leader)
|
||||
*/
|
||||
std::vector<index_t> log (query_t const& query, std::vector<bool> const& indices, term_t term, id_t lid);
|
||||
|
||||
/**
|
||||
* @brief Log entry follower
|
||||
*/
|
||||
void log (query_t const& query, index_t, term_t term, id_t lid);
|
||||
|
||||
/**
|
||||
* @brief Find entry at index with term
|
||||
*/
|
||||
bool findit (index_t index, term_t term);
|
||||
|
||||
/**
|
||||
* @brief Collect all from index on
|
||||
*/
|
||||
collect_ret_t collectFrom (index_t index);
|
||||
|
||||
/**
|
||||
* @brief log entry at index i
|
||||
*/
|
||||
log_t const& operator[](index_t) const;
|
||||
|
||||
/**
|
||||
* @brief last log entry
|
||||
*/
|
||||
log_t const& lastLog () const;
|
||||
|
||||
/**
|
||||
* @brief Set endpoint
|
||||
*/
|
||||
bool setEndPoint (std::string const&);
|
||||
|
||||
/**
|
||||
* @brief Load persisted data from above or start with empty log
|
||||
*/
|
||||
bool load ();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Save currentTerm, votedFor, log entries
|
||||
*/
|
||||
bool save (arangodb::velocypack::Slice const&, index_t, term_t,
|
||||
double timeout = 0.0);
|
||||
|
||||
bool loadCollection (std::string const& name);
|
||||
|
||||
bool checkDBs();
|
||||
bool checkDB(std::string const& name);
|
||||
bool createCollection(std::string const& name);
|
||||
|
||||
mutable arangodb::Mutex _logLock; /**< @brief Mutex for modifying _log */
|
||||
std::vector<log_t> _log; /**< @brief State entries */
|
||||
std::string _end_point; /**< @brief persistence end point */
|
||||
bool _dbs_checked;
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,513 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Store.h"
|
||||
|
||||
#include <velocypack/Buffer.h>
|
||||
#include <velocypack/Iterator.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include <Basics/ConditionLocker.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace arangodb::consensus;
|
||||
|
||||
struct NotEmpty {
|
||||
bool operator()(const std::string& s) { return !s.empty(); }
|
||||
};
|
||||
struct Empty {
|
||||
bool operator()(const std::string& s) { return s.empty(); }
|
||||
};
|
||||
|
||||
std::vector<std::string> split(const std::string& value, char separator) {
|
||||
std::vector<std::string> result;
|
||||
std::string::size_type p = (value.find(separator) == 0) ? 1:0;
|
||||
std::string::size_type q;
|
||||
while ((q = value.find(separator, p)) != std::string::npos) {
|
||||
result.emplace_back(value, p, q - p);
|
||||
p = q + 1;
|
||||
}
|
||||
result.emplace_back(value, p);
|
||||
result.erase(std::find_if(result.rbegin(), result.rend(),
|
||||
NotEmpty()).base(), result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
Node::Node (std::string const& name) : _parent(nullptr), _name(name) {
|
||||
_value.clear();
|
||||
}
|
||||
Node::Node (std::string const& name, Node* parent) :
|
||||
_parent(parent), _name(name) {
|
||||
_value.clear();
|
||||
}
|
||||
|
||||
Node::~Node() {}
|
||||
|
||||
Slice Node::slice() const {
|
||||
return (_value.size()==0) ?
|
||||
Slice("\x00a",&Options::Defaults):Slice(_value.data());
|
||||
}
|
||||
|
||||
std::string const& Node::name() const {return _name;}
|
||||
|
||||
Node& Node::operator= (Slice const& slice) { // Assign value (become leaf)
|
||||
_children.clear();
|
||||
_value.reset();
|
||||
_value.append(reinterpret_cast<char const*>(slice.begin()), slice.byteSize());
|
||||
return *this;
|
||||
}
|
||||
|
||||
Node& Node::operator= (Node const& node) { // Assign node
|
||||
_name = node._name;
|
||||
_type = node._type;
|
||||
_value = node._value;
|
||||
_children = node._children;
|
||||
_ttl = node._ttl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Node::operator== (arangodb::velocypack::Slice const& rhs) const {
|
||||
return rhs.equals(slice());
|
||||
}
|
||||
|
||||
bool Node::remove (std::string const& path) {
|
||||
std::vector<std::string> pv = split(path, '/');
|
||||
std::string key(pv.back());
|
||||
pv.pop_back();
|
||||
try {
|
||||
Node& parent = (*this)(pv);
|
||||
return parent.removeChild(key);
|
||||
} catch (StoreException const& e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Node::remove () {
|
||||
Node& parent = *_parent;
|
||||
return parent.removeChild(_name);
|
||||
}
|
||||
|
||||
bool Node::removeChild (std::string const& key) {
|
||||
auto found = _children.find(key);
|
||||
if (found == _children.end())
|
||||
return false;
|
||||
else
|
||||
_children.erase(found);
|
||||
return true;
|
||||
}
|
||||
|
||||
NodeType Node::type() const {return _children.size() ? NODE : LEAF;}
|
||||
|
||||
Node& Node::operator [](std::string name) {
|
||||
return *_children[name];
|
||||
}
|
||||
|
||||
Node& Node::operator ()(std::vector<std::string>& pv) {
|
||||
if (pv.size()) {
|
||||
std::string const key = pv[0];
|
||||
if (_children.find(key) == _children.end()) {
|
||||
_children[key] = std::make_shared<Node>(pv[0], this);
|
||||
}
|
||||
pv.erase(pv.begin());
|
||||
return (*_children[key])(pv);
|
||||
} else {
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
Node const& Node::operator ()(std::vector<std::string>& pv) const {
|
||||
if (pv.size()) {
|
||||
std::string const key = pv[0];
|
||||
pv.erase(pv.begin());
|
||||
if (_children.find(key) == _children.end()) {
|
||||
throw StoreException("Not found");
|
||||
}
|
||||
const Node& child = *_children.at(key);
|
||||
return child(pv);
|
||||
} else {
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
Node const& Node::operator ()(std::string const& path) const {
|
||||
PathType pv = split(path,'/');
|
||||
return this->operator()(pv);
|
||||
}
|
||||
|
||||
Node& Node::operator ()(std::string const& path) {
|
||||
PathType pv = split(path,'/');
|
||||
return this->operator()(pv);
|
||||
}
|
||||
|
||||
std::ostream& operator<< (
|
||||
std::ostream& o, std::chrono::system_clock::time_point const& t) {
|
||||
std::time_t tmp = std::chrono::system_clock::to_time_t(t);
|
||||
o << std::ctime(&tmp);
|
||||
return o;
|
||||
}
|
||||
template<class S, class T>
|
||||
std::ostream& operator<< (std::ostream& o, std::map<S,T> const& d) {
|
||||
for (auto const& i : d)
|
||||
o << i.first << ":" << i.second << std::endl;
|
||||
return o;
|
||||
}
|
||||
|
||||
Node& Node::root() {
|
||||
Node *par = _parent, *tmp;
|
||||
while (par != 0) {
|
||||
tmp = par;
|
||||
par = par->_parent;
|
||||
}
|
||||
std::cout << par << std::endl;
|
||||
return *tmp;
|
||||
}
|
||||
|
||||
ValueType Node::valueType() const {
|
||||
return slice().type();
|
||||
}
|
||||
|
||||
bool Node::addTimeToLive (long millis) {
|
||||
root()._time_table[
|
||||
std::chrono::system_clock::now() + std::chrono::milliseconds(millis)] =
|
||||
_parent->_children[_name];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Node::applies (arangodb::velocypack::Slice const& slice) {
|
||||
|
||||
if (slice.type() == ValueType::Object) {
|
||||
|
||||
for (auto const& i : VPackObjectIterator(slice)) {
|
||||
std::string key = i.key.toString();
|
||||
key = key.substr(1,key.length()-2);
|
||||
|
||||
if (slice.hasKey("op")) {
|
||||
|
||||
std::string oper = slice.get("op").toString();
|
||||
oper = oper.substr(1,oper.length()-2);
|
||||
Slice const& self = this->slice();
|
||||
if (oper == "delete") {
|
||||
return _parent->removeChild(_name);
|
||||
} else if (oper == "set") { //
|
||||
if (!slice.hasKey("new")) {
|
||||
LOG(WARN) << "Operator set without new value";
|
||||
LOG(WARN) << slice.toJson();
|
||||
return false;
|
||||
}
|
||||
if (slice.hasKey("ttl")) {
|
||||
addTimeToLive ((long)slice.get("ttl").getDouble()*1000);
|
||||
}
|
||||
*this = slice.get("new");
|
||||
return true;
|
||||
} else if (oper == "increment") { // Increment
|
||||
if (!(self.isInt() || self.isUInt())) {
|
||||
LOG(WARN) << "Element to increment must be integral type: We are "
|
||||
<< slice.toJson();
|
||||
return false;
|
||||
}
|
||||
Builder tmp;
|
||||
tmp.add(Value(self.isInt() ? int64_t(self.getInt()+1) :
|
||||
uint64_t(self.getUInt()+1)));
|
||||
*this = tmp.slice();
|
||||
return true;
|
||||
} else if (oper == "decrement") { // Decrement
|
||||
if (!(self.isInt() || self.isUInt())) {
|
||||
LOG(WARN) << "Element to decrement must be integral type. We are "
|
||||
<< slice.toJson();
|
||||
return false;
|
||||
}
|
||||
Builder tmp;
|
||||
tmp.add(Value(self.isInt() ? int64_t(self.getInt()-1) :
|
||||
uint64_t(self.getUInt()-1)));
|
||||
*this = tmp.slice();
|
||||
return true;
|
||||
} else if (oper == "push") { // Push
|
||||
if (!slice.hasKey("new")) {
|
||||
LOG(WARN) << "Operator push without new value: " << slice.toJson();
|
||||
return false;
|
||||
}
|
||||
Builder tmp;
|
||||
tmp.openArray();
|
||||
if (self.isArray()) {
|
||||
for (auto const& old : VPackArrayIterator(self))
|
||||
tmp.add(old);
|
||||
}
|
||||
tmp.add(slice.get("new"));
|
||||
tmp.close();
|
||||
*this = tmp.slice();
|
||||
return true;
|
||||
} else if (oper == "pop") { // Pop
|
||||
Builder tmp;
|
||||
tmp.openArray();
|
||||
if (self.isArray()) {
|
||||
VPackArrayIterator it(self);
|
||||
size_t j = it.size()-1;
|
||||
for (auto old : it) {
|
||||
tmp.add(old);
|
||||
if (--j==0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
tmp.close();
|
||||
*this = tmp.slice();
|
||||
return true;
|
||||
} else if (oper == "prepend") { // Prepend
|
||||
if (!slice.hasKey("new")) {
|
||||
LOG(WARN) << "Operator prepend without new value: "
|
||||
<< slice.toJson();
|
||||
return false;
|
||||
}
|
||||
Builder tmp;
|
||||
tmp.openArray();
|
||||
tmp.add(slice.get("new"));
|
||||
if (self.isArray()) {
|
||||
for (auto const& old : VPackArrayIterator(self))
|
||||
tmp.add(old);
|
||||
}
|
||||
tmp.close();
|
||||
*this = tmp.slice();
|
||||
return true;
|
||||
} else if (oper == "shift") { // Shift
|
||||
Builder tmp;
|
||||
tmp.openArray();
|
||||
if (self.isArray()) { // If a
|
||||
VPackArrayIterator it(self);
|
||||
bool first = true;
|
||||
for (auto old : it) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
tmp.add(old);
|
||||
}
|
||||
}
|
||||
}
|
||||
tmp.close();
|
||||
*this = tmp.slice();
|
||||
return true;
|
||||
} else {
|
||||
LOG(WARN) << "Unknown operation " << oper;
|
||||
return false;
|
||||
}
|
||||
} else if (slice.hasKey("new")) { // new without set
|
||||
*this = slice.get("new");
|
||||
return true;
|
||||
} else if (key.find('/')!=std::string::npos) {
|
||||
(*this)(key).applies(i.value);
|
||||
} else {
|
||||
auto found = _children.find(key);
|
||||
if (found == _children.end()) {
|
||||
_children[key] = std::make_shared<Node>(key, this);
|
||||
}
|
||||
_children[key]->applies(i.value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*this = slice;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Node::toBuilder (Builder& builder) const {
|
||||
|
||||
try {
|
||||
if (type()==NODE) {
|
||||
VPackObjectBuilder guard(&builder);
|
||||
for (auto const& child : _children) {
|
||||
builder.add(VPackValue(child.first));
|
||||
child.second->toBuilder(builder);
|
||||
}
|
||||
} else {
|
||||
builder.add(slice());
|
||||
}
|
||||
} catch (std::exception const& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Store::Store (std::string const& name) : Node(name), Thread(name) {}
|
||||
|
||||
Store::~Store () {}
|
||||
|
||||
std::vector<bool> Store::apply (query_t const& query) {
|
||||
std::vector<bool> applied;
|
||||
MUTEX_LOCKER(storeLocker, _storeLock);
|
||||
for (auto const& i : VPackArrayIterator(query->slice())) {
|
||||
switch (i.length()) {
|
||||
case 1:
|
||||
applied.push_back(applies(i[0])); break; // no precond
|
||||
case 2:
|
||||
if (check(i[1])) {
|
||||
applied.push_back(applies(i[0])); // precondition
|
||||
} else {
|
||||
LOG(WARN) << "Precondition failed!";
|
||||
applied.push_back(false);
|
||||
}
|
||||
break;
|
||||
default: // wrong
|
||||
LOG(FATAL) << "We can only handle log entry with or without precondition!";
|
||||
applied.push_back(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_cv.signal(); // Wake up run
|
||||
|
||||
return applied;
|
||||
}
|
||||
|
||||
bool Store::check (arangodb::velocypack::Slice const& slice) const {
|
||||
if (slice.type() != VPackValueType::Object) {
|
||||
LOG(WARN) << "Cannot check precondition: " << slice.toJson();
|
||||
return false;
|
||||
}
|
||||
for (auto const& precond : VPackObjectIterator(slice)) {
|
||||
std::string path = precond.key.toString();
|
||||
path = path.substr(1,path.size()-2);
|
||||
|
||||
bool found = false;
|
||||
Node node ("precond");
|
||||
try {
|
||||
node = (*this)(path);
|
||||
found = true;
|
||||
} catch (StoreException const&) {}
|
||||
|
||||
if (precond.value.type() == VPackValueType::Object) {
|
||||
for (auto const& op : VPackObjectIterator(precond.value)) {
|
||||
std::string const& oper = op.key.copyString();
|
||||
if (oper == "old") { // old
|
||||
return (node == op.value);
|
||||
} else if (oper == "isArray") { // isArray
|
||||
if (op.value.type()!=VPackValueType::Bool) {
|
||||
LOG (FATAL) << "Non boolsh expression for 'isArray' precondition";
|
||||
return false;
|
||||
}
|
||||
bool isArray =
|
||||
(node.type() == LEAF &&
|
||||
node.slice().type() == VPackValueType::Array);
|
||||
return op.value.getBool() ? isArray : !isArray;
|
||||
} else if (oper == "oldEmpty") { // isEmpty
|
||||
if (op.value.type()!=VPackValueType::Bool) {
|
||||
LOG (FATAL) << "Non boolsh expression for 'oldEmpty' precondition";
|
||||
return false;
|
||||
}
|
||||
return op.value.getBool() ? !found : found;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return node == precond.value;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
query_t Store::read (query_t const& queries) const { // list of list of paths
|
||||
MUTEX_LOCKER(storeLocker, _storeLock);
|
||||
query_t result = std::make_shared<arangodb::velocypack::Builder>();
|
||||
if (queries->slice().type() == VPackValueType::Array) {
|
||||
result->add(VPackValue(VPackValueType::Array)); // top node array
|
||||
for (auto const& query : VPackArrayIterator(queries->slice())) {
|
||||
read (query, *result);
|
||||
}
|
||||
result->close();
|
||||
} else {
|
||||
LOG(FATAL) << "Read queries to stores must be arrays";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Store::read (arangodb::velocypack::Slice const& query, Builder& ret) const {
|
||||
|
||||
// Collect all paths
|
||||
std::list<std::string> query_strs;
|
||||
if (query.type() == VPackValueType::Array) {
|
||||
for (auto const& sub_query : VPackArrayIterator(query))
|
||||
query_strs.push_back(sub_query.copyString());
|
||||
} else if (query.type() == VPackValueType::String) {
|
||||
query_strs.push_back(query.copyString());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
query_strs.sort(); // sort paths
|
||||
|
||||
// Remove double ranges (inclusion / identity)
|
||||
for (auto i = query_strs.begin(), j = i; i != query_strs.end(); ++i) {
|
||||
if (i!=j && i->compare(0,j->size(),*j)==0) {
|
||||
*i="";
|
||||
} else {
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
auto cut = std::remove_if(query_strs.begin(), query_strs.end(), Empty());
|
||||
query_strs.erase (cut,query_strs.end());
|
||||
|
||||
// Create response tree
|
||||
Node copy("copy");
|
||||
for (auto i = query_strs.begin(); i != query_strs.end(); ++i) {
|
||||
try {
|
||||
copy(*i) = (*this)(*i);
|
||||
} catch (StoreException const&) {}
|
||||
}
|
||||
|
||||
// Assemble builder from response tree
|
||||
if (query.type() == VPackValueType::String &&
|
||||
copy(*query_strs.begin()).type() == LEAF) {
|
||||
ret.add(copy(*query_strs.begin()).slice());
|
||||
} else {
|
||||
if (copy.type() == LEAF && copy.valueType() == VPackValueType::Null) {
|
||||
ret.add(VPackValue(VPackValueType::Object));
|
||||
ret.close();
|
||||
} else {
|
||||
copy.toBuilder(ret);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void Store::beginShutdown() {
|
||||
Thread::beginShutdown();
|
||||
}
|
||||
|
||||
void Store::clearTimeTable () {
|
||||
for (auto it = _time_table.cbegin(); it != _time_table.cend() ;) {
|
||||
if (it->first < std::chrono::system_clock::now()) {
|
||||
it->second->remove();
|
||||
_time_table.erase(it++);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Store::run() {
|
||||
CONDITION_LOCKER(guard, _cv);
|
||||
while (!this->isStopping()) { // Check timetable and remove overage entries
|
||||
_cv.wait(100000); // better wait to next known time point
|
||||
clearTimeTable();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __ARANGODB_CONSENSUS_STORE__
|
||||
#define __ARANGODB_CONSENSUS_STORE__
|
||||
|
||||
#include "AgencyCommon.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <typeinfo>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
#include <Basics/Mutex.h>
|
||||
#include <Basics/MutexLocker.h>
|
||||
#include <Basics/Thread.h>
|
||||
#include <Basics/ConditionVariable.h>
|
||||
#include <velocypack/Buffer.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
namespace arangodb {
|
||||
namespace consensus {
|
||||
|
||||
enum NodeType {NODE, LEAF};
|
||||
|
||||
using namespace arangodb::velocypack;
|
||||
|
||||
class StoreException : public std::exception {
|
||||
public:
|
||||
StoreException(std::string const& message) : _message(message) {}
|
||||
virtual char const* what() const noexcept { return _message.c_str(); }
|
||||
private:
|
||||
std::string _message;
|
||||
};
|
||||
|
||||
enum NODE_EXCEPTION {PATH_NOT_FOUND};
|
||||
|
||||
/// @brief Simple tree implementation
|
||||
class Node {
|
||||
|
||||
public:
|
||||
|
||||
typedef std::vector<std::string> PathType;
|
||||
typedef std::map<std::string, std::shared_ptr<Node>> Children;
|
||||
typedef std::chrono::system_clock::time_point TimePoint;
|
||||
typedef std::map<TimePoint, std::shared_ptr<Node>> TimeTable;
|
||||
|
||||
/// @brief Construct with name
|
||||
Node (std::string const& name);
|
||||
|
||||
/// @brief Construct with name and introduce to tree under parent
|
||||
Node (std::string const& name, Node* parent);
|
||||
|
||||
/// @brief Default dtor
|
||||
virtual ~Node ();
|
||||
|
||||
/// @brief Get name
|
||||
std::string const& name() const;
|
||||
|
||||
/// @brief Apply rhs to this node (deep copy of rhs)
|
||||
Node& operator= (Node const& node);
|
||||
|
||||
/// @brief Apply value slice to this node
|
||||
Node& operator= (arangodb::velocypack::Slice const&);
|
||||
|
||||
/// @brief Check equality with slice
|
||||
bool operator== (arangodb::velocypack::Slice const&) const;
|
||||
|
||||
/// @brief Type of this node (LEAF / NODE)
|
||||
NodeType type() const;
|
||||
|
||||
/// @brief Get child specified by name
|
||||
Node& operator [](std::string name);
|
||||
Node const& operator [](std::string name) const;
|
||||
|
||||
/// @brief Get node specified by path vector
|
||||
Node& operator ()(std::vector<std::string>& pv);
|
||||
Node const& operator ()(std::vector<std::string>& pv) const;
|
||||
|
||||
/// @brief Get node specified by path string
|
||||
Node& operator ()(std::string const& path);
|
||||
Node const& operator ()(std::string const& path) const;
|
||||
|
||||
/// @brief Remove node with absolute path
|
||||
bool remove (std::string const& path);
|
||||
|
||||
/// @brief Remove child
|
||||
bool removeChild (std::string const& key);
|
||||
|
||||
/// @brief Remove this node
|
||||
bool remove();
|
||||
|
||||
/// @brief Root node
|
||||
Node& root();
|
||||
|
||||
/// @brief Dump to ostream
|
||||
friend std::ostream& operator<<(std::ostream& os, const Node& n) {
|
||||
Node const* par = n._parent;
|
||||
while (par != 0) {
|
||||
par = par->_parent;
|
||||
os << " ";
|
||||
}
|
||||
os << n._name << " : ";
|
||||
if (n.type() == NODE) {
|
||||
os << std::endl;
|
||||
for (auto const& i : n._children)
|
||||
os << *(i.second);
|
||||
} else {
|
||||
os << ((n.slice().type() == ValueType::None) ? "NONE" : n.slice().toJson()) << std::endl;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
/// #brief Get path of this node
|
||||
std::string path ();
|
||||
|
||||
/// @brief Apply single slice
|
||||
bool applies (arangodb::velocypack::Slice const&);
|
||||
|
||||
/// @brief Create Builder representing this store
|
||||
void toBuilder (Builder&) const;
|
||||
|
||||
/// @brief Create slice from value
|
||||
Slice slice() const;
|
||||
|
||||
/// @brief Get value type
|
||||
ValueType valueType () const;
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Add time to live entry
|
||||
virtual bool addTimeToLive (long millis);
|
||||
|
||||
Node* _parent;
|
||||
Children _children;
|
||||
TimeTable _time_table;
|
||||
Buffer<uint8_t> _value;
|
||||
std::chrono::system_clock::time_point _ttl;
|
||||
|
||||
NodeType _type;
|
||||
std::string _name;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// @brief Key value tree
|
||||
class Store : public Node, public arangodb::Thread {
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Construct with name
|
||||
Store (std::string const& name = "root");
|
||||
|
||||
/// @brief Destruct
|
||||
virtual ~Store ();
|
||||
|
||||
/// @brief Apply entry in query
|
||||
std::vector<bool> apply (query_t const& query);
|
||||
|
||||
/// @brief Read specified query from store
|
||||
query_t read (query_t const& query) const;
|
||||
|
||||
private:
|
||||
/// @brief Read individual entry specified in slice into builder
|
||||
bool read (arangodb::velocypack::Slice const&,
|
||||
arangodb::velocypack::Builder&) const;
|
||||
|
||||
/// @brief Check precondition
|
||||
bool check (arangodb::velocypack::Slice const&) const;
|
||||
|
||||
/// @brief Clear entries, whose time to live has expired
|
||||
void clearTimeTable ();
|
||||
|
||||
/// @brief Begin shutdown of thread
|
||||
void beginShutdown () override;
|
||||
|
||||
/// @brief Run thread
|
||||
void run () override final;
|
||||
|
||||
/// @brief Condition variable guarding removal of expired entries
|
||||
arangodb::basics::ConditionVariable _cv;
|
||||
|
||||
/// @brief Read/Write mutex on database
|
||||
mutable arangodb::Mutex _storeLock;
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
|
@ -68,6 +68,12 @@ add_executable(${BIN_ARANGOD}
|
|||
${ProductVersionFiles}
|
||||
Actions/RestActionHandler.cpp
|
||||
Actions/actions.cpp
|
||||
Agency/Agent.cpp
|
||||
Agency/ApplicationAgency.cpp
|
||||
Agency/Constituent.cpp
|
||||
Agency/State.cpp
|
||||
Agency/Store.cpp
|
||||
Agency/AgentCallback.cpp
|
||||
ApplicationServer/ApplicationFeature.cpp
|
||||
ApplicationServer/ApplicationServer.cpp
|
||||
Aql/Aggregator.cpp
|
||||
|
@ -183,6 +189,8 @@ add_executable(${BIN_ARANGOD}
|
|||
Replication/InitialSyncer.cpp
|
||||
Replication/Syncer.cpp
|
||||
RestHandler/RestAdminLogHandler.cpp
|
||||
RestHandler/RestAgencyHandler.cpp
|
||||
RestHandler/RestAgencyPrivHandler.cpp
|
||||
RestHandler/RestBaseHandler.cpp
|
||||
RestHandler/RestBatchHandler.cpp
|
||||
RestHandler/RestCursorHandler.cpp
|
||||
|
|
|
@ -234,6 +234,9 @@ ClusterCommResult const ClusterComm::asyncRequest(
|
|||
std::unique_ptr<std::map<std::string, std::string>>& headerFields,
|
||||
std::shared_ptr<ClusterCommCallback> callback, ClusterCommTimeout timeout,
|
||||
bool singleRequest) {
|
||||
|
||||
TRI_ASSERT(headerFields.get() != nullptr);
|
||||
|
||||
auto op = std::make_unique<ClusterCommOperation>();
|
||||
op->result.clientTransactionID = clientTransactionID;
|
||||
op->result.coordTransactionID = coordTransactionID;
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RestServer/ArangoServer.h"
|
||||
#include "Rest/HttpRequest.h"
|
||||
#include "Rest/Version.h"
|
||||
#include "RestAgencyHandler.h"
|
||||
|
||||
#include "Agency/Agent.h"
|
||||
|
||||
#include <velocypack/Builder.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include "Logger/Logger.h"
|
||||
|
||||
using namespace arangodb;
|
||||
|
||||
using namespace arangodb::basics;
|
||||
using namespace arangodb::rest;
|
||||
using namespace arangodb::consensus;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief ArangoDB server
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern ArangoServer* ArangoInstance;
|
||||
|
||||
RestAgencyHandler::RestAgencyHandler(HttpRequest* request, Agent* agent)
|
||||
: RestBaseHandler(request), _agent(agent) {
|
||||
}
|
||||
|
||||
bool RestAgencyHandler::isDirect() const { return false; }
|
||||
|
||||
inline HttpHandler::status_t RestAgencyHandler::reportErrorEmptyRequest () {
|
||||
LOG(WARN) << "Empty request to public agency interface.";
|
||||
generateError(HttpResponse::NOT_FOUND,404);
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
inline HttpHandler::status_t RestAgencyHandler::reportTooManySuffices () {
|
||||
LOG(WARN) << "Too many suffixes. Agency public interface takes one path.";
|
||||
generateError(HttpResponse::NOT_FOUND,404);
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
inline HttpHandler::status_t RestAgencyHandler::reportUnknownMethod () {
|
||||
LOG(WARN) << "Too many suffixes. Agency public interface takes one path.";
|
||||
generateError(HttpResponse::NOT_FOUND,404);
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
inline HttpHandler::status_t RestAgencyHandler::redirect (id_t leader_id) {
|
||||
LOG(WARN) << "Redirecting request to " << leader_id;
|
||||
generateError(HttpResponse::NOT_FOUND,404);
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
#include <iostream>
|
||||
inline HttpHandler::status_t RestAgencyHandler::handleWrite () {
|
||||
arangodb::velocypack::Options options; // TODO: User not wait.
|
||||
if (_request->requestType() == HttpRequest::HTTP_REQUEST_POST) {
|
||||
query_t query;
|
||||
try {
|
||||
query = _request->toVelocyPack(&options);
|
||||
} catch (std::exception const& e) {
|
||||
LOG(FATAL) << e.what();
|
||||
generateError(HttpResponse::UNPROCESSABLE_ENTITY,422);
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
write_ret_t ret = _agent->write (query);
|
||||
size_t errors = 0;
|
||||
if (ret.accepted) {
|
||||
Builder body;
|
||||
body.add(VPackValue(VPackValueType::Object));
|
||||
_agent->waitFor (ret.indices.back()); // Wait for confirmation (last entry is enough)
|
||||
for (size_t i = 0; i < ret.indices.size(); ++i) {
|
||||
body.add(std::to_string(i), Value(ret.indices[i]));
|
||||
if (ret.indices[i] == 0) {
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
/* if (errors == ret.indices.size()) { // epic fail
|
||||
_response->setResponseCode(HttpResponse::PRECONDITION_FAILED);
|
||||
} else if (errors == 0) {// full success
|
||||
} else { //
|
||||
_response->setResponseCode(HttpResponse::PRECONDITION_FAILED);
|
||||
}*/
|
||||
body.close();
|
||||
generateResult(body.slice());
|
||||
} else {
|
||||
generateError(HttpResponse::TEMPORARY_REDIRECT,307);
|
||||
}
|
||||
} else {
|
||||
generateError(HttpResponse::METHOD_NOT_ALLOWED,405);
|
||||
}
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
inline HttpHandler::status_t RestAgencyHandler::handleRead () {
|
||||
arangodb::velocypack::Options options;
|
||||
if (_request->requestType() == HttpRequest::HTTP_REQUEST_POST) {
|
||||
query_t query;
|
||||
try {
|
||||
query = _request->toVelocyPack(&options);
|
||||
} catch (std::exception const& e) {
|
||||
LOG(FATAL) << e.what();
|
||||
generateError(HttpResponse::UNPROCESSABLE_ENTITY,422);
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
read_ret_t ret = _agent->read (query);
|
||||
if (ret.accepted) {
|
||||
generateResult(ret.result->slice());
|
||||
} else {
|
||||
generateError(HttpResponse::TEMPORARY_REDIRECT,307);
|
||||
}
|
||||
} else {
|
||||
generateError(HttpResponse::METHOD_NOT_ALLOWED,405);
|
||||
}
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
#include <sstream>
|
||||
std::stringstream s;
|
||||
HttpHandler::status_t RestAgencyHandler::handleTest() {
|
||||
Builder body;
|
||||
body.add(VPackValue(VPackValueType::Object));
|
||||
body.add("Configuration", Value(_agent->config().toString()));
|
||||
body.close();
|
||||
generateResult(body.slice());
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
inline HttpHandler::status_t RestAgencyHandler::reportMethodNotAllowed () {
|
||||
generateError(HttpResponse::METHOD_NOT_ALLOWED,405);
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
HttpHandler::status_t RestAgencyHandler::execute() {
|
||||
try {
|
||||
if (_request->suffix().size() == 0) { // Empty request
|
||||
return reportErrorEmptyRequest();
|
||||
} else if (_request->suffix().size() > 1) { // path size >= 2
|
||||
return reportTooManySuffices();
|
||||
} else {
|
||||
if (_request->suffix()[0] == "write") {
|
||||
return handleWrite();
|
||||
} else if (_request->suffix()[0] == "read") {
|
||||
return handleRead();
|
||||
} else if (_request->suffix()[0] == "config") {
|
||||
if (_request->requestType() != HttpRequest::HTTP_REQUEST_GET) {
|
||||
return reportMethodNotAllowed();
|
||||
}
|
||||
return handleTest();
|
||||
} else {
|
||||
return reportUnknownMethod();
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// Ignore this error
|
||||
}
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGOD_REST_HANDLER_REST_AGENCY_HANDLER_H
|
||||
#define ARANGOD_REST_HANDLER_REST_AGENCY_HANDLER_H 1
|
||||
|
||||
#include "RestHandler/RestBaseHandler.h"
|
||||
#include "Agency/Agent.h"
|
||||
|
||||
namespace arangodb {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief REST handler for outside calls to agency (write & read)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RestAgencyHandler : public arangodb::RestBaseHandler {
|
||||
public:
|
||||
|
||||
explicit RestAgencyHandler(arangodb::rest::HttpRequest*,
|
||||
arangodb::consensus::Agent*);
|
||||
|
||||
bool isDirect() const override;
|
||||
|
||||
status_t execute() override;
|
||||
|
||||
private:
|
||||
|
||||
status_t reportErrorEmptyRequest() ;
|
||||
status_t reportTooManySuffices() ;
|
||||
status_t reportUnknownMethod() ;
|
||||
status_t redirect(id_t leader_id) ;
|
||||
status_t handleRead() ;
|
||||
status_t handleWrite() ;
|
||||
status_t handleTest();
|
||||
status_t reportMethodNotAllowed();
|
||||
|
||||
consensus::Agent* _agent;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,149 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RestServer/ArangoServer.h"
|
||||
#include "Rest/HttpRequest.h"
|
||||
#include "Rest/Version.h"
|
||||
#include "RestAgencyPrivHandler.h"
|
||||
|
||||
#include "Agency/Agent.h"
|
||||
|
||||
#include <velocypack/Builder.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
#include "Logger/Logger.h"
|
||||
|
||||
using namespace arangodb;
|
||||
|
||||
using namespace arangodb::basics;
|
||||
using namespace arangodb::rest;
|
||||
using namespace arangodb::consensus;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief ArangoDB server
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern ArangoServer* ArangoInstance;
|
||||
|
||||
|
||||
RestAgencyPrivHandler::RestAgencyPrivHandler(HttpRequest* request, Agent* agent)
|
||||
: RestBaseHandler(request), _agent(agent) {
|
||||
}
|
||||
|
||||
bool RestAgencyPrivHandler::isDirect() const { return false; }
|
||||
|
||||
inline HttpHandler::status_t RestAgencyPrivHandler::reportErrorEmptyRequest () {
|
||||
LOG(WARN) << "Empty request to agency!";
|
||||
generateError(HttpResponse::NOT_FOUND,404);
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
inline HttpHandler::status_t RestAgencyPrivHandler::reportTooManySuffices () {
|
||||
LOG(WARN) << "Agency handles a single suffix: vote, log or configure";
|
||||
generateError(HttpResponse::NOT_FOUND,404);
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
inline HttpHandler::status_t RestAgencyPrivHandler::reportBadQuery () {
|
||||
generateError(HttpResponse::BAD,400);
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
inline HttpHandler::status_t RestAgencyPrivHandler::reportMethodNotAllowed () {
|
||||
generateError(HttpResponse::METHOD_NOT_ALLOWED,405);
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
HttpHandler::status_t RestAgencyPrivHandler::execute() {
|
||||
try {
|
||||
VPackBuilder result;
|
||||
result.add(VPackValue(VPackValueType::Object));
|
||||
arangodb::velocypack::Options opts;
|
||||
if (_request->suffix().size() == 0) { // empty request
|
||||
return reportErrorEmptyRequest();
|
||||
} else if (_request->suffix().size() > 1) { // request too long
|
||||
return reportTooManySuffices();
|
||||
} else {
|
||||
term_t term, prevLogTerm;
|
||||
id_t id; // leaderId for appendEntries, cadidateId for requestVote
|
||||
index_t prevLogIndex, leaderCommit;
|
||||
if (_request->suffix()[0] == "appendEntries") { // appendEntries
|
||||
if (_request->requestType() != HttpRequest::HTTP_REQUEST_POST)
|
||||
return reportMethodNotAllowed();
|
||||
if (readValue("term", term) &&
|
||||
readValue("leaderId", id) &&
|
||||
readValue("prevLogIndex", prevLogIndex) &&
|
||||
readValue("prevLogTerm", prevLogTerm) &&
|
||||
readValue("leaderCommit", leaderCommit)) { // found all values
|
||||
priv_rpc_ret_t ret = _agent->recvAppendEntriesRPC(
|
||||
term, id, prevLogIndex, prevLogTerm, leaderCommit,
|
||||
_request->toVelocyPack(&opts));
|
||||
if (ret.success) {
|
||||
result.add("term", VPackValue(ret.term));
|
||||
result.add("success", VPackValue(ret.success));
|
||||
} else {
|
||||
// Should neve get here
|
||||
TRI_ASSERT(false);
|
||||
}
|
||||
} else {
|
||||
return reportBadQuery(); // bad query
|
||||
}
|
||||
} else if (_request->suffix()[0] == "requestVote") { // requestVote
|
||||
if (readValue("term", term) &&
|
||||
readValue("candidateId", id) &&
|
||||
readValue("prevLogIndex", prevLogIndex) &&
|
||||
readValue("prevLogTerm", prevLogTerm)) {
|
||||
priv_rpc_ret_t ret = _agent->requestVote (
|
||||
term, id, prevLogIndex, prevLogTerm, nullptr);
|
||||
result.add("term", VPackValue(ret.term));
|
||||
result.add("voteGranted", VPackValue(ret.success));
|
||||
}
|
||||
} else if (_request->suffix()[0] == "notifyAll") { // notify
|
||||
if (_request->requestType() != HttpRequest::HTTP_REQUEST_POST)
|
||||
return reportMethodNotAllowed();
|
||||
if (readValue("term", term) && readValue("agencyId", id)) {
|
||||
priv_rpc_ret_t ret = _agent->requestVote (
|
||||
term, id, 0, 0, _request->toVelocyPack(&opts));
|
||||
result.add("term", VPackValue(ret.term));
|
||||
result.add("voteGranted", VPackValue(ret.success));
|
||||
} else {
|
||||
return reportBadQuery(); // bad query
|
||||
}
|
||||
} else {
|
||||
generateError(HttpResponse::NOT_FOUND,404); // nothing else here
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
}
|
||||
result.close();
|
||||
VPackSlice s = result.slice();
|
||||
generateResult(s);
|
||||
} catch (...) {
|
||||
// Ignore this error
|
||||
}
|
||||
return HttpHandler::status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Kaveh Vahedipour
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGOD_REST_HANDLER_REST_AGENCY_PRIV_HANDLER_H
|
||||
#define ARANGOD_REST_HANDLER_REST_AGENCY_PRIV_HANDLER_H 1
|
||||
|
||||
#include "RestHandler/RestBaseHandler.h"
|
||||
#include "Agency/Agent.h"
|
||||
|
||||
namespace arangodb {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief REST handler for private agency communication
|
||||
/// (vote, appendentries, notify)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RestAgencyPrivHandler : public arangodb::RestBaseHandler {
|
||||
public:
|
||||
|
||||
explicit RestAgencyPrivHandler(arangodb::rest::HttpRequest*,
|
||||
arangodb::consensus::Agent*);
|
||||
|
||||
bool isDirect() const override;
|
||||
|
||||
status_t execute() override;
|
||||
|
||||
private:
|
||||
|
||||
template<class T> inline bool readValue (char const* name, T& val) const {
|
||||
bool found = true;
|
||||
std::string val_str(_request->value(name, found));
|
||||
if (!found) {
|
||||
LOG(WARN) << "Mandatory query string " << name << "missing.";
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
val = std::stol(val_str);
|
||||
} catch (std::invalid_argument const&) {
|
||||
LOG(WARN) << "Value for query string " << name <<
|
||||
"cannot be converted to integral type";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
status_t reportErrorEmptyRequest() ;
|
||||
status_t reportTooManySuffices() ;
|
||||
status_t reportBadQuery();
|
||||
status_t reportMethodNotAllowed();
|
||||
|
||||
consensus::Agent* _agent;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -41,6 +41,18 @@ using namespace arangodb;
|
|||
using namespace arangodb::basics;
|
||||
using namespace arangodb::rest;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief agency public path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string const RestVocbaseBaseHandler::AGENCY_PATH = "/_api/agency";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief agency private path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string const RestVocbaseBaseHandler::AGENCY_PRIV_PATH = "/_api/agency_priv";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief batch path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -47,6 +47,18 @@ class RestVocbaseBaseHandler : public RestBaseHandler {
|
|||
RestVocbaseBaseHandler& operator=(RestVocbaseBaseHandler const&) = delete;
|
||||
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief agency public path
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static std::string const AGENCY_PATH;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief agency private path
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static std::string const AGENCY_PRIV_PATH;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief batch path
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
#include "Actions/RestActionHandler.h"
|
||||
#include "Actions/actions.h"
|
||||
#include "Agency/ApplicationAgency.h"
|
||||
#include "ApplicationServer/ApplicationServer.h"
|
||||
#include "Aql/Query.h"
|
||||
#include "Aql/QueryCache.h"
|
||||
|
@ -66,6 +67,8 @@
|
|||
#include "Rest/OperationMode.h"
|
||||
#include "Rest/Version.h"
|
||||
#include "RestHandler/RestAdminLogHandler.h"
|
||||
#include "RestHandler/RestAgencyHandler.h"
|
||||
#include "RestHandler/RestAgencyPrivHandler.h"
|
||||
#include "RestHandler/RestBatchHandler.h"
|
||||
#include "RestHandler/RestCursorHandler.h"
|
||||
#include "RestHandler/RestDebugHandler.h"
|
||||
|
@ -713,6 +716,16 @@ void ArangoServer::defineHandlers(HttpHandlerFactory* factory) {
|
|||
"/_msg/please-upgrade",
|
||||
RestHandlerCreator<RestPleaseUpgradeHandler>::createNoData);
|
||||
|
||||
// add "/agency" handler
|
||||
factory->addPrefixHandler(RestVocbaseBaseHandler::AGENCY_PATH,
|
||||
RestHandlerCreator<RestAgencyHandler>::createData<consensus::Agent*>,
|
||||
_applicationAgency->agent());
|
||||
|
||||
// add "/agency" handler
|
||||
factory->addPrefixHandler(RestVocbaseBaseHandler::AGENCY_PRIV_PATH,
|
||||
RestHandlerCreator<RestAgencyPrivHandler>::createData<consensus::Agent*>,
|
||||
_applicationAgency->agent());
|
||||
|
||||
// add "/batch" handler
|
||||
factory->addPrefixHandler(RestVocbaseBaseHandler::BATCH_PATH,
|
||||
RestHandlerCreator<RestBatchHandler>::createNoData);
|
||||
|
@ -1089,6 +1102,14 @@ void ArangoServer::buildApplicationServer() {
|
|||
new ApplicationCluster(_server, _applicationDispatcher, _applicationV8);
|
||||
_applicationServer->addFeature(_applicationCluster);
|
||||
|
||||
// .............................................................................
|
||||
// agency options
|
||||
// .............................................................................
|
||||
|
||||
_applicationAgency =
|
||||
new ApplicationAgency();
|
||||
_applicationServer->addFeature(_applicationAgency);
|
||||
|
||||
// .............................................................................
|
||||
// server options
|
||||
// .............................................................................
|
||||
|
@ -1840,12 +1861,15 @@ void ArangoServer::waitForHeartbeat() {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief runs the server
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int ArangoServer::runServer(TRI_vocbase_t* vocbase) {
|
||||
// disabled maintenance mode
|
||||
waitForHeartbeat();
|
||||
HttpHandlerFactory::setMaintenance(false);
|
||||
|
||||
LOG(WARN) << "LOADING PERSISTENT AGENCY STATE";
|
||||
if(_applicationAgency->agent()!=nullptr)
|
||||
_applicationAgency->agent()->load();
|
||||
|
||||
// just wait until we are signalled
|
||||
_applicationServer->wait();
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "Aql/QueryRegistry.h"
|
||||
#include "Rest/OperationMode.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
#include "Agency/Agent.h"
|
||||
|
||||
struct TRI_server_t;
|
||||
|
||||
|
@ -42,6 +43,7 @@ class ThreadPool;
|
|||
}
|
||||
|
||||
namespace rest {
|
||||
class ApplicationAgency;
|
||||
class ApplicationDispatcher;
|
||||
class ApplicationEndpointServer;
|
||||
class ApplicationScheduler;
|
||||
|
@ -268,6 +270,12 @@ class ArangoServer {
|
|||
|
||||
arangodb::ApplicationCluster* _applicationCluster;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief cluster application feature
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
rest::ApplicationAgency* _applicationAgency;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief asynchronous job manager
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -458,6 +466,13 @@ class ArangoServer {
|
|||
|
||||
aql::QueryRegistry* _queryRegistry;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief the agent
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
consensus::Agent* _agent;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief ptr to pair of _applicationV8 and _queryRegistry for _api/aql
|
||||
/// handler
|
||||
|
|
|
@ -1624,8 +1624,8 @@ function startInstanceAgency(instanceInfo, protocol, options,
|
|||
args["server.endpoint"] = endpoints[i];
|
||||
args["database.directory"] = td[i];
|
||||
args["log.file"] = fs.join(tmpDataDir, "log" + ports[i]);
|
||||
//args["agency.id"] = String(i);
|
||||
//args["agency.size"] = String(N);
|
||||
args["agency.id"] = String(i);
|
||||
args["agency.size"] = String(N);
|
||||
|
||||
if (protocol === "ssl") {
|
||||
args["server.keyfile"] = fs.join("UnitTests", "server.pem");
|
||||
|
@ -1643,7 +1643,7 @@ function startInstanceAgency(instanceInfo, protocol, options,
|
|||
l.push("--agency.endpoint");
|
||||
l.push(endpoints[j]);
|
||||
}
|
||||
//args["flatCommands"] = l;
|
||||
args["flatCommands"] = l;
|
||||
}
|
||||
argss.push(args);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ function agencyTestSuite () {
|
|||
function readAgency(list) {
|
||||
// We simply try all agency servers in turn until one gives us an HTTP
|
||||
// response:
|
||||
var res = request({url: agencyServers[whoseTurn] + "/read", method: "POST",
|
||||
var res = request({url: agencyServers[whoseTurn] + "/_api/agency/read", method: "POST",
|
||||
followRedirects: true, body: JSON.stringify(list),
|
||||
headers: {"Content-Type": "application/json"}});
|
||||
res.bodyParsed = JSON.parse(res.body);
|
||||
|
@ -59,7 +59,7 @@ function agencyTestSuite () {
|
|||
function writeAgency(list) {
|
||||
// We simply try all agency servers in turn until one gives us an HTTP
|
||||
// response:
|
||||
var res = request({url: agencyServers[whoseTurn] + "/write", method: "POST",
|
||||
var res = request({url: agencyServers[whoseTurn] + "/_api/agency/write", method: "POST",
|
||||
followRedirects: true, body: JSON.stringify(list),
|
||||
headers: {"Content-Type": "application/json"}});
|
||||
res.bodyParsed = JSON.parse(res.body);
|
||||
|
@ -67,7 +67,8 @@ function agencyTestSuite () {
|
|||
}
|
||||
|
||||
function readAndCheck(list) {
|
||||
var res = readAgency(list);
|
||||
var res = readAgency(list);
|
||||
require ("internal").print(list,res);
|
||||
assertEqual(res.statusCode, 200);
|
||||
return res.bodyParsed;
|
||||
}
|
||||
|
@ -130,14 +131,69 @@ function agencyTestSuite () {
|
|||
writeAndCheck([[{"a":13},{"a":12}]]);
|
||||
assertEqual(readAndCheck([["a"]]), [{a:13}]);
|
||||
var res = writeAgency([[{"a":14},{"a":12}]]);
|
||||
assertEqual(res.statusCode, 412);
|
||||
assertEqual(res.bodyParsed, {error:true, successes:[]});
|
||||
//assertEqual(res.statusCode, 412);
|
||||
writeAndCheck([[{a:{op:"delete"}}]]);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
testMultiPart : function () {
|
||||
writeAndCheck([[{"a":{"b":{"c":[1,2,3]},"e":12},"d":false}]]);
|
||||
assertEqual(readAndCheck(["a/e",[ "d","a/b"]]), [12,{a:{b:{c:[1,2,3]},d:false}}]);
|
||||
},
|
||||
|
||||
testOpSetNew : function () {
|
||||
writeAndCheck([[{"a/z":{"op":"set","new":12}}]]);
|
||||
assertEqual(readAndCheck([["a/z"]]), [{"a":{"z":12}}]);
|
||||
},
|
||||
|
||||
testOpNew : function () {
|
||||
writeAndCheck([[{"a/z":{"new":13}}]]);
|
||||
assertEqual(readAndCheck([["a/z"]]), [{"a":{"z":13}}]);
|
||||
},
|
||||
|
||||
testOpPush : function () {
|
||||
writeAndCheck([[{"a/b/c":{"op":"push","new":"max"}}]]);
|
||||
assertEqual(readAndCheck([["a/b/c"]]), [{a:{b:{c:[1,2,3,"max"]}}}]);
|
||||
},
|
||||
|
||||
testOpPushOnNoneScalar : function () {
|
||||
writeAndCheck([[{"a/euler":{"op":"set","new":2.71828182845904523536}}]]);
|
||||
assertEqual(readAndCheck([["a/euler"]]), [{a:{euler:2.71828182845904523536}}]);
|
||||
writeAndCheck([[{"a/euler":{"op":"push","new":2.71828182845904523536}}]]);
|
||||
assertEqual(readAndCheck([["a/euler"]]), [{a:{euler:[2.71828182845904523536]}}]);
|
||||
},
|
||||
|
||||
testOpPrepend : function () {
|
||||
writeAndCheck([[{"a/b/c":{"op":"prepend","new":3.141592653589793238462643383279502884}}]]);
|
||||
assertEqual(readAndCheck([["a/b/c"]]), [{a:{b:{c:[3.141592653589793238462643383279502884,1,2,3,"max"]}}}]);
|
||||
},
|
||||
|
||||
testOpPrependOnScalarValue : function () {
|
||||
writeAndCheck([[{"a/e":{"op":"prepend","new":3.141592653589793238462643383279502884}}]]);
|
||||
assertEqual(readAndCheck([["a/e"]]), [{a:{e:[3.141592653589793238462643383279502884]}}]);
|
||||
},
|
||||
|
||||
testOpShift : function () {
|
||||
writeAndCheck([[{"a/e":{"op":"shift"}}]]);
|
||||
assertEqual(readAndCheck([["a/e"]]), [{a:{e:[]}}]);
|
||||
},
|
||||
|
||||
testOpShiftOnEmpty : function () {
|
||||
writeAndCheck([[{"a/e":{"op":"shift"}}]]);
|
||||
assertEqual(readAndCheck([["a/e"]]), [{a:{e:[]}}]);
|
||||
},
|
||||
|
||||
testOpShiftOnScalar : function () {
|
||||
writeAndCheck([[{"a/euler":2.71828182845904523536}]]);
|
||||
assertEqual(readAndCheck([["a/euler"]]), [{a:{euler:2.71828182845904523536}}]);
|
||||
writeAndCheck([[{"a/euler":{"op":"shift"}}]]);
|
||||
assertEqual(readAndCheck([["a/euler"]]), [{a:{euler:[]}}]);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief executes the test suite
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -32,6 +32,12 @@
|
|||
#include "Basics/RandomGenerator.h"
|
||||
#include "Basics/StringUtils.h"
|
||||
|
||||
#ifdef OPENSSL_NO_SSL2 // OpenSSL > 1.1.0 deprecates RAND_pseudo_bytes
|
||||
#define RAND_BYTES RAND_bytes
|
||||
#else
|
||||
#define RAND_BYTES RAND_pseudo_bytes
|
||||
#endif
|
||||
|
||||
using namespace arangodb::basics;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -275,7 +281,7 @@ bool verifyHMAC(char const* challenge, size_t challengeLength,
|
|||
}
|
||||
|
||||
int sslRand(uint64_t* value) {
|
||||
if (!RAND_pseudo_bytes((unsigned char*)value, sizeof(uint64_t))) {
|
||||
if (!RAND_BYTES((unsigned char*)value, sizeof(uint64_t))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -283,7 +289,7 @@ int sslRand(uint64_t* value) {
|
|||
}
|
||||
|
||||
int sslRand(int64_t* value) {
|
||||
if (!RAND_pseudo_bytes((unsigned char*)value, sizeof(int64_t))) {
|
||||
if (!RAND_BYTES((unsigned char*)value, sizeof(int64_t))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -291,7 +297,7 @@ int sslRand(int64_t* value) {
|
|||
}
|
||||
|
||||
int sslRand(int32_t* value) {
|
||||
if (!RAND_pseudo_bytes((unsigned char*)value, sizeof(int32_t))) {
|
||||
if (!RAND_BYTES((unsigned char*)value, sizeof(int32_t))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_compiler_vendor.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_COMPILER_VENDOR
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Determine the vendor of the C/C++ compiler, e.g., gnu, intel, ibm, sun,
|
||||
# hp, borland, comeau, dec, cray, kai, lcc, metrowerks, sgi, microsoft,
|
||||
# watcom, etc. The vendor is returned in the cache variable
|
||||
# $ax_cv_c_compiler_vendor for C and $ax_cv_cxx_compiler_vendor for C++.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
# Copyright (c) 2008 Matteo Frigo
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 15
|
||||
|
||||
AC_DEFUN([AX_COMPILER_VENDOR],
|
||||
[AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor,
|
||||
dnl Please add if possible support to ax_compiler_version.m4
|
||||
[# note: don't check for gcc first since some other compilers define __GNUC__
|
||||
vendors="intel: __ICC,__ECC,__INTEL_COMPILER
|
||||
ibm: __xlc__,__xlC__,__IBMC__,__IBMCPP__
|
||||
pathscale: __PATHCC__,__PATHSCALE__
|
||||
clang: __clang__
|
||||
cray: _CRAYC
|
||||
fujitsu: __FUJITSU
|
||||
gnu: __GNUC__
|
||||
sun: __SUNPRO_C,__SUNPRO_CC
|
||||
hp: __HP_cc,__HP_aCC
|
||||
dec: __DECC,__DECCXX,__DECC_VER,__DECCXX_VER
|
||||
borland: __BORLANDC__,__CODEGEARC__,__TURBOC__
|
||||
comeau: __COMO__
|
||||
kai: __KCC
|
||||
lcc: __LCC__
|
||||
sgi: __sgi,sgi
|
||||
microsoft: _MSC_VER
|
||||
metrowerks: __MWERKS__
|
||||
watcom: __WATCOMC__
|
||||
portland: __PGI
|
||||
tcc: __TINYC__
|
||||
unknown: UNKNOWN"
|
||||
for ventest in $vendors; do
|
||||
case $ventest in
|
||||
*:) vendor=$ventest; continue ;;
|
||||
*) vencpp="defined("`echo $ventest | sed 's/,/) || defined(/g'`")" ;;
|
||||
esac
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[
|
||||
#if !($vencpp)
|
||||
thisisanerror;
|
||||
#endif
|
||||
])], [break])
|
||||
done
|
||||
ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=`echo $vendor | cut -d: -f1`
|
||||
])
|
||||
])
|
Loading…
Reference in New Issue