From dc5cfeccd98dcdd3a696eac9164d3fdf131e1873 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 27 Jan 2016 11:33:21 +0100 Subject: [PATCH 01/61] Staring implementation of the agency --- arangod/Agency/Agent.cpp | 22 +++++ arangod/Agency/Agent.h | 67 ++++++++++++++ arangod/Agency/ApplicationAgency.cpp | 100 ++++++++++++++++++++ arangod/Agency/ApplicationAgency.h | 131 +++++++++++++++++++++++++++ arangod/Agency/Constituent.cpp | 55 +++++++++++ arangod/Agency/Constituent.h | 110 ++++++++++++++++++++++ arangod/Agency/Log.cpp | 10 ++ arangod/Agency/Log.h | 27 ++++++ config/config.h.in | 25 +---- 9 files changed, 524 insertions(+), 23 deletions(-) create mode 100644 arangod/Agency/Agent.cpp create mode 100644 arangod/Agency/Agent.h create mode 100644 arangod/Agency/ApplicationAgency.cpp create mode 100644 arangod/Agency/ApplicationAgency.h create mode 100644 arangod/Agency/Constituent.cpp create mode 100644 arangod/Agency/Constituent.h create mode 100644 arangod/Agency/Log.cpp create mode 100644 arangod/Agency/Log.h diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp new file mode 100644 index 0000000000..092af2321c --- /dev/null +++ b/arangod/Agency/Agent.cpp @@ -0,0 +1,22 @@ +#include "Agent.h" + +using namespace arangodb::consensus; + +Agent::Agent () {} + +Agent::Agent (AgentConfig const&) {} + +Agent::Agent (Agent const&) {} + +Agent::~Agent() {} + +Slice const& Agent::log (const Slice& slice) { + if (_constituent.Leader()) + return _log.log(slice); + else + return redirect(slice); +} + +Slice const& Agent::redirect (const Slice& slice) { + return slice; //TODO +} diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h new file mode 100644 index 0000000000..601f7df408 --- /dev/null +++ b/arangod/Agency/Agent.h @@ -0,0 +1,67 @@ +#ifndef __ARANGODB_CONSENSUS_AGENT__ +#define __ARANGODB_CONSENSUS_AGENT__ + +#include "Constituent.h" +#include "Log.h" + +namespace arangodb { + namespace consensus { + + class Agent { + + public: + /** + * @brief Host descriptor + */ + + template struct host_t { + std::string host; + std::string port; + T vote; + }; + + /** + * @brief Agent configuration + */ + template struct AgentConfig { + T min_ping; + T max_ping; + std::vector > start_hosts; + }; + + /** + * @brief Default ctor + */ + Agent (); + + /** + * @brief Construct with program options \n + * One process starts with options, the \n remaining start with list of peers and gossip. + */ + Agent (AgentConfig const&); + + /** + * @brief Copy ctor + */ + Agent (Agent const&); + + /** + * @brief Clean up + */ + virtual ~Agent(); + + /** + * @brief + */ + Slice const& log (Slice const&); + Slice const& redirect (Slice const&); + + private: + Constituent _constituent; /**< @brief Leader election delegate */ + Log _log; /**< @brief Log replica */ + + }; + + }} + +#endif diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp new file mode 100644 index 0000000000..3cb4804dcc --- /dev/null +++ b/arangod/Agency/ApplicationAgency.cpp @@ -0,0 +1,100 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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 Dr. Kaveh Vahedipour +//////////////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 +#include "Basics/win-utils.h" +#endif + + +#include "Basics/logging.h" +#include "Scheduler/PeriodicTask.h" + +#include "ApplicationAgency.h" + +using namespace std; +using namespace arangodb::basics; +using namespace arangodb::rest; + +ApplicationAgency::ApplicationAgency() + : ApplicationFeature("agency"), + _reportInterval(0.0), + _nrStandardThreads(0) {} + + +ApplicationAgency::~ApplicationAgency() { /*delete _dispatcher;*/ } + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the number of used threads +//////////////////////////////////////////////////////////////////////////////// + +size_t ApplicationAgency::numberOfThreads() { + return _nrStandardThreads /* + _nrAQLThreads */; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief sets the processor affinity +//////////////////////////////////////////////////////////////////////////////// + +void ApplicationAgency::setupOptions( + std::map& options) { + options["Server Options:help-admin"]("dispatcher.report-interval", + &_reportInterval, + "dispatcher report interval"); +} + + +bool ApplicationAgency::prepare() { + if (_disabled) { + return true; + } + return true; +} + + +bool ApplicationAgency::start() { + if (_disabled) { + return true; + } + return true; +} + + +bool ApplicationAgency::open() { return true; } + + +void ApplicationAgency::close() { + if (_disabled) { + return; + } +} + + +void ApplicationAgency::stop() { + if (_disabled) { + return; + } +} + + + diff --git a/arangod/Agency/ApplicationAgency.h b/arangod/Agency/ApplicationAgency.h new file mode 100644 index 0000000000..0b0d18b429 --- /dev/null +++ b/arangod/Agency/ApplicationAgency.h @@ -0,0 +1,131 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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 Dr. Kaveh Vahedipour +//////////////////////////////////////////////////////////////////////////////// + +#ifndef ARANGOD_AGENCY_APPLICATION_AGENCY_H +#define ARANGOD_AGENCY_APPLICATION_AGENCY_H 1 + +#include "Basics/Common.h" + +#include "ApplicationServer/ApplicationFeature.h" +#include "ApplicationAgency.h" + + +namespace arangodb { +namespace rest { +class ApplicationScheduler; +//class Dispatcher; +class Task; + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief application server with agency +//////////////////////////////////////////////////////////////////////////////// + + 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 numberOfThreads(); + + ////////////////////////////////////////////////////////////////////////////// + /// @brief sets the processor affinity + ////////////////////////////////////////////////////////////////////////////// + + void setProcessorAffinity(std::vector const& cores); + + + public: + + void setupOptions(std::map&); + + + bool prepare(); + + + bool start(); + + + bool open(); + + + void close(); + + + void stop(); + + + private: + + ////////////////////////////////////////////////////////////////////////////// + /// @brief interval for reports + ////////////////////////////////////////////////////////////////////////////// + + double _reportInterval; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief total number of standard threads + ////////////////////////////////////////////////////////////////////////////// + + size_t _nrStandardThreads; + +}; +} +} + +#endif + + diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp new file mode 100644 index 0000000000..e1685db18a --- /dev/null +++ b/arangod/Agency/Constituent.cpp @@ -0,0 +1,55 @@ +#include "Constituent.h" + +using namespace arangodb::consensus; + +Constituent::Constituent () : + _term(0), _gen(std::random_device()()) { +} + +Constituent::Constituent (const uint32_t n) : _votes(std::vector(n)) { +} + +Constituent::Constituent (const constituency_t& constituency) : + _gen(std::random_device()()) { +} + +Constituent::~Constituent() {} + +Constituent::duration_t Constituent::SleepFor () { + dist_t dis(0., 1.); + return Constituent::duration_t(dis(_gen)); +} + +Constituent::term_t Constituent::term() const { + return _term; +} + +void Constituent::runForLeaderShip (bool b) { +} + +Constituent::state_t Constituent::state () const { + return _state; +} + +Constituent::mode_t Constituent::mode () const { + return _mode; +} + +void Constituent::Gossip (const Constituent::constituency_t& constituency) { + // Talk to all peers i have not talked to + // Answer by sending my complete list of peers +} + +const Constituent::constituency_t& Constituent::Gossip () { + return _constituency; +} + +bool Constituent::Leader() const { + return _mode==LEADER; +} + +void Constituent::CallElection() {} + + + + diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h new file mode 100644 index 0000000000..91fc177a15 --- /dev/null +++ b/arangod/Agency/Constituent.h @@ -0,0 +1,110 @@ +#ifndef __ARANGODB_CONSENSUS_CONSTITUENT__ +#define __ARANGODB_CONSENSUS_CONSTITUENT__ + +#include +#include +#include +#include +#include + +namespace arangodb { + namespace consensus { + + /** + * @brief Raft leader election + */ + class Constituent { + + public: + + enum mode_t { + LEADER, CANDIDATE, FOLLOWER + }; + typedef std::chrono::duration duration_t; + typedef uint64_t term_t; + typedef uint32_t id_t; + struct constituent_t { + id_t id; + std::string address; + std::string port; + }; + typedef std::vector constituency_t; + typedef uint32_t state_t; + typedef std::uniform_real_distribution dist_t; + + /** + * brief Construct with size of constituency + */ + Constituent (); + + /** + * brief Construct with size of constituency + */ + Constituent (const uint32_t n); + + /** + * brief Construct with subset of peers + */ + Constituent (constituency_t const & constituency); + + /** + * @brief Clean up and exit election + */ + virtual ~Constituent (); + + term_t term() const; + + void runForLeaderShip (bool b); + + state_t state () const; + + mode_t mode () const; + + /** + * @brief Gossip protocol: listen + */ + void Gossip (Constituent::constituency_t const& constituency); + + /** + * @brief Gossip protocol: talk + */ + const Constituent::constituency_t& Gossip (); + + bool Leader() const; + + private: + + /** + * @brief Call for vote (by leader or candidates after timeout) + */ + void CallElection (); + + /** + * @brief Count my votes + */ + void CountVotes(); + + /** + * @brief Sleep for how long + */ + duration_t SleepFor (); + + term_t _term; /**< @brief term number */ + id_t _leader_id; /**< @brief Current leader */ + id_t _cur_vote; /**< @brief My current vote */ + constituency_t _constituency; /**< @brief List of consituents */ + uint32_t _nvotes; /**< @brief Votes in my favour + * (candidate/leader) */ + state_t _state; /**< @brief State (follower, + * candidate, leader)*/ + std::mt19937 _gen; + + mode_t _mode; + + std::vector _votes; + + }; + + }} + +#endif //__ARANGODB_CONSENSUS_CONSTITUENT__ diff --git a/arangod/Agency/Log.cpp b/arangod/Agency/Log.cpp new file mode 100644 index 0000000000..3f3688e325 --- /dev/null +++ b/arangod/Agency/Log.cpp @@ -0,0 +1,10 @@ +#include "Log.h" + +using namespace arangodb::consensus; + +Log::Log() {} +Log::~Log() {} + +Slice const& Log::log (Slice const& slice) { + return slice; +} diff --git a/arangod/Agency/Log.h b/arangod/Agency/Log.h new file mode 100644 index 0000000000..41921db6e1 --- /dev/null +++ b/arangod/Agency/Log.h @@ -0,0 +1,27 @@ +#include + +//using namespace arangodb::velocypack; + +class Slice {}; + +namespace arangodb { + namespace consensus { + + class Log { + + public: + typedef uint64_t index_t; + + Log (); + virtual ~Log(); + + Slice const& log (Slice const&); + + private: + + index_t _commit_id; /**< @brief: index of highest log entry known + to be committed (initialized to 0, increases monotonically) */ + index_t _last_applied; /**< @brief: index of highest log entry applied to state machine */ + }; + + }} diff --git a/config/config.h.in b/config/config.h.in index ace4656d60..cdaa568194 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -21,9 +21,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H -/* Define to 1 if you have the `tcmalloc' library (-ltcmalloc). */ -#undef HAVE_LIBTCMALLOC - /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H @@ -33,9 +30,6 @@ /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD -/* Define to 1 if you have the header file. */ -#undef HAVE_READLINE_READLINE_H - /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H @@ -100,29 +94,14 @@ /* true if failure testing ins enabled */ #undef TRI_ENABLE_FAILURE_TESTS -/* true if figures are enabled */ -#undef TRI_ENABLE_FIGURES - -/* true if high resolution clock should be used */ -#undef TRI_ENABLE_HIRES_FIGURES - /* true if logging is enabled */ #undef TRI_ENABLE_LOGGER -/* true if timing is enabled */ -#undef TRI_ENABLE_LOGGER_TIMING - /* true if maintainer mode is enabled */ #undef TRI_ENABLE_MAINTAINER_MODE -/* true if timing is enabled */ -#undef TRI_ENABLE_TIMING - -/* true if linenoise is used */ -#undef TRI_HAVE_LINENOISE - -/* true if readline is used */ -#undef TRI_HAVE_READLINE +/* true if tcmalloc is used */ +#undef TRI_HAVE_TCMALLOC /* true if we have the linux splice api */ #undef TRI_LINUX_SPLICE From 08710aa2df3e020a8b52f76d143d80648aa5be8f Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 27 Jan 2016 14:32:20 +0100 Subject: [PATCH 02/61] Adding agency rest handler --- arangod/Agency/Agent.cpp | 16 +- arangod/Agency/Agent.h | 121 +++++------ arangod/Agency/ApplicationAgency.cpp | 18 +- arangod/Agency/ApplicationAgency.h | 14 +- arangod/Agency/Constituent.cpp | 35 ++-- arangod/Agency/Constituent.h | 192 +++++++++--------- arangod/Agency/Log.h | 50 +++-- arangod/Makefile.files | 5 + .../RestHandler/RestVocbaseBaseHandler.cpp | 6 + arangod/RestHandler/RestVocbaseBaseHandler.h | 6 + arangod/RestServer/ArangoServer.cpp | 6 + 11 files changed, 248 insertions(+), 221 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 092af2321c..55678dfd26 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -8,15 +8,19 @@ Agent::Agent (AgentConfig const&) {} Agent::Agent (Agent const&) {} -Agent::~Agent() {} +Agent::~Agent () {} + +Constituent::term_t Agent::term () const { + return _constituent.term(); +} Slice const& Agent::log (const Slice& slice) { - if (_constituent.Leader()) - return _log.log(slice); - else - return redirect(slice); + if (_constituent.leader()) + return _log.log(slice); + else + return redirect(slice); } Slice const& Agent::redirect (const Slice& slice) { - return slice; //TODO + return slice; //TODO } diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 601f7df408..4d02fd1d6f 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -5,63 +5,68 @@ #include "Log.h" namespace arangodb { - namespace consensus { - - class Agent { - - public: - /** - * @brief Host descriptor - */ - - template struct host_t { - std::string host; - std::string port; - T vote; - }; - - /** - * @brief Agent configuration - */ - template struct AgentConfig { - T min_ping; - T max_ping; - std::vector > start_hosts; - }; - - /** - * @brief Default ctor - */ - Agent (); - - /** - * @brief Construct with program options \n - * One process starts with options, the \n remaining start with list of peers and gossip. - */ - Agent (AgentConfig const&); - - /** - * @brief Copy ctor - */ - Agent (Agent const&); - - /** - * @brief Clean up - */ - virtual ~Agent(); - - /** - * @brief - */ - Slice const& log (Slice const&); - Slice const& redirect (Slice const&); - - private: - Constituent _constituent; /**< @brief Leader election delegate */ - Log _log; /**< @brief Log replica */ - - }; - - }} +namespace consensus { + + class Agent { + + public: + /** + * @brief Host descriptor + */ + + template struct host_t { + std::string host; + std::string port; + T vote; + }; + + /** + * @brief Agent configuration + */ + template struct AgentConfig { + T min_ping; + T max_ping; + std::vector > start_hosts; + }; + + /** + * @brief Default ctor + */ + Agent (); + + /** + * @brief Construct with program options \n + * One process starts with options, the \n remaining start with list of peers and gossip. + */ + Agent (AgentConfig const&); + + /** + * @brief Copy ctor + */ + Agent (Agent const&); + + /** + * @brief Clean up + */ + virtual ~Agent(); + + /** + * @brief Get current term + */ + Constituent::term_t term() const; + + /** + * @brief + */ + Slice const& log (Slice const&); + Slice const& redirect (Slice const&); + + private: + Constituent _constituent; /**< @brief Leader election delegate */ + Log _log; /**< @brief Log replica */ + + }; + +}} #endif diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index 3cb4804dcc..f70f94be90 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -36,34 +36,24 @@ using namespace arangodb::basics; using namespace arangodb::rest; ApplicationAgency::ApplicationAgency() - : ApplicationFeature("agency"), - _reportInterval(0.0), - _nrStandardThreads(0) {} + : ApplicationFeature("agency"), _size(5) {} ApplicationAgency::~ApplicationAgency() { /*delete _dispatcher;*/ } -//////////////////////////////////////////////////////////////////////////////// -/// @brief returns the number of used threads -//////////////////////////////////////////////////////////////////////////////// - -size_t ApplicationAgency::numberOfThreads() { - return _nrStandardThreads /* + _nrAQLThreads */; -} - //////////////////////////////////////////////////////////////////////////////// /// @brief sets the processor affinity //////////////////////////////////////////////////////////////////////////////// void ApplicationAgency::setupOptions( std::map& options) { - options["Server Options:help-admin"]("dispatcher.report-interval", - &_reportInterval, - "dispatcher report interval"); + options["Agency Options:help-agency"]("agency.size", &_size, "Agency size"); } + + bool ApplicationAgency::prepare() { if (_disabled) { return true; diff --git a/arangod/Agency/ApplicationAgency.h b/arangod/Agency/ApplicationAgency.h index 0b0d18b429..333e86a862 100644 --- a/arangod/Agency/ApplicationAgency.h +++ b/arangod/Agency/ApplicationAgency.h @@ -79,7 +79,7 @@ class Task; /// @brief returns the number of used threads ////////////////////////////////////////////////////////////////////////////// - size_t numberOfThreads(); + size_t size(); ////////////////////////////////////////////////////////////////////////////// /// @brief sets the processor affinity @@ -110,17 +110,7 @@ class Task; private: - ////////////////////////////////////////////////////////////////////////////// - /// @brief interval for reports - ////////////////////////////////////////////////////////////////////////////// - - double _reportInterval; - - ////////////////////////////////////////////////////////////////////////////// - /// @brief total number of standard threads - ////////////////////////////////////////////////////////////////////////////// - - size_t _nrStandardThreads; + uint64_t _size; /**< @brief: agency size (default: 5)*/ }; } diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index e1685db18a..4a9a27c7c6 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -3,52 +3,53 @@ using namespace arangodb::consensus; Constituent::Constituent () : - _term(0), _gen(std::random_device()()) { + _term(0), _gen(std::random_device()()), _mode(FOLLOWER) { } -Constituent::Constituent (const uint32_t n) : _votes(std::vector(n)) { +Constituent::Constituent (const uint32_t n) : + _mode(FOLLOWER), _votes(std::vector(n)) { } Constituent::Constituent (const constituency_t& constituency) : - _gen(std::random_device()()) { + _gen(std::random_device()()), _mode(FOLLOWER) { } Constituent::~Constituent() {} -Constituent::duration_t Constituent::SleepFor () { - dist_t dis(0., 1.); - return Constituent::duration_t(dis(_gen)); +Constituent::duration_t Constituent::sleepFor () { + dist_t dis(0., 1.); + return Constituent::duration_t(dis(_gen)); } Constituent::term_t Constituent::term() const { - return _term; + return _term; } void Constituent::runForLeaderShip (bool b) { } Constituent::state_t Constituent::state () const { - return _state; + return _state; } Constituent::mode_t Constituent::mode () const { - return _mode; + return _mode; } -void Constituent::Gossip (const Constituent::constituency_t& constituency) { - // Talk to all peers i have not talked to - // Answer by sending my complete list of peers +void Constituent::gossip (const Constituent::constituency_t& constituency) { + // Talk to all peers i have not talked to + // Answer by sending my complete list of peers } -const Constituent::constituency_t& Constituent::Gossip () { - return _constituency; +const Constituent::constituency_t& Constituent::gossip () { + return _constituency; } -bool Constituent::Leader() const { - return _mode==LEADER; +bool Constituent::leader() const { + return _mode==LEADER; } -void Constituent::CallElection() {} +void Constituent::callElection() {} diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h index 91fc177a15..4b6e3f6019 100644 --- a/arangod/Agency/Constituent.h +++ b/arangod/Agency/Constituent.h @@ -8,103 +8,103 @@ #include namespace arangodb { - namespace consensus { +namespace consensus { - /** - * @brief Raft leader election - */ - class Constituent { +/** + * @brief Raft leader election + */ +class Constituent { + +public: + + enum mode_t { + LEADER, CANDIDATE, FOLLOWER + }; + typedef std::chrono::duration duration_t; + typedef uint64_t term_t; + typedef uint32_t id_t; + struct constituent_t { + id_t id; + std::string address; + std::string port; + }; + typedef std::vector constituency_t; + typedef uint32_t state_t; + typedef std::uniform_real_distribution dist_t; + + /** + * brief Construct with size of constituency + */ + Constituent (); + + /** + * brief Construct with size of constituency + */ + Constituent (const uint32_t n); + + /** + * brief Construct with subset of peers + */ + Constituent (constituency_t const & constituency); + + /** + * @brief Clean up and exit election + */ + virtual ~Constituent (); + + term_t term() const; + + void runForLeaderShip (bool b); + + state_t state () const; + + mode_t mode () const; + + /** + * @brief Gossip protocol: listen + */ + void gossip (Constituent::constituency_t const& constituency); + + /** + * @brief Gossip protocol: talk + */ + const Constituent::constituency_t& gossip (); + + bool leader() const; - public: - - enum mode_t { - LEADER, CANDIDATE, FOLLOWER - }; - typedef std::chrono::duration duration_t; - typedef uint64_t term_t; - typedef uint32_t id_t; - struct constituent_t { - id_t id; - std::string address; - std::string port; - }; - typedef std::vector constituency_t; - typedef uint32_t state_t; - typedef std::uniform_real_distribution dist_t; - - /** - * brief Construct with size of constituency - */ - Constituent (); - - /** - * brief Construct with size of constituency - */ - Constituent (const uint32_t n); - - /** - * brief Construct with subset of peers - */ - Constituent (constituency_t const & constituency); - - /** - * @brief Clean up and exit election - */ - virtual ~Constituent (); - - term_t term() const; - - void runForLeaderShip (bool b); - - state_t state () const; - - mode_t mode () const; - - /** - * @brief Gossip protocol: listen - */ - void Gossip (Constituent::constituency_t const& constituency); - - /** - * @brief Gossip protocol: talk - */ - const Constituent::constituency_t& Gossip (); - - bool Leader() const; - - private: - - /** - * @brief Call for vote (by leader or candidates after timeout) - */ - void CallElection (); - - /** - * @brief Count my votes - */ - void CountVotes(); - - /** - * @brief Sleep for how long - */ - duration_t SleepFor (); - - term_t _term; /**< @brief term number */ - id_t _leader_id; /**< @brief Current leader */ - id_t _cur_vote; /**< @brief My current vote */ - constituency_t _constituency; /**< @brief List of consituents */ - uint32_t _nvotes; /**< @brief Votes in my favour - * (candidate/leader) */ - state_t _state; /**< @brief State (follower, - * candidate, leader)*/ - std::mt19937 _gen; - - mode_t _mode; - - std::vector _votes; - - }; - - }} +private: + + /** + * @brief Call for vote (by leader or candidates after timeout) + */ + void callElection (); + + /** + * @brief Count my votes + */ + void countVotes(); + + /** + * @brief Sleep for how long + */ + duration_t sleepFor (); + + term_t _term; /**< @brief term number */ + id_t _leader_id; /**< @brief Current leader */ + id_t _cur_vote; /**< @brief My current vote */ + constituency_t _constituency; /**< @brief List of consituents */ + uint32_t _nvotes; /**< @brief Votes in my favour + * (candidate/leader) */ + state_t _state; /**< @brief State (follower, + * candidate, leader)*/ + std::mt19937 _gen; + + mode_t _mode; + + std::vector _votes; + +}; + +}} #endif //__ARANGODB_CONSENSUS_CONSTITUENT__ diff --git a/arangod/Agency/Log.h b/arangod/Agency/Log.h index 41921db6e1..5b432ea758 100644 --- a/arangod/Agency/Log.h +++ b/arangod/Agency/Log.h @@ -5,23 +5,37 @@ class Slice {}; namespace arangodb { - namespace consensus { - - class Log { +namespace consensus { - public: - typedef uint64_t index_t; - - Log (); - virtual ~Log(); - - Slice const& log (Slice const&); - - private: - - index_t _commit_id; /**< @brief: index of highest log entry known - to be committed (initialized to 0, increases monotonically) */ - index_t _last_applied; /**< @brief: index of highest log entry applied to state machine */ - }; +/** + * @brief Log repilca + */ +class Log { + +public: + typedef uint64_t index_t; - }} + /** + * @brief Default constructor + */ + Log (); + + /** + * @brief Default Destructor + */ + virtual ~Log(); + + /** + * @brief Log + */ + + Slice const& log (Slice const&); + +private: + + index_t _commit_id; /**< @brief: index of highest log entry known + to be committed (initialized to 0, increases monotonically) */ + index_t _last_applied; /**< @brief: index of highest log entry applied to state machine */ +}; + +}} diff --git a/arangod/Makefile.files b/arangod/Makefile.files index bd137efbf4..1c4b2dcaa1 100644 --- a/arangod/Makefile.files +++ b/arangod/Makefile.files @@ -15,6 +15,10 @@ arangod_libarangod_a_CPPFLAGS = \ arangod_libarangod_a_SOURCES = \ arangod/Actions/actions.cpp \ arangod/Actions/RestActionHandler.cpp \ + arangod/Agency/ApplicationAgency.cpp \ + arangod/Agency/Agent.cpp \ + arangod/Agency/Constituent.cpp \ + arangod/Agency/Log.cpp \ arangod/ApplicationServer/ApplicationFeature.cpp \ arangod/ApplicationServer/ApplicationServer.cpp \ arangod/Aql/Aggregator.cpp \ @@ -130,6 +134,7 @@ arangod_libarangod_a_SOURCES = \ arangod/Replication/Syncer.cpp \ arangod/Rest/AnyServer.cpp \ arangod/RestHandler/RestAdminLogHandler.cpp \ + arangod/RestHandler/RestAgencyHandler.cpp \ arangod/RestHandler/RestBaseHandler.cpp \ arangod/RestHandler/RestBatchHandler.cpp \ arangod/RestHandler/RestCursorHandler.cpp \ diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.cpp b/arangod/RestHandler/RestVocbaseBaseHandler.cpp index 3ef1238b29..09b95134dd 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.cpp +++ b/arangod/RestHandler/RestVocbaseBaseHandler.cpp @@ -42,6 +42,12 @@ using namespace arangodb::basics; using namespace arangodb::rest; +//////////////////////////////////////////////////////////////////////////////// +/// @brief batch path +//////////////////////////////////////////////////////////////////////////////// + +std::string const RestVocbaseBaseHandler::AGENCY_PATH = "/_api/agency"; + //////////////////////////////////////////////////////////////////////////////// /// @brief batch path //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.h b/arangod/RestHandler/RestVocbaseBaseHandler.h index 3a4a56f37d..0b3a36006d 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.h +++ b/arangod/RestHandler/RestVocbaseBaseHandler.h @@ -52,6 +52,12 @@ class RestVocbaseBaseHandler : public admin::RestBaseHandler { public: + ////////////////////////////////////////////////////////////////////////////// + /// @brief agency path + ////////////////////////////////////////////////////////////////////////////// + + static std::string const AGENCY_PATH; + ////////////////////////////////////////////////////////////////////////////// /// @brief batch path ////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 4c24758b01..7a5b356ab2 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -28,6 +28,7 @@ #include "Actions/RestActionHandler.h" #include "Actions/actions.h" +#include "Agency/ApplicationAgency.h" #include "Aql/Query.h" #include "Aql/QueryCache.h" #include "Aql/RestAqlHandler.h" @@ -56,6 +57,7 @@ #include "Rest/OperationMode.h" #include "Rest/Version.h" #include "RestHandler/RestAdminLogHandler.h" +#include "RestHandler/RestAgencyHandler.h" #include "RestHandler/RestBatchHandler.h" #include "RestHandler/RestCursorHandler.h" #include "RestHandler/RestDebugHandler.h" @@ -130,6 +132,10 @@ void ArangoServer::defineHandlers(HttpHandlerFactory* factory) { "/_msg/please-upgrade", RestHandlerCreator::createNoData); + // add "/agency" handler + factory->addPrefixHandler(RestVocbaseBaseHandler::AGENCY_PATH, + RestHandlerCreator::createNoData); + // add "/batch" handler factory->addPrefixHandler(RestVocbaseBaseHandler::BATCH_PATH, RestHandlerCreator::createNoData); From 18fb4b8c26e749290d0cbc2a1cd68979e3b1490c Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 27 Jan 2016 14:37:21 +0100 Subject: [PATCH 03/61] Adding agency rest handler --- arangod/Aql/tokens.cpp | 73 ++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 49 deletions(-) diff --git a/arangod/Aql/tokens.cpp b/arangod/Aql/tokens.cpp index 9c2e299dd0..7b74a94a72 100644 --- a/arangod/Aql/tokens.cpp +++ b/arangod/Aql/tokens.cpp @@ -12,7 +12,7 @@ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 -#define YY_FLEX_SUBMINOR_VERSION 39 +#define YY_FLEX_SUBMINOR_VERSION 35 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif @@ -50,6 +50,7 @@ typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; +typedef uint64_t flex_uint64_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; @@ -57,6 +58,7 @@ typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN @@ -87,8 +89,6 @@ typedef unsigned int flex_uint32_t; #define UINT32_MAX (4294967295U) #endif -#endif /* ! C99 */ - #endif /* ! FLEXINT_H */ #ifdef __cplusplus @@ -162,15 +162,7 @@ typedef void* yyscan_t; /* Size of default input buffer. */ #ifndef YY_BUF_SIZE -#ifdef __ia64__ -/* On IA-64, the buffer size is 16k, not 8k. - * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. - * Ditto for the __ia64__ case accordingly. - */ -#define YY_BUF_SIZE 32768 -#else #define YY_BUF_SIZE 16384 -#endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. @@ -200,18 +192,11 @@ typedef size_t yy_size_t; */ #define YY_LESS_LINENO(n) \ do { \ - int yyl;\ + yy_size_t yyl;\ for ( yyl = n; yyl < yyleng; ++yyl )\ if ( yytext[yyl] == '\n' )\ --yylineno;\ }while(0) - #define YY_LINENO_REWIND_TO(dst) \ - do {\ - const char *p;\ - for ( p = yy_cp-1; p >= (dst); --p)\ - if ( *p == '\n' )\ - --yylineno;\ - }while(0) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ @@ -357,7 +342,7 @@ void Aqlfree (void * ,yyscan_t yyscanner ); /* Begin user sect3 */ -#define Aqlwrap(yyscanner) 1 +#define Aqlwrap(n) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; @@ -376,7 +361,7 @@ static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ - yyleng = (size_t) (yy_cp - yy_bp); \ + yyleng = (yy_size_t) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; @@ -809,10 +794,6 @@ int Aqlget_lineno (yyscan_t yyscanner ); void Aqlset_lineno (int line_number ,yyscan_t yyscanner ); -int Aqlget_column (yyscan_t yyscanner ); - -void Aqlset_column (int column_no ,yyscan_t yyscanner ); - YYSTYPE * Aqlget_lval (yyscan_t yyscanner ); void Aqlset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); @@ -853,12 +834,7 @@ static int input (yyscan_t yyscanner ); /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE -#ifdef __ia64__ -/* On IA-64, the buffer size is 16k, not 8k */ -#define YY_READ_BUF_SIZE 16384 -#else #define YY_READ_BUF_SIZE 8192 -#endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ @@ -866,7 +842,7 @@ static int input (yyscan_t yyscanner ); /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ -#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0) +#define ECHO fwrite( yytext, yyleng, 1, yyout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, @@ -877,7 +853,7 @@ static int input (yyscan_t yyscanner ); if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ - size_t n; \ + yy_size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ @@ -962,6 +938,10 @@ YY_DECL int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* --------------------------------------------------------------------------- + * language keywords + * --------------------------------------------------------------------------- */ + yylval = yylval_param; yylloc = yylloc_param; @@ -992,12 +972,6 @@ YY_DECL Aql_load_buffer_state(yyscanner ); } - { - - /* --------------------------------------------------------------------------- - * language keywords - * --------------------------------------------------------------------------- */ - while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; @@ -1014,7 +988,7 @@ YY_DECL yy_match: do { - YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; @@ -1821,7 +1795,6 @@ case YY_STATE_EOF(COMMENT_MULTI): "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ - } /* end of user's declarations */ } /* end of Aqllex */ /* yy_get_next_buffer - try to read in a new buffer @@ -1885,7 +1858,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); @@ -2020,7 +1993,6 @@ static int yy_get_next_buffer (yyscan_t yyscanner) yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 225); - (void)yyg; return yy_is_jam ? 0 : yy_current_state; } @@ -2073,7 +2045,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) case EOB_ACT_END_OF_FILE: { if ( Aqlwrap(yyscanner ) ) - return EOF; + return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; @@ -2220,6 +2192,10 @@ static void Aql_load_buffer_state (yyscan_t yyscanner) Aqlfree((void *) b ,yyscanner ); } +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a Aqlrestart() or at EOF. @@ -2429,8 +2405,8 @@ YY_BUFFER_STATE Aql_scan_string (yyconst char * yystr , yyscan_t yyscanner) /** Setup the input buffer state to scan the given bytes. The next call to Aqllex() will * scan from a @e copy of @a bytes. - * @param yybytes the byte buffer to scan - * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ @@ -2438,8 +2414,7 @@ YY_BUFFER_STATE Aql_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len { YY_BUFFER_STATE b; char *buf; - yy_size_t n; - yy_size_t i; + yy_size_t n, i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; @@ -2585,7 +2560,7 @@ void Aqlset_lineno (int line_number , yyscan_t yyscanner) /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) - YY_FATAL_ERROR( "Aqlset_lineno called with no buffer" ); + yy_fatal_error( "Aqlset_lineno called with no buffer" , yyscanner); yylineno = line_number; } @@ -2600,7 +2575,7 @@ void Aqlset_column (int column_no , yyscan_t yyscanner) /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) - YY_FATAL_ERROR( "Aqlset_column called with no buffer" ); + yy_fatal_error( "Aqlset_column called with no buffer" , yyscanner); yycolumn = column_no; } From b89cf5a8e680d8f3ebf7b4d9d074dd9e1f2cefa9 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 27 Jan 2016 15:04:57 +0100 Subject: [PATCH 04/61] Agency in arangod --- arangod/Agency/Agent.cpp | 23 +++++++++++++++++++++++ arangod/Agency/Agent.h | 23 +++++++++++++++++++++++ arangod/Agency/ApplicationAgency.cpp | 8 ++++++-- arangod/Agency/ApplicationAgency.h | 4 +--- arangod/Agency/Constituent.cpp | 23 +++++++++++++++++++++++ arangod/Agency/Constituent.h | 23 +++++++++++++++++++++++ arangod/Agency/Log.cpp | 23 +++++++++++++++++++++++ arangod/Agency/Log.h | 23 +++++++++++++++++++++++ arangod/RestServer/ArangoServer.cpp | 8 ++++++++ arangod/RestServer/ArangoServer.h | 7 +++++++ 10 files changed, 160 insertions(+), 5 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 55678dfd26..174461dffb 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -1,3 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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" using namespace arangodb::consensus; diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 4d02fd1d6f..4103c0191e 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -1,3 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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__ diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index f70f94be90..5ad5cfbdda 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -18,7 +18,7 @@ /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// -/// @author Dr. Kaveh Vahedipour +/// @author Kaveh Vahedipour //////////////////////////////////////////////////////////////////////////////// #ifdef _WIN32 @@ -48,7 +48,11 @@ ApplicationAgency::~ApplicationAgency() { /*delete _dispatcher;*/ } void ApplicationAgency::setupOptions( std::map& options) { - options["Agency Options:help-agency"]("agency.size", &_size, "Agency size"); + options["Agency Options:help-agency"]("agency.size", &_size, "Agency size") + ("agency.election-timeout-min", &_size, "Minimum timeout before an agent" + " calls for new election (default: 150ms)") + ("agency.election-timeout-max", &_size, "Minimum timeout before an agent" + " calls for new election (default: 300ms)"); } diff --git a/arangod/Agency/ApplicationAgency.h b/arangod/Agency/ApplicationAgency.h index 333e86a862..719d5fdaf2 100644 --- a/arangod/Agency/ApplicationAgency.h +++ b/arangod/Agency/ApplicationAgency.h @@ -18,7 +18,7 @@ /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// -/// @author Dr. Kaveh Vahedipour +/// @author Kaveh Vahedipour //////////////////////////////////////////////////////////////////////////////// #ifndef ARANGOD_AGENCY_APPLICATION_AGENCY_H @@ -32,8 +32,6 @@ namespace arangodb { namespace rest { -class ApplicationScheduler; -//class Dispatcher; class Task; diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index 4a9a27c7c6..42c8b15798 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -1,3 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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 "Constituent.h" using namespace arangodb::consensus; diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h index 4b6e3f6019..924fcdcfa5 100644 --- a/arangod/Agency/Constituent.h +++ b/arangod/Agency/Constituent.h @@ -1,3 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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__ diff --git a/arangod/Agency/Log.cpp b/arangod/Agency/Log.cpp index 3f3688e325..c9a922fab3 100644 --- a/arangod/Agency/Log.cpp +++ b/arangod/Agency/Log.cpp @@ -1,3 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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 "Log.h" using namespace arangodb::consensus; diff --git a/arangod/Agency/Log.h b/arangod/Agency/Log.h index 5b432ea758..81f021671e 100644 --- a/arangod/Agency/Log.h +++ b/arangod/Agency/Log.h @@ -1,3 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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 //using namespace arangodb::velocypack; diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 1745fc6504..28480f4648 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -599,6 +599,14 @@ void ArangoServer::buildApplicationServer() { new ApplicationCluster(_server, _applicationDispatcher, _applicationV8); _applicationServer->addFeature(_applicationCluster); + // ............................................................................. + // cluster options + // ............................................................................. + + _applicationAgency = + new ApplicationAgency(); + _applicationServer->addFeature(_applicationAgency); + // ............................................................................. // server options // ............................................................................. diff --git a/arangod/RestServer/ArangoServer.h b/arangod/RestServer/ArangoServer.h index fcb5383573..83e15de454 100644 --- a/arangod/RestServer/ArangoServer.h +++ b/arangod/RestServer/ArangoServer.h @@ -44,6 +44,7 @@ class ThreadPool; } namespace rest { +class ApplicationAgency; class ApplicationDispatcher; class ApplicationEndpointServer; class ApplicationScheduler; @@ -176,6 +177,12 @@ class ArangoServer : public rest::AnyServer { arangodb::ApplicationCluster* _applicationCluster; + ////////////////////////////////////////////////////////////////////////////// + /// @brief cluster application feature + ////////////////////////////////////////////////////////////////////////////// + + rest::ApplicationAgency* _applicationAgency; + ////////////////////////////////////////////////////////////////////////////// /// @brief asynchronous job manager ////////////////////////////////////////////////////////////////////////////// From a8af99aca5c3b8e00f8efed3b5ea8ed6cc24a6b7 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 27 Jan 2016 18:35:41 +0100 Subject: [PATCH 05/61] Agency is attached to application. Vote reception runnin.g --- arangod/Agency/Agent.cpp | 4 ++++ arangod/Agency/Agent.h | 4 +++- arangod/Agency/ApplicationAgency.cpp | 19 +++++++++++++------ arangod/Agency/ApplicationAgency.h | 8 +++++++- arangod/Agency/Constituent.cpp | 13 +++++++++++++ arangod/Agency/Constituent.h | 3 +++ arangod/RestServer/ArangoServer.cpp | 5 +++-- arangod/RestServer/ArangoServer.h | 8 ++++++++ 8 files changed, 54 insertions(+), 10 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 174461dffb..f85fcaef8b 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -37,6 +37,10 @@ Constituent::term_t Agent::term () const { return _constituent.term(); } +bool Agent::vote(Constituent::id_t id, Constituent::term_t term) { + bool res = _constituent.vote(id, term); +} + Slice const& Agent::log (const Slice& slice) { if (_constituent.leader()) return _log.log(slice); diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 4103c0191e..a0599ee522 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -83,9 +83,11 @@ namespace consensus { */ Slice const& log (Slice const&); Slice const& redirect (Slice const&); + + bool vote(Constituent::id_t, Constituent::term_t); private: - Constituent _constituent; /**< @brief Leader election delegate */ + Constituent _constituent; /**< @brief Leader election delegate */ Log _log; /**< @brief Log replica */ }; diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index 5ad5cfbdda..4e37d3bb43 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -36,10 +36,12 @@ using namespace arangodb::basics; using namespace arangodb::rest; ApplicationAgency::ApplicationAgency() - : ApplicationFeature("agency"), _size(5) {} + : ApplicationFeature("agency"), _size(5), _min_election_timeout(.15), + _max_election_timeout(.3), _agent(new consensus::Agent()) { +} -ApplicationAgency::~ApplicationAgency() { /*delete _dispatcher;*/ } +ApplicationAgency::~ApplicationAgency() {} //////////////////////////////////////////////////////////////////////////////// @@ -49,10 +51,11 @@ ApplicationAgency::~ApplicationAgency() { /*delete _dispatcher;*/ } void ApplicationAgency::setupOptions( std::map& options) { options["Agency Options:help-agency"]("agency.size", &_size, "Agency size") - ("agency.election-timeout-min", &_size, "Minimum timeout before an agent" - " calls for new election (default: 150ms)") - ("agency.election-timeout-max", &_size, "Minimum timeout before an agent" - " calls for new election (default: 300ms)"); + ("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.host", &_agency_endpoints, "Agency endpoints"); } @@ -90,5 +93,9 @@ void ApplicationAgency::stop() { } } +arangodb::consensus::Agent* ApplicationAgency::agent () const { + return _agent.get(); +} + diff --git a/arangod/Agency/ApplicationAgency.h b/arangod/Agency/ApplicationAgency.h index 719d5fdaf2..c62406c8a0 100644 --- a/arangod/Agency/ApplicationAgency.h +++ b/arangod/Agency/ApplicationAgency.h @@ -27,7 +27,7 @@ #include "Basics/Common.h" #include "ApplicationServer/ApplicationFeature.h" -#include "ApplicationAgency.h" +#include "Agency/Agent.h" namespace arangodb { @@ -105,10 +105,16 @@ class Task; void stop(); + consensus::Agent* 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 */ + std::vector _agency_endpoints; + std::unique_ptr _agent; }; } diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index 42c8b15798..a0f4e4aa71 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -59,6 +59,19 @@ Constituent::mode_t Constituent::mode () const { return _mode; } +bool Constituent::vote (id_t id, term_t term) { + if (id == _id) + return false; + else { + if (term > _term) { + _state = FOLLOWER; + return true; + } else { + return false; + } + } +} + void Constituent::gossip (const Constituent::constituency_t& constituency) { // Talk to all peers i have not talked to // Answer by sending my complete list of peers diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h index 924fcdcfa5..4ddbdbe533 100644 --- a/arangod/Agency/Constituent.h +++ b/arangod/Agency/Constituent.h @@ -95,6 +95,8 @@ public: bool leader() const; + bool vote(id_t, term_t); + private: /** @@ -115,6 +117,7 @@ private: term_t _term; /**< @brief term number */ id_t _leader_id; /**< @brief Current leader */ id_t _cur_vote; /**< @brief My current vote */ + id_t _id; constituency_t _constituency; /**< @brief List of consituents */ uint32_t _nvotes; /**< @brief Votes in my favour * (candidate/leader) */ diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 28480f4648..bc51db9cb9 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -133,7 +133,8 @@ void ArangoServer::defineHandlers(HttpHandlerFactory* factory) { // add "/agency" handler factory->addPrefixHandler(RestVocbaseBaseHandler::AGENCY_PATH, - RestHandlerCreator::createNoData); + RestHandlerCreator::createData< + consensus::Agent*>, _applicationAgency->agent()); // add "/batch" handler factory->addPrefixHandler(RestVocbaseBaseHandler::BATCH_PATH, @@ -600,7 +601,7 @@ void ArangoServer::buildApplicationServer() { _applicationServer->addFeature(_applicationCluster); // ............................................................................. - // cluster options + // agency options // ............................................................................. _applicationAgency = diff --git a/arangod/RestServer/ArangoServer.h b/arangod/RestServer/ArangoServer.h index 83e15de454..6bb77c02d7 100644 --- a/arangod/RestServer/ArangoServer.h +++ b/arangod/RestServer/ArangoServer.h @@ -34,6 +34,7 @@ #include "Rest/AnyServer.h" #include "Rest/OperationMode.h" #include "VocBase/vocbase.h" +#include "Agency/Agent.h" struct TRI_server_t; struct TRI_vocbase_defaults_t; @@ -373,6 +374,13 @@ class ArangoServer : public rest::AnyServer { aql::QueryRegistry* _queryRegistry; + ////////////////////////////////////////////////////////////////////////////// + /// @brief the agent + ////////////////////////////////////////////////////////////////////////////// + + consensus::Agent* _agent; + + ////////////////////////////////////////////////////////////////////////////// /// @brief ptr to pair of _applicationV8 and _queryRegistry for _api/aql /// handler From ab1fb97552214a14bfc6ee64b2d5177f1374d685 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 1 Feb 2016 13:01:51 +0100 Subject: [PATCH 06/61] Agency in arangod --- arangod/Agency/Agent.cpp | 14 ++++---- arangod/Agency/Agent.h | 30 +++------------- arangod/Agency/ApplicationAgency.cpp | 4 ++- arangod/Agency/Constituent.cpp | 53 +++++++++++++++++++++------- arangod/Agency/Constituent.h | 25 ++++++------- arangod/Agency/Log.h | 6 ++-- 6 files changed, 71 insertions(+), 61 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index f85fcaef8b..e73d6a7f77 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -25,20 +25,20 @@ using namespace arangodb::consensus; -Agent::Agent () {} +Agent::Agent () { + _constituent.start(); +} -Agent::Agent (AgentConfig const&) {} - -Agent::Agent (Agent const&) {} - -Agent::~Agent () {} +Agent::~Agent () { + _constituent.stop(); +} Constituent::term_t Agent::term () const { return _constituent.term(); } bool Agent::vote(Constituent::id_t id, Constituent::term_t term) { - bool res = _constituent.vote(id, term); + return _constituent.vote(id, term); } Slice const& Agent::log (const Slice& slice) { diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index a0599ee522..a350c6382a 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -24,6 +24,7 @@ #ifndef __ARANGODB_CONSENSUS_AGENT__ #define __ARANGODB_CONSENSUS_AGENT__ +#include "AgencyCommon.h" #include "Constituent.h" #include "Log.h" @@ -33,25 +34,7 @@ namespace consensus { class Agent { public: - /** - * @brief Host descriptor - */ - - template struct host_t { - std::string host; - std::string port; - T vote; - }; - - /** - * @brief Agent configuration - */ - template struct AgentConfig { - T min_ping; - T max_ping; - std::vector > start_hosts; - }; - + /** * @brief Default ctor */ @@ -61,13 +44,8 @@ namespace consensus { * @brief Construct with program options \n * One process starts with options, the \n remaining start with list of peers and gossip. */ - Agent (AgentConfig const&); - - /** - * @brief Copy ctor - */ - Agent (Agent const&); - + Agent (Config const&); + /** * @brief Clean up */ diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index 4e37d3bb43..ce82e18523 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -37,7 +37,9 @@ using namespace arangodb::rest; ApplicationAgency::ApplicationAgency() : ApplicationFeature("agency"), _size(5), _min_election_timeout(.15), - _max_election_timeout(.3), _agent(new consensus::Agent()) { + _max_election_timeout(.3) , _agent(new consensus::Agent()){ + //arangodb::consensus::Config config (5, .15, .9); + //_agent = std::uniqe_ptr(new config); } diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index a0f4e4aa71..556f646b21 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -21,26 +21,26 @@ /// @author Kaveh Vahedipour //////////////////////////////////////////////////////////////////////////////// +#include "Basics/logging.h" +#include "Cluster/ClusterComm.h" + #include "Constituent.h" +#include +#include + using namespace arangodb::consensus; -Constituent::Constituent () : - _term(0), _gen(std::random_device()()), _mode(FOLLOWER) { -} +Constituent::Constituent (const uint32_t n) : Thread("Constituent"), + _term(0), _gen(std::random_device()()), _mode(FOLLOWER), _run(true), + _votes(std::vector(n)) { -Constituent::Constituent (const uint32_t n) : - _mode(FOLLOWER), _votes(std::vector(n)) { -} - -Constituent::Constituent (const constituency_t& constituency) : - _gen(std::random_device()()), _mode(FOLLOWER) { } Constituent::~Constituent() {} Constituent::duration_t Constituent::sleepFor () { - dist_t dis(0., 1.); + dist_t dis(.15, .99); return Constituent::duration_t(dis(_gen)); } @@ -60,11 +60,13 @@ Constituent::mode_t Constituent::mode () const { } bool Constituent::vote (id_t id, term_t term) { + LOG_WARNING("(%d %d) (%d %d)", _id, _term, id, term); if (id == _id) return false; else { if (term > _term) { _state = FOLLOWER; + _term = term; return true; } else { return false; @@ -85,8 +87,33 @@ bool Constituent::leader() const { return _mode==LEADER; } -void Constituent::callElection() {} - - +void Constituent::callElection() { + + std::unique_ptr> headerFields; + + std::vector results(_constituency.size()); + for (auto& i : _constituency) + results[i.id] = arangodb::ClusterComm::instance()->asyncRequest( + 0, 0, i.address, rest::HttpRequest::HTTP_REQUEST_GET, + "/_api/agency/vote?id=0&term=3", nullptr, headerFields, nullptr, + .75*.15); + std::this_thread::sleep_for(sleepFor(.9*.15)); + for (auto i : _votes) + arangodb::ClusterComm::instance()->enquire(results[i.id].OperationID); + // time out + //count votes +} + +void Constituent::run() { + while (_run) { + if (_state == FOLLOWER) { + LOG_WARNING ("sleeping ... "); + std::this_thread::sleep_for(sleepFor()); + } else { + callElection(); + } + + } +}; diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h index 4ddbdbe533..c7f630922b 100644 --- a/arangod/Agency/Constituent.h +++ b/arangod/Agency/Constituent.h @@ -30,16 +30,20 @@ #include #include +#include "AgencyCommon.h" + +#include "Basics/Thread.h" + namespace arangodb { namespace consensus { /** * @brief Raft leader election */ -class Constituent { +class Constituent : public arangodb::basics::Thread { public: - + enum mode_t { LEADER, CANDIDATE, FOLLOWER }; @@ -55,20 +59,11 @@ public: typedef uint32_t state_t; typedef std::uniform_real_distribution dist_t; - /** - * brief Construct with size of constituency - */ - Constituent (); /** * brief Construct with size of constituency */ - Constituent (const uint32_t n); - - /** - * brief Construct with subset of peers - */ - Constituent (constituency_t const & constituency); + Constituent (const uint32_t n = 3); /** * @brief Clean up and exit election @@ -97,6 +92,10 @@ public: bool vote(id_t, term_t); + void run(); + + void voteCallBack(); + private: /** @@ -127,6 +126,8 @@ private: mode_t _mode; + bool _run; + std::vector _votes; }; diff --git a/arangod/Agency/Log.h b/arangod/Agency/Log.h index 81f021671e..ca16dcc0c3 100644 --- a/arangod/Agency/Log.h +++ b/arangod/Agency/Log.h @@ -23,6 +23,8 @@ #include +#include "AgencyCommon.h" + //using namespace arangodb::velocypack; class Slice {}; @@ -33,7 +35,7 @@ namespace consensus { /** * @brief Log repilca */ -class Log { +class Log { public: typedef uint64_t index_t; @@ -51,7 +53,7 @@ public: /** * @brief Log */ - + Slice const& log (Slice const&); private: From b54aa2f1a60ccfb3b379b2c32c53e39afa75c584 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 3 Feb 2016 10:14:34 +0100 Subject: [PATCH 07/61] minor changes of LOG and namespaces --- arangod/Agency/Constituent.cpp | 14 +- arangod/Aql/grammar.cpp | 766 +++++++++++++++++---------------- arangod/Aql/grammar.h | 12 +- 3 files changed, 399 insertions(+), 393 deletions(-) diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index 556f646b21..cd304a0ab3 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -21,8 +21,8 @@ /// @author Kaveh Vahedipour //////////////////////////////////////////////////////////////////////////////// -#include "Basics/logging.h" #include "Cluster/ClusterComm.h" +#include "Basics/Logger.h" #include "Constituent.h" @@ -31,6 +31,7 @@ using namespace arangodb::consensus; + Constituent::Constituent (const uint32_t n) : Thread("Constituent"), _term(0), _gen(std::random_device()()), _mode(FOLLOWER), _run(true), _votes(std::vector(n)) { @@ -60,7 +61,7 @@ Constituent::mode_t Constituent::mode () const { } bool Constituent::vote (id_t id, term_t term) { - LOG_WARNING("(%d %d) (%d %d)", _id, _term, id, term); + LOG(WARN) << _id << "," << _term << " " << id << "," << term; if (id == _id) return false; else { @@ -97,9 +98,10 @@ void Constituent::callElection() { 0, 0, i.address, rest::HttpRequest::HTTP_REQUEST_GET, "/_api/agency/vote?id=0&term=3", nullptr, headerFields, nullptr, .75*.15); - std::this_thread::sleep_for(sleepFor(.9*.15)); - for (auto i : _votes) - arangodb::ClusterComm::instance()->enquire(results[i.id].OperationID); + std::this_thread::sleep_for(Constituent::duration_t(.9*.15)); + for (auto& i : _constituency) + arangodb::ClusterComm::instance()->enquire(results[i.id].operationID); + // time out //count votes } @@ -107,7 +109,7 @@ void Constituent::callElection() { void Constituent::run() { while (_run) { if (_state == FOLLOWER) { - LOG_WARNING ("sleeping ... "); + LOG(WARN) << "sleeping ... "; std::this_thread::sleep_for(sleepFor()); } else { callElection(); diff --git a/arangod/Aql/grammar.cpp b/arangod/Aql/grammar.cpp index ba5d350653..1b504e1e73 100644 --- a/arangod/Aql/grammar.cpp +++ b/arangod/Aql/grammar.cpp @@ -1,8 +1,8 @@ -/* A Bison parser, made by GNU Bison 3.0.2. */ +/* A Bison parser, made by GNU Bison 3.0.4. */ /* Bison implementation for Yacc-like parsers in C - Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. 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 @@ -44,7 +44,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "3.0.2" +#define YYBISON_VERSION "3.0.4" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -184,7 +184,7 @@ extern int Aqldebug; /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED -typedef union YYSTYPE YYSTYPE; + union YYSTYPE { #line 18 "arangod/Aql/grammar.y" /* yacc.c:355 */ @@ -199,6 +199,8 @@ union YYSTYPE #line 201 "arangod/Aql/grammar.cpp" /* yacc.c:355 */ }; + +typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif @@ -368,7 +370,7 @@ static AstNode const* GetIntoExpression(AstNode const* node) { } -#line 372 "arangod/Aql/grammar.cpp" /* yacc.c:358 */ +#line 374 "arangod/Aql/grammar.cpp" /* yacc.c:358 */ #ifdef short # undef short @@ -1948,233 +1950,233 @@ yyreduce: switch (yyn) { case 2: -#line 330 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 330 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1955 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1957 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 3: -#line 335 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 335 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 1962 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1964 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 4: -#line 337 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 337 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->ast()->scopes()->endNested(); } -#line 1970 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1972 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 5: -#line 340 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 340 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->ast()->scopes()->endNested(); } -#line 1978 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1980 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 6: -#line 343 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 343 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->ast()->scopes()->endNested(); } -#line 1986 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1988 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 7: -#line 346 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 346 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->ast()->scopes()->endNested(); } -#line 1994 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 1996 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 8: -#line 349 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 349 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->ast()->scopes()->endNested(); } -#line 2002 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2004 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 9: -#line 355 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 355 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2009 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2011 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 10: -#line 357 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 357 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2016 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2018 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 11: -#line 362 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 362 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2023 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2025 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 12: -#line 364 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 364 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2030 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2032 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 13: -#line 366 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 366 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2037 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2039 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 14: -#line 368 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 368 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2044 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2046 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 15: -#line 370 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 370 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2051 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2053 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 16: -#line 372 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 372 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2058 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2060 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 17: -#line 374 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 374 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2065 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2067 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 18: -#line 376 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 376 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2072 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2074 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 19: -#line 378 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 378 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2079 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2081 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 20: -#line 380 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 380 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2086 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2088 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 21: -#line 382 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 382 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2093 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2095 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 22: -#line 387 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 387 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->ast()->scopes()->start(arangodb::aql::AQL_SCOPE_FOR); auto node = parser->ast()->createNodeFor((yyvsp[-2].strval).value, (yyvsp[-2].strval).length, (yyvsp[0].node), true); parser->ast()->addOperation(node); } -#line 2104 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2106 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 23: -#line 393 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 393 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->ast()->scopes()->start(arangodb::aql::AQL_SCOPE_FOR); auto node = parser->ast()->createNodeTraversal((yyvsp[-4].strval).value, (yyvsp[-4].strval).length, (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2114 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2116 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 24: -#line 398 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 398 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->ast()->scopes()->start(arangodb::aql::AQL_SCOPE_FOR); auto node = parser->ast()->createNodeTraversal((yyvsp[-6].strval).value, (yyvsp[-6].strval).length, (yyvsp[-4].strval).value, (yyvsp[-4].strval).length, (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2124 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2126 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 25: -#line 403 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 403 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->ast()->scopes()->start(arangodb::aql::AQL_SCOPE_FOR); auto node = parser->ast()->createNodeTraversal((yyvsp[-8].strval).value, (yyvsp[-8].strval).length, (yyvsp[-6].strval).value, (yyvsp[-6].strval).length, (yyvsp[-4].strval).value, (yyvsp[-4].strval).length, (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2134 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2136 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 26: -#line 411 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 411 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // operand is a reference. can use it directly auto node = parser->ast()->createNodeFilter((yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2144 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2146 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 27: -#line 419 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 419 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2151 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2153 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 28: -#line 424 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 424 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2158 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2160 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 29: -#line 426 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 426 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2165 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2167 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 30: -#line 431 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 431 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeLet((yyvsp[-2].strval).value, (yyvsp[-2].strval).length, (yyvsp[0].node), true); parser->ast()->addOperation(node); } -#line 2174 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2176 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 31: -#line 438 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 438 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! TRI_CaseEqualString((yyvsp[-2].strval).value, "COUNT")) { parser->registerParseError(TRI_ERROR_QUERY_PARSE, "unexpected qualifier '%s', expecting 'COUNT'", (yyvsp[-2].strval).value, yylloc.first_line, yylloc.first_column); @@ -2182,20 +2184,20 @@ yyreduce: (yyval.strval) = (yyvsp[0].strval); } -#line 2186 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2188 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 32: -#line 448 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 448 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeArray(); parser->pushStack(node); } -#line 2195 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2197 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 33: -#line 451 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 451 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto list = static_cast(parser->popStack()); @@ -2204,11 +2206,11 @@ yyreduce: } (yyval.node) = list; } -#line 2208 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2210 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 34: -#line 462 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 462 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { /* COLLECT WITH COUNT INTO var OPTIONS ... */ auto scopes = parser->ast()->scopes(); @@ -2218,11 +2220,11 @@ yyreduce: auto node = parser->ast()->createNodeCollectCount(parser->ast()->createNodeArray(), (yyvsp[-1].strval).value, (yyvsp[-1].strval).length, (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2222 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2224 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 35: -#line 471 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 471 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { /* COLLECT var = expr WITH COUNT INTO var OPTIONS ... */ auto scopes = parser->ast()->scopes(); @@ -2234,11 +2236,11 @@ yyreduce: auto node = parser->ast()->createNodeCollectCount((yyvsp[-2].node), (yyvsp[-1].strval).value, (yyvsp[-1].strval).length, (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2238 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2240 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 36: -#line 482 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 482 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { /* AGGREGATE var = expr OPTIONS ... */ auto scopes = parser->ast()->scopes(); @@ -2258,11 +2260,11 @@ yyreduce: auto node = parser->ast()->createNodeCollect(parser->ast()->createNodeArray(), (yyvsp[-2].node), into, intoExpression, nullptr, (yyvsp[-1].node)); parser->ast()->addOperation(node); } -#line 2262 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2264 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 37: -#line 501 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 501 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { /* COLLECT var = expr AGGREGATE var = expr OPTIONS ... */ auto scopes = parser->ast()->scopes(); @@ -2314,11 +2316,11 @@ yyreduce: auto node = parser->ast()->createNodeCollect((yyvsp[-3].node), (yyvsp[-2].node), into, intoExpression, nullptr, (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2318 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2320 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 38: -#line 552 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 552 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { /* COLLECT var = expr INTO var OPTIONS ... */ auto scopes = parser->ast()->scopes(); @@ -2333,11 +2335,11 @@ yyreduce: auto node = parser->ast()->createNodeCollect((yyvsp[-2].node), parser->ast()->createNodeArray(), into, intoExpression, nullptr, (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2337 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2339 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 39: -#line 566 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 566 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { /* COLLECT var = expr INTO var KEEP ... OPTIONS ... */ auto scopes = parser->ast()->scopes(); @@ -2357,61 +2359,61 @@ yyreduce: auto node = parser->ast()->createNodeCollect((yyvsp[-3].node), parser->ast()->createNodeArray(), into, intoExpression, (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2361 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2363 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 40: -#line 588 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 588 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2368 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2370 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 41: -#line 590 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 590 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2375 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2377 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 42: -#line 595 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 595 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeAssign((yyvsp[-2].strval).value, (yyvsp[-2].strval).length, (yyvsp[0].node)); parser->pushArrayElement(node); } -#line 2384 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2386 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 43: -#line 602 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 602 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = nullptr; } -#line 2392 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2394 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 44: -#line 605 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 605 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueString((yyvsp[0].strval).value, (yyvsp[0].strval).length); } -#line 2400 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2402 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 45: -#line 608 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 608 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeArray(); node->addMember(parser->ast()->createNodeValueString((yyvsp[-2].strval).value, (yyvsp[-2].strval).length)); node->addMember((yyvsp[0].node)); (yyval.node) = node; } -#line 2411 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2413 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 46: -#line 617 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 617 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->ast()->scopes()->existsVariable((yyvsp[0].strval).value, (yyvsp[0].strval).length)) { parser->registerParseError(TRI_ERROR_QUERY_PARSE, "use of unknown variable '%s' for KEEP", (yyvsp[0].strval).value, yylloc.first_line, yylloc.first_column); @@ -2426,11 +2428,11 @@ yyreduce: node->setFlag(FLAG_KEEP_VARIABLENAME); parser->pushArrayElement(node); } -#line 2430 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2432 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 47: -#line 631 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 631 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->ast()->scopes()->existsVariable((yyvsp[0].strval).value, (yyvsp[0].strval).length)) { parser->registerParseError(TRI_ERROR_QUERY_PARSE, "use of unknown variable '%s' for KEEP", (yyvsp[0].strval).value, yylloc.first_line, yylloc.first_column); @@ -2445,11 +2447,11 @@ yyreduce: node->setFlag(FLAG_KEEP_VARIABLENAME); parser->pushArrayElement(node); } -#line 2449 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2451 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 48: -#line 648 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 648 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! TRI_CaseEqualString((yyvsp[0].strval).value, "KEEP")) { parser->registerParseError(TRI_ERROR_QUERY_PARSE, "unexpected qualifier '%s', expecting 'KEEP'", (yyvsp[0].strval).value, yylloc.first_line, yylloc.first_column); @@ -2458,158 +2460,158 @@ yyreduce: auto node = parser->ast()->createNodeArray(); parser->pushStack(node); } -#line 2462 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2464 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 49: -#line 655 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 655 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto list = static_cast(parser->popStack()); (yyval.node) = list; } -#line 2471 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2473 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 50: -#line 662 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 662 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeArray(); parser->pushStack(node); } -#line 2480 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2482 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 51: -#line 665 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 665 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto list = static_cast(parser->popStack()); (yyval.node) = list; } -#line 2489 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2491 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 52: -#line 672 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 672 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeArray(); parser->pushStack(node); } -#line 2498 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2500 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 53: -#line 675 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 675 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto list = static_cast(parser->popStack()); auto node = parser->ast()->createNodeSort(list); parser->ast()->addOperation(node); } -#line 2508 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2510 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 54: -#line 683 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 683 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushArrayElement((yyvsp[0].node)); } -#line 2516 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2518 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 55: -#line 686 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 686 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushArrayElement((yyvsp[0].node)); } -#line 2524 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2526 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 56: -#line 692 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 692 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeSortElement((yyvsp[-1].node), (yyvsp[0].node)); } -#line 2532 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2534 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 57: -#line 698 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 698 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueBool(true); } -#line 2540 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2542 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 58: -#line 701 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 701 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueBool(true); } -#line 2548 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2550 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 59: -#line 704 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 704 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueBool(false); } -#line 2556 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2558 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 60: -#line 707 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 707 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2564 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2566 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 61: -#line 713 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 713 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto offset = parser->ast()->createNodeValueInt(0); auto node = parser->ast()->createNodeLimit(offset, (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2574 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2576 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 62: -#line 718 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 718 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeLimit((yyvsp[-2].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2583 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2585 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 63: -#line 725 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 725 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeReturn((yyvsp[0].node)); parser->ast()->addOperation(node); parser->ast()->scopes()->endNested(); } -#line 2593 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2595 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 64: -#line 733 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 733 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2601 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2603 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 65: -#line 736 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 736 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2609 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2611 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 66: -#line 742 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 742 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2617,11 +2619,11 @@ yyreduce: auto node = parser->ast()->createNodeRemove((yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2621 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2623 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 67: -#line 752 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 752 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2629,11 +2631,11 @@ yyreduce: auto node = parser->ast()->createNodeInsert((yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2633 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2635 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 68: -#line 762 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 762 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2642,11 +2644,11 @@ yyreduce: AstNode* node = parser->ast()->createNodeUpdate(nullptr, (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2646 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2648 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 69: -#line 770 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 770 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2655,18 +2657,18 @@ yyreduce: AstNode* node = parser->ast()->createNodeUpdate((yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2659 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2661 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 70: -#line 781 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 781 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2666 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2668 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 71: -#line 786 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 786 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2675,11 +2677,11 @@ yyreduce: AstNode* node = parser->ast()->createNodeReplace(nullptr, (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2679 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2681 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 72: -#line 794 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 794 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2688,44 +2690,44 @@ yyreduce: AstNode* node = parser->ast()->createNodeReplace((yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2692 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2694 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 73: -#line 805 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 805 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 2699 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2701 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 74: -#line 810 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 810 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.intval) = static_cast(NODE_TYPE_UPDATE); } -#line 2707 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2709 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 75: -#line 813 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 813 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.intval) = static_cast(NODE_TYPE_REPLACE); } -#line 2715 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2717 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 76: -#line 819 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 819 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // reserve a variable named "$OLD", we might need it in the update expression // and in a later return thing parser->pushStack(parser->ast()->createNodeVariable(TRI_CHAR_LENGTH_PAIR(Variable::NAME_OLD), true)); } -#line 2725 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2727 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 77: -#line 823 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 823 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if (! parser->configureWriteQuery((yyvsp[-1].node), (yyvsp[0].node))) { YYABORT; @@ -2770,11 +2772,11 @@ yyreduce: auto node = parser->ast()->createNodeUpsert(static_cast((yyvsp[-3].intval)), parser->ast()->createNodeReference(TRI_CHAR_LENGTH_PAIR(Variable::NAME_OLD)), (yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[-1].node), (yyvsp[0].node)); parser->ast()->addOperation(node); } -#line 2774 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2776 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 78: -#line 870 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 870 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto const scopeType = parser->ast()->scopes()->type(); @@ -2783,83 +2785,83 @@ yyreduce: parser->registerParseError(TRI_ERROR_QUERY_PARSE, "cannot use DISTINCT modifier on top-level query element", yylloc.first_line, yylloc.first_column); } } -#line 2787 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2789 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 79: -#line 877 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 877 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeDistinct((yyvsp[0].node)); } -#line 2795 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2797 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 80: -#line 880 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 880 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2803 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2805 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 81: -#line 886 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 886 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2811 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2813 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 82: -#line 889 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 889 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2819 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2821 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 83: -#line 892 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 892 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2827 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2829 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 84: -#line 895 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 895 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2835 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2837 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 85: -#line 898 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 898 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 2843 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2845 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 86: -#line 901 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 901 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeRange((yyvsp[-2].node), (yyvsp[0].node)); } -#line 2851 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2853 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 87: -#line 907 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 907 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.strval) = (yyvsp[0].strval); } -#line 2859 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2861 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 88: -#line 910 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 910 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { std::string temp((yyvsp[-2].strval).value, (yyvsp[-2].strval).length); temp.append("::"); @@ -2873,214 +2875,214 @@ yyreduce: (yyval.strval).value = p; (yyval.strval).length = temp.size(); } -#line 2877 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2879 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 89: -#line 926 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 926 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushStack((yyvsp[0].strval).value); auto node = parser->ast()->createNodeArray(); parser->pushStack(node); } -#line 2888 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2890 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 90: -#line 931 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 931 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto list = static_cast(parser->popStack()); (yyval.node) = parser->ast()->createNodeFunctionCall(static_cast(parser->popStack()), list); } -#line 2897 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2899 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 91: -#line 938 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 938 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeUnaryOperator(NODE_TYPE_OPERATOR_UNARY_PLUS, (yyvsp[0].node)); } -#line 2905 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2907 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 92: -#line 941 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 941 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeUnaryOperator(NODE_TYPE_OPERATOR_UNARY_MINUS, (yyvsp[0].node)); } -#line 2913 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2915 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 93: -#line 944 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 944 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeUnaryOperator(NODE_TYPE_OPERATOR_UNARY_NOT, (yyvsp[0].node)); } -#line 2921 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2923 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 94: -#line 950 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 950 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_OR, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2929 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2931 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 95: -#line 953 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 953 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_AND, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2937 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2939 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 96: -#line 956 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 956 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_PLUS, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2945 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2947 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 97: -#line 959 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 959 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_MINUS, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2953 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2955 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 98: -#line 962 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 962 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_TIMES, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2961 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2963 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 99: -#line 965 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 965 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_DIV, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2969 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2971 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 100: -#line 968 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 968 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_MOD, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2977 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2979 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 101: -#line 971 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 971 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_EQ, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2985 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2987 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 102: -#line 974 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 974 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_NE, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 2993 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 2995 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 103: -#line 977 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 977 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_LT, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 3001 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3003 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 104: -#line 980 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 980 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_GT, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 3009 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3011 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 105: -#line 983 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 983 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_LE, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 3017 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3019 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 106: -#line 986 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 986 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_GE, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 3025 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3027 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 107: -#line 989 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 989 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_IN, (yyvsp[-2].node), (yyvsp[0].node)); } -#line 3033 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3035 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 108: -#line 992 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 992 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_NIN, (yyvsp[-3].node), (yyvsp[0].node)); } -#line 3041 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3043 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 109: -#line 998 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 998 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeTernaryOperator((yyvsp[-4].node), (yyvsp[-2].node), (yyvsp[0].node)); } -#line 3049 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3051 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 110: -#line 1004 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1004 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 3056 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3058 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 111: -#line 1006 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1006 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 3063 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3065 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 112: -#line 1011 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1011 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3071 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3073 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 113: -#line 1014 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1014 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->ast()->scopes()->start(arangodb::aql::AQL_SCOPE_SUBQUERY); parser->ast()->startSubQuery(); } -#line 3080 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3082 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 114: -#line 1017 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1017 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { AstNode* node = parser->ast()->endSubQuery(); parser->ast()->scopes()->endCurrent(); @@ -3091,98 +3093,98 @@ yyreduce: (yyval.node) = parser->ast()->createNodeReference(variableName); } -#line 3095 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3097 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 115: -#line 1030 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1030 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushArrayElement((yyvsp[0].node)); } -#line 3103 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3105 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 116: -#line 1033 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1033 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushArrayElement((yyvsp[0].node)); } -#line 3111 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3113 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 117: -#line 1039 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1039 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3119 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3121 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 118: -#line 1042 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1042 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3127 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3129 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 119: -#line 1048 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1048 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeArray(); parser->pushStack(node); } -#line 3136 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3138 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 120: -#line 1051 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1051 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = static_cast(parser->popStack()); } -#line 3144 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3146 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 121: -#line 1057 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1057 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 3151 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3153 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 122: -#line 1059 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1059 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 3158 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3160 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 123: -#line 1064 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1064 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushArrayElement((yyvsp[0].node)); } -#line 3166 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3168 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 124: -#line 1067 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1067 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->pushArrayElement((yyvsp[0].node)); } -#line 3174 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3176 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 125: -#line 1073 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1073 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = nullptr; } -#line 3182 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3184 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 126: -#line 1076 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1076 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if ((yyvsp[0].node) == nullptr) { ABORT_OOM @@ -3194,56 +3196,56 @@ yyreduce: (yyval.node) = (yyvsp[0].node); } -#line 3198 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3200 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 127: -#line 1090 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1090 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeObject(); parser->pushStack(node); } -#line 3207 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3209 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 128: -#line 1093 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1093 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = static_cast(parser->popStack()); } -#line 3215 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3217 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 129: -#line 1099 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1099 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 3222 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3224 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 130: -#line 1101 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1101 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 3229 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3231 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 131: -#line 1106 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1106 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 3236 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3238 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 132: -#line 1108 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1108 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { } -#line 3243 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3245 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 133: -#line 1113 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1113 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // attribute-name-only (comparable to JS enhanced object literals, e.g. { foo, bar }) auto ast = parser->ast(); @@ -3258,20 +3260,20 @@ yyreduce: auto node = ast->createNodeReference(variable); parser->pushObjectElement((yyvsp[0].strval).value, (yyvsp[0].strval).length, node); } -#line 3262 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3264 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 134: -#line 1127 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1127 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // attribute-name : attribute-value parser->pushObjectElement((yyvsp[-2].strval).value, (yyvsp[-2].strval).length, (yyvsp[0].node)); } -#line 3271 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3273 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 135: -#line 1131 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1131 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // bind-parameter : attribute-value if ((yyvsp[-2].strval).length < 1 || (yyvsp[-2].strval).value[0] == '@') { @@ -3281,100 +3283,100 @@ yyreduce: auto param = parser->ast()->createNodeParameter((yyvsp[-2].strval).value, (yyvsp[-2].strval).length); parser->pushObjectElement(param, (yyvsp[0].node)); } -#line 3285 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3287 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 136: -#line 1140 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1140 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // [ attribute-name-expression ] : attribute-value parser->pushObjectElement((yyvsp[-3].node), (yyvsp[0].node)); } -#line 3294 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3296 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 137: -#line 1147 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1147 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.intval) = 1; } -#line 3302 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3304 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 138: -#line 1150 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1150 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.intval) = (yyvsp[-1].intval) + 1; } -#line 3310 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3312 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 139: -#line 1156 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1156 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = nullptr; } -#line 3318 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3320 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 140: -#line 1159 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1159 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3326 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3328 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 141: -#line 1165 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1165 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = nullptr; } -#line 3334 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3336 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 142: -#line 1168 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1168 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeArrayLimit(nullptr, (yyvsp[0].node)); } -#line 3342 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3344 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 143: -#line 1171 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1171 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeArrayLimit((yyvsp[-2].node), (yyvsp[0].node)); } -#line 3350 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3352 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 144: -#line 1177 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1177 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = nullptr; } -#line 3358 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3360 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 145: -#line 1180 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1180 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3366 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3368 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 146: -#line 1186 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1186 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueString((yyvsp[0].strval).value, (yyvsp[0].strval).length); } -#line 3374 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3376 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 147: -#line 1189 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1189 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { char const* p = (yyvsp[0].node)->getStringValue(); size_t const len = (yyvsp[0].node)->getStringLength(); @@ -3383,20 +3385,20 @@ yyreduce: } (yyval.node) = (yyvsp[0].node); } -#line 3387 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3389 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 148: -#line 1197 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1197 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto tmp = parser->ast()->createNodeValueString((yyvsp[0].strval).value, (yyvsp[0].strval).length); (yyval.node) = parser->ast()->createNodeCollectionDirection((yyvsp[-1].intval), tmp); } -#line 3396 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3398 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 149: -#line 1201 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1201 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { char const* p = (yyvsp[0].node)->getStringValue(); size_t const len = (yyvsp[0].node)->getStringLength(); @@ -3405,58 +3407,58 @@ yyreduce: } (yyval.node) = parser->ast()->createNodeCollectionDirection((yyvsp[-1].intval), (yyvsp[0].node)); } -#line 3409 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3411 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 150: -#line 1212 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1212 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = static_cast(parser->peekStack()); node->addMember((yyvsp[0].node)); } -#line 3418 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3420 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 151: -#line 1216 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1216 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = static_cast(parser->peekStack()); node->addMember((yyvsp[0].node)); } -#line 3427 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3429 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 152: -#line 1223 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1223 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeArray(); node->addMember((yyvsp[0].node)); (yyval.node) = parser->ast()->createNodeCollectionList(node); } -#line 3437 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3439 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 153: -#line 1229 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1229 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = parser->ast()->createNodeArray(); parser->pushStack(node); node->addMember((yyvsp[-1].node)); } -#line 3447 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3449 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 154: -#line 1233 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1233 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto node = static_cast(parser->popStack()); (yyval.node) = parser->ast()->createNodeCollectionList(node); } -#line 3456 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3458 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 155: -#line 1237 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1237 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // graph name char const* p = (yyvsp[0].node)->getStringValue(); @@ -3466,60 +3468,60 @@ yyreduce: } (yyval.node) = (yyvsp[0].node); } -#line 3470 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3472 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 156: -#line 1246 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1246 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // graph name (yyval.node) = parser->ast()->createNodeValueString((yyvsp[0].strval).value, (yyvsp[0].strval).length); } -#line 3479 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3481 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 157: -#line 1255 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1255 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.intval) = 2; } -#line 3487 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3489 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 158: -#line 1258 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1258 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.intval) = 1; } -#line 3495 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3497 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 159: -#line 1261 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1261 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.intval) = 0; } -#line 3503 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3505 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 160: -#line 1267 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1267 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeDirection((yyvsp[0].intval), 1); } -#line 3511 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3513 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 161: -#line 1270 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1270 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeDirection((yyvsp[0].intval), (yyvsp[-1].node)); } -#line 3519 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3521 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 162: -#line 1276 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1276 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // variable or collection auto ast = parser->ast(); @@ -3552,27 +3554,27 @@ yyreduce: (yyval.node) = node; } -#line 3556 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3558 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 163: -#line 1308 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1308 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3564 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3566 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 164: -#line 1311 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1311 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3572 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3574 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 165: -#line 1314 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1314 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); @@ -3580,11 +3582,11 @@ yyreduce: ABORT_OOM } } -#line 3584 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3586 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 166: -#line 1321 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1321 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if ((yyvsp[-1].node)->type == NODE_TYPE_EXPANSION) { // create a dummy passthru node that reduces and evaluates the expansion first @@ -3595,20 +3597,20 @@ yyreduce: (yyval.node) = (yyvsp[-1].node); } } -#line 3599 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3601 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 167: -#line 1331 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1331 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { parser->ast()->scopes()->start(arangodb::aql::AQL_SCOPE_SUBQUERY); parser->ast()->startSubQuery(); } -#line 3608 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3610 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 168: -#line 1334 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1334 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { AstNode* node = parser->ast()->endSubQuery(); parser->ast()->scopes()->endCurrent(); @@ -3619,11 +3621,11 @@ yyreduce: (yyval.node) = parser->ast()->createNodeReference(variableName); } -#line 3623 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3625 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 169: -#line 1344 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1344 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // named variable access, e.g. variable.reference if ((yyvsp[-2].node)->type == NODE_TYPE_EXPANSION) { @@ -3639,11 +3641,11 @@ yyreduce: (yyval.node) = parser->ast()->createNodeAttributeAccess((yyvsp[-2].node), (yyvsp[0].strval).value, (yyvsp[0].strval).length); } } -#line 3643 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3645 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 170: -#line 1359 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1359 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // named variable access, e.g. variable.@reference if ((yyvsp[-2].node)->type == NODE_TYPE_EXPANSION) { @@ -3658,11 +3660,11 @@ yyreduce: (yyval.node) = parser->ast()->createNodeBoundAttributeAccess((yyvsp[-2].node), (yyvsp[0].node)); } } -#line 3662 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3664 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 171: -#line 1373 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1373 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // indexed variable access, e.g. variable[index] if ((yyvsp[-3].node)->type == NODE_TYPE_EXPANSION) { @@ -3677,11 +3679,11 @@ yyreduce: (yyval.node) = parser->ast()->createNodeIndexedAccess((yyvsp[-3].node), (yyvsp[-1].node)); } } -#line 3681 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3683 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 172: -#line 1387 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1387 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { // variable expansion, e.g. variable[*], with optional FILTER, LIMIT and RETURN clauses if ((yyvsp[0].intval) > 1 && (yyvsp[-2].node)->type == NODE_TYPE_EXPANSION) { @@ -3705,11 +3707,11 @@ yyreduce: auto scopes = parser->ast()->scopes(); scopes->stackCurrentVariable(scopes->getVariable(nextName)); } -#line 3709 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3711 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 173: -#line 1409 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1409 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { auto scopes = parser->ast()->scopes(); scopes->unstackCurrentVariable(); @@ -3728,27 +3730,27 @@ yyreduce: (yyval.node) = parser->ast()->createNodeExpansion((yyvsp[-5].intval), iterator, parser->ast()->createNodeReference(variable->name), (yyvsp[-3].node), (yyvsp[-2].node), (yyvsp[-1].node)); } } -#line 3732 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3734 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 174: -#line 1430 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1430 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3740 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3742 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 175: -#line 1433 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1433 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3748 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3750 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 176: -#line 1439 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1439 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if ((yyvsp[0].node) == nullptr) { ABORT_OOM @@ -3756,11 +3758,11 @@ yyreduce: (yyval.node) = (yyvsp[0].node); } -#line 3760 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3762 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 177: -#line 1446 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1446 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if ((yyvsp[0].node) == nullptr) { ABORT_OOM @@ -3768,67 +3770,67 @@ yyreduce: (yyval.node) = (yyvsp[0].node); } -#line 3772 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3774 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 178: -#line 1456 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1456 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueString((yyvsp[0].strval).value, (yyvsp[0].strval).length); } -#line 3780 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3782 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 179: -#line 1459 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1459 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = (yyvsp[0].node); } -#line 3788 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3790 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 180: -#line 1462 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1462 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueNull(); } -#line 3796 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3798 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 181: -#line 1465 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1465 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueBool(true); } -#line 3804 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3806 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 182: -#line 1468 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1468 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeValueBool(false); } -#line 3812 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3814 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 183: -#line 1474 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1474 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeCollection((yyvsp[0].strval).value, TRI_TRANSACTION_WRITE); } -#line 3820 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3822 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 184: -#line 1477 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1477 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeCollection((yyvsp[0].strval).value, TRI_TRANSACTION_WRITE); } -#line 3828 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3830 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 185: -#line 1480 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1480 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { if ((yyvsp[0].strval).length < 2 || (yyvsp[0].strval).value[0] != '@') { parser->registerParseError(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE, TRI_errno_string(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE), (yyvsp[0].strval).value, yylloc.first_line, yylloc.first_column); @@ -3836,43 +3838,43 @@ yyreduce: (yyval.node) = parser->ast()->createNodeParameter((yyvsp[0].strval).value, (yyvsp[0].strval).length); } -#line 3840 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3842 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 186: -#line 1490 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1490 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.node) = parser->ast()->createNodeParameter((yyvsp[0].strval).value, (yyvsp[0].strval).length); } -#line 3848 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3850 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 187: -#line 1496 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1496 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.strval) = (yyvsp[0].strval); } -#line 3856 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3858 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 188: -#line 1499 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1499 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.strval) = (yyvsp[0].strval); } -#line 3864 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3866 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; case 189: -#line 1504 "arangod/Aql/grammar.y" /* yacc.c:1646 */ +#line 1504 "arangod/Aql/grammar.y" /* yacc.c:1661 */ { (yyval.strval) = (yyvsp[0].strval); } -#line 3872 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3874 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ break; -#line 3876 "arangod/Aql/grammar.cpp" /* yacc.c:1646 */ +#line 3878 "arangod/Aql/grammar.cpp" /* yacc.c:1661 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires diff --git a/arangod/Aql/grammar.h b/arangod/Aql/grammar.h index ad6064a2be..af0205fd1a 100644 --- a/arangod/Aql/grammar.h +++ b/arangod/Aql/grammar.h @@ -1,8 +1,8 @@ -/* A Bison parser, made by GNU Bison 3.0.2. */ +/* A Bison parser, made by GNU Bison 3.0.4. */ /* Bison interface for Yacc-like parsers in C - Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. 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 @@ -117,10 +117,10 @@ extern int Aqldebug; /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED -typedef union YYSTYPE YYSTYPE; + union YYSTYPE { -#line 18 "arangod/Aql/grammar.y" /* yacc.c:1909 */ +#line 18 "arangod/Aql/grammar.y" /* yacc.c:1915 */ arangodb::aql::AstNode* node; struct { @@ -130,8 +130,10 @@ union YYSTYPE bool boolval; int64_t intval; -#line 134 "arangod/Aql/grammar.hpp" /* yacc.c:1909 */ +#line 134 "arangod/Aql/grammar.hpp" /* yacc.c:1915 */ }; + +typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif From 1b0cb631233c9973b6428320429460f705417fae Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 3 Feb 2016 10:15:54 +0100 Subject: [PATCH 08/61] minor changes of LOG and namespaces --- arangod/Agency/AgencyCommon.h | 45 +++++++++ arangod/RestHandler/RestAgencyHandler.cpp | 107 ++++++++++++++++++++++ arangod/RestHandler/RestAgencyHandler.h | 53 +++++++++++ m4/ax_compiler_vendor.m4 | 87 ++++++++++++++++++ 4 files changed, 292 insertions(+) create mode 100644 arangod/Agency/AgencyCommon.h create mode 100644 arangod/RestHandler/RestAgencyHandler.cpp create mode 100644 arangod/RestHandler/RestAgencyHandler.h create mode 100644 m4/ax_compiler_vendor.m4 diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h new file mode 100644 index 0000000000..d3538382ba --- /dev/null +++ b/arangod/Agency/AgencyCommon.h @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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 +#include + +namespace arangodb { +namespace consensus { + + /** + * @brief Agent configuration + */ + template struct Config { + T min_ping; + T max_ping; + std::vector end_points; + Config (T min_p, T max_p) : min_ping(min_p), max_ping(max_p) {} + }; + +}} +#endif // __ARANGODB_CONSENSUS_AGENT__ + diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp new file mode 100644 index 0000000000..26ae7fe71c --- /dev/null +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -0,0 +1,107 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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 "Rest/AnyServer.h" +#include "Rest/HttpRequest.h" +#include "Rest/Version.h" +#include "RestAgencyHandler.h" + +#include "Agency/Agent.h" + +#include +#include + +#include "Basics/Logger.h" + +using namespace arangodb; + +using namespace arangodb::basics; +using namespace arangodb::rest; +using namespace arangodb::consensus; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief ArangoDB server +//////////////////////////////////////////////////////////////////////////////// + +extern AnyServer* ArangoInstance; + + +RestAgencyHandler::RestAgencyHandler(HttpRequest* request, Agent* agent) + : RestBaseHandler(request), _agent(agent) { +} + +bool RestAgencyHandler::isDirect() const { return false; } + +HttpHandler::status_t RestAgencyHandler::execute() { + try { + VPackBuilder result; + result.add(VPackValue(VPackValueType::Object)); + + + // Empty request + if (_request->suffix().size() == 0) { + LOG(WARN) << "Empty request to agency. Must ask for vote, log or " + "configure"; + generateError(HttpResponse::NOT_FOUND,404); + return status_t(HANDLER_DONE); + } else if (_request->suffix().size() > 1) { // path size >= 2 + LOG(WARN) << "Agency handles a single suffix: vote, log or configure"; + generateError(HttpResponse::NOT_FOUND,404); + return status_t(HANDLER_DONE); + } else { + if (_request->suffix()[0].compare("vote") == 0) { //vote4me + LOG(WARN) << "Vote request"; + bool found; + char const* termStr = _request->value("term", found); + if (!found) { // For now: don't like this + LOG(WARN) << "No term specified"; // should be handled by + generateError(HttpResponse::BAD,400); // Agent rather than Rest + return status_t(HANDLER_DONE); // handler. + } + char const* idStr = _request->value("id", found); + if (!found) { + LOG(WARN) << "No id specified"; + generateError(HttpResponse::BAD,400); + return status_t(HANDLER_DONE); + } + if (_agent->vote(std::stoul(idStr), std::stoul(termStr))) { + result.add("vote", VPackValue("YES")); + } else { + result.add("vote", VPackValue("NO")); + } + } /*else { + + + + } else if (_request->suffix()[0].compare("log") == 0) { // log replication + } else if (_request->suffix()[0].compare("configure") == 0) {} // cluster conf*/ + } + + result.close(); + VPackSlice s = result.slice(); + generateResult(s); + } catch (...) { + // Ignore this error + } + return status_t(HANDLER_DONE); +} diff --git a/arangod/RestHandler/RestAgencyHandler.h b/arangod/RestHandler/RestAgencyHandler.h new file mode 100644 index 0000000000..6a4d3cc4a2 --- /dev/null +++ b/arangod/RestHandler/RestAgencyHandler.h @@ -0,0 +1,53 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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 version request handler +//////////////////////////////////////////////////////////////////////////////// + +class RestAgencyHandler : public arangodb::RestBaseHandler { + public: + + explicit RestAgencyHandler(arangodb::rest::HttpRequest*, + arangodb::consensus::Agent*); + + bool isDirect() const override; + + status_t execute() override; + + private: + + consensus::Agent* _agent; + +}; +} + +#endif diff --git a/m4/ax_compiler_vendor.m4 b/m4/ax_compiler_vendor.m4 new file mode 100644 index 0000000000..39ca3c0f33 --- /dev/null +++ b/m4/ax_compiler_vendor.m4 @@ -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 +# 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 . +# +# 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` + ]) +]) From c2069d4b15a57738280772933e9bbcc11d0f7ed1 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Thu, 4 Feb 2016 09:29:47 +0100 Subject: [PATCH 09/61] Election call in arango agency --- arangod/Agency/AgencyCommon.h | 4 +- arangod/Agency/Agent.cpp | 22 +++++--- arangod/Agency/Agent.h | 9 +++- arangod/Agency/ApplicationAgency.cpp | 9 +++- arangod/Agency/Constituent.cpp | 64 ++++++++++++++++------- arangod/Agency/Constituent.h | 19 ++++--- arangod/Agency/Log.cpp | 4 +- arangod/Agency/Log.h | 5 +- arangod/RestHandler/RestAgencyHandler.cpp | 31 ++++++----- config/config.h.in | 3 -- 10 files changed, 110 insertions(+), 60 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index d3538382ba..9aff4feff5 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -37,7 +37,9 @@ namespace consensus { T min_ping; T max_ping; std::vector end_points; - Config (T min_p, T max_p) : min_ping(min_p), max_ping(max_p) {} + Config () : min_ping(.15), max_ping(.3) {}; + Config (T min_p, T max_p, std::vector& end_p) : + min_ping(min_p), max_ping(max_p), end_points(end_p) {} }; }} diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index e73d6a7f77..eb1933ae84 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -24,15 +24,24 @@ #include "Agent.h" using namespace arangodb::consensus; +using namespace arangodb::velocypack; Agent::Agent () { - _constituent.start(); +} + +Agent::Agent (Config const& config) : _config(config) { + _constituent.configure(this); + } Agent::~Agent () { _constituent.stop(); } +void Agent::start() { + _constituent.start(); +} + Constituent::term_t Agent::term () const { return _constituent.term(); } @@ -41,13 +50,10 @@ bool Agent::vote(Constituent::id_t id, Constituent::term_t term) { return _constituent.vote(id, term); } -Slice const& Agent::log (const Slice& slice) { - if (_constituent.leader()) - return _log.log(slice); - else - return redirect(slice); +Log::ret_t Agent::log (std::shared_ptr const builder) { + return _log.log(builder); } -Slice const& Agent::redirect (const Slice& slice) { - return slice; //TODO +Config const& Agent::config () const { + return _config; } diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index a350c6382a..d3e865e399 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -59,14 +59,21 @@ namespace consensus { /** * @brief */ - Slice const& log (Slice const&); + Log::ret_t + log (std::shared_ptr const); + Slice const& redirect (Slice const&); bool vote(Constituent::id_t, Constituent::term_t); + + Config const& config () const; + + void start (); private: Constituent _constituent; /**< @brief Leader election delegate */ Log _log; /**< @brief Log replica */ + Config _config; }; diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index ce82e18523..b9acabba5d 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -37,9 +37,10 @@ using namespace arangodb::rest; ApplicationAgency::ApplicationAgency() : ApplicationFeature("agency"), _size(5), _min_election_timeout(.15), - _max_election_timeout(.3) , _agent(new consensus::Agent()){ + _max_election_timeout(.3) /*, _agent(new consensus::Agent())*/{ //arangodb::consensus::Config config (5, .15, .9); //_agent = std::uniqe_ptr(new config); + } @@ -57,7 +58,7 @@ void ApplicationAgency::setupOptions( "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.host", &_agency_endpoints, "Agency endpoints"); + ("agency.endpoint", &_agency_endpoints, "Agency endpoints"); } @@ -67,6 +68,9 @@ bool ApplicationAgency::prepare() { if (_disabled) { return true; } + _agent = std::unique_ptr(new consensus::Agent( + consensus::Config(_min_election_timeout, _max_election_timeout, + _agency_endpoints))); return true; } @@ -75,6 +79,7 @@ bool ApplicationAgency::start() { if (_disabled) { return true; } + _agent->start(); return true; } diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index cd304a0ab3..6cdd9a0990 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -25,23 +25,25 @@ #include "Basics/Logger.h" #include "Constituent.h" +#include "Agent.h" #include #include using namespace arangodb::consensus; +using namespace arangodb::rest; - -Constituent::Constituent (const uint32_t n) : Thread("Constituent"), - _term(0), _gen(std::random_device()()), _mode(FOLLOWER), _run(true), - _votes(std::vector(n)) { - +void Constituent::configure(Agent* agent) { + _agent = agent; } +Constituent::Constituent() : Thread("Constituent"), _term(0), + _gen(std::random_device()()), _mode(LEADER), _run(true), _agent(0) {} + Constituent::~Constituent() {} Constituent::duration_t Constituent::sleepFor () { - dist_t dis(.15, .99); + dist_t dis(_agent->config().min_ping, _agent->config().max_ping); return Constituent::duration_t(dis(_gen)); } @@ -90,32 +92,56 @@ bool Constituent::leader() const { void Constituent::callElection() { + LOG(WARN) << "Calling for election " << _agent->config().end_points.size(); + std::string body; + arangodb::velocypack::Options opts; std::unique_ptr> headerFields; - - std::vector results(_constituency.size()); - for (auto& i : _constituency) - results[i.id] = arangodb::ClusterComm::instance()->asyncRequest( - 0, 0, i.address, rest::HttpRequest::HTTP_REQUEST_GET, - "/_api/agency/vote?id=0&term=3", nullptr, headerFields, nullptr, - .75*.15); - std::this_thread::sleep_for(Constituent::duration_t(.9*.15)); - for (auto& i : _constituency) - arangodb::ClusterComm::instance()->enquire(results[i.id].operationID); + std::vector results(_agent->config().end_points.size()); + + for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { + LOG(WARN) << "+++ " << i << " +++ " << _agent->config().end_points[i] ; + results[i] = arangodb::ClusterComm::instance()->asyncRequest( + "1", 1, _agent->config().end_points[i], rest::HttpRequest::HTTP_REQUEST_GET, + "/_api/agency/vote?id=0&term=3", std::make_shared(body), + headerFields, nullptr, .75*_agent->config().min_ping, true); + LOG(WARN) << "+++ " << i << " +++ " << results[i].operationID ; + } + +/* + std::this_thread::sleep_for(Constituent::duration_t( + .85*_agent->config().min_ping)); + +/* for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { + ClusterCommResult res = arangodb::ClusterComm::instance()-> + enquire(results[i].operationID); + + //TRI_ASSERT(res.status == CL_COMM_SENT); + if (res.status == CL_COMM_SENT) { + res = arangodb::ClusterComm::instance()->wait("1", 1, results[i].operationID, "1"); + //std::shared_ptr< arangodb::velocypack::Builder > answer = res.answer->toVelocyPack(&opts); + //LOG(WARN) << answer->toString(); + } + }*/ +/* for (auto& i : _agent->config().end_points) { + ClusterCommResult res = arangodb::ClusterComm::instance()-> + enquire(results[i.id].operationID); + //LOG(WARN) << res.answer_code << "\n"; + }*/ + // time out //count votes } void Constituent::run() { - while (_run) { + //while (_run) { if (_state == FOLLOWER) { LOG(WARN) << "sleeping ... "; std::this_thread::sleep_for(sleepFor()); } else { callElection(); } - - } + //} }; diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h index c7f630922b..3fafb9921b 100644 --- a/arangod/Agency/Constituent.h +++ b/arangod/Agency/Constituent.h @@ -30,13 +30,17 @@ #include #include -#include "AgencyCommon.h" +#include "AgencyCommon.h" #include "Basics/Thread.h" + + namespace arangodb { namespace consensus { +class Agent; + /** * @brief Raft leader election */ @@ -52,24 +56,21 @@ public: typedef uint32_t id_t; struct constituent_t { id_t id; - std::string address; - std::string port; + std::string endpoint; }; typedef std::vector constituency_t; typedef uint32_t state_t; typedef std::uniform_real_distribution dist_t; - - /** - * brief Construct with size of constituency - */ - Constituent (const uint32_t n = 3); + Constituent (); /** * @brief Clean up and exit election */ virtual ~Constituent (); + void configure (Agent*); + term_t term() const; void runForLeaderShip (bool b); @@ -130,6 +131,8 @@ private: std::vector _votes; + Agent* _agent; + }; }} diff --git a/arangod/Agency/Log.cpp b/arangod/Agency/Log.cpp index c9a922fab3..40ec3aa386 100644 --- a/arangod/Agency/Log.cpp +++ b/arangod/Agency/Log.cpp @@ -28,6 +28,6 @@ using namespace arangodb::consensus; Log::Log() {} Log::~Log() {} -Slice const& Log::log (Slice const& slice) { - return slice; +Log::ret_t Log::log (std::shared_ptr const builder) { + return OK; } diff --git a/arangod/Agency/Log.h b/arangod/Agency/Log.h index ca16dcc0c3..28d2c0e2a9 100644 --- a/arangod/Agency/Log.h +++ b/arangod/Agency/Log.h @@ -22,7 +22,7 @@ //////////////////////////////////////////////////////////////////////////////// #include - +#include #include "AgencyCommon.h" //using namespace arangodb::velocypack; @@ -39,6 +39,7 @@ class Log { public: typedef uint64_t index_t; + enum ret_t {OK, REDIRECT}; /** * @brief Default constructor @@ -54,7 +55,7 @@ public: * @brief Log */ - Slice const& log (Slice const&); + ret_t log (std::shared_ptr const); private: diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index 26ae7fe71c..4e3439b8aa 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -56,7 +56,7 @@ HttpHandler::status_t RestAgencyHandler::execute() { try { VPackBuilder result; result.add(VPackValue(VPackValueType::Object)); - + arangodb::velocypack::Options opts; // Empty request if (_request->suffix().size() == 0) { @@ -73,28 +73,31 @@ HttpHandler::status_t RestAgencyHandler::execute() { LOG(WARN) << "Vote request"; bool found; char const* termStr = _request->value("term", found); - if (!found) { // For now: don't like this - LOG(WARN) << "No term specified"; // should be handled by - generateError(HttpResponse::BAD,400); // Agent rather than Rest + if (!found) { // For now: don't like this + LOG(WARN) << "No term specified"; // should be handled by + generateError(HttpResponse::BAD,400); // Agent rather than Rest return status_t(HANDLER_DONE); // handler. } char const* idStr = _request->value("id", found); if (!found) { LOG(WARN) << "No id specified"; - generateError(HttpResponse::BAD,400); + generateError(HttpResponse::BAD,400); return status_t(HANDLER_DONE); } - if (_agent->vote(std::stoul(idStr), std::stoul(termStr))) { - result.add("vote", VPackValue("YES")); - } else { - result.add("vote", VPackValue("NO")); - } - } /*else { - - + if (_agent->vote(std::stoul(idStr), std::stoul(termStr))) { + result.add("vote", VPackValue("YES")); + } else { + result.add("vote", VPackValue("NO")); + } } else if (_request->suffix()[0].compare("log") == 0) { // log replication - } else if (_request->suffix()[0].compare("configure") == 0) {} // cluster conf*/ + _agent->log(_request->toVelocyPack(&opts)); + } /*else if (_request->suffix()[0].compare("configure") == 0) { // cluster conf + _agent->configure(_request->toVelocyPack(&opts)); + } */else { + generateError(HttpResponse::METHOD_NOT_ALLOWED,405); + return status_t(HANDLER_DONE); + } } result.close(); diff --git a/config/config.h.in b/config/config.h.in index cdaa568194..52af50f5ca 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -94,9 +94,6 @@ /* true if failure testing ins enabled */ #undef TRI_ENABLE_FAILURE_TESTS -/* true if logging is enabled */ -#undef TRI_ENABLE_LOGGER - /* true if maintainer mode is enabled */ #undef TRI_ENABLE_MAINTAINER_MODE From 777783a41ed8ef0ae434e63ac5b8f22772897a3b Mon Sep 17 00:00:00 2001 From: Max Neunhoeffer Date: Thu, 4 Feb 2016 10:53:29 +0100 Subject: [PATCH 10/61] Add a non-nullptr asserting in asyncRequest. --- arangod/Cluster/ClusterComm.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arangod/Cluster/ClusterComm.cpp b/arangod/Cluster/ClusterComm.cpp index d14eab0aef..24dafb3d4f 100644 --- a/arangod/Cluster/ClusterComm.cpp +++ b/arangod/Cluster/ClusterComm.cpp @@ -228,6 +228,9 @@ ClusterCommResult const ClusterComm::asyncRequest( std::unique_ptr>& headerFields, std::shared_ptr callback, ClusterCommTimeout timeout, bool singleRequest) { + + TRI_ASSERT(headerFields.get() != nullptr); + auto op = std::make_unique(); op->result.clientTransactionID = clientTransactionID; op->result.coordTransactionID = coordTransactionID; From 29d0b0ae04fecfb0a855c1132420937ac6084887 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Thu, 4 Feb 2016 11:40:36 +0100 Subject: [PATCH 11/61] searching for bug in http client --- arangod/Agency/Constituent.cpp | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index 6cdd9a0990..f5077044c0 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -95,46 +95,42 @@ void Constituent::callElection() { LOG(WARN) << "Calling for election " << _agent->config().end_points.size(); std::string body; arangodb::velocypack::Options opts; - std::unique_ptr> headerFields; + std::unique_ptr> headerFields = + std::make_unique >(); + (new std::map()); std::vector results(_agent->config().end_points.size()); for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { - LOG(WARN) << "+++ " << i << " +++ " << _agent->config().end_points[i] ; results[i] = arangodb::ClusterComm::instance()->asyncRequest( "1", 1, _agent->config().end_points[i], rest::HttpRequest::HTTP_REQUEST_GET, "/_api/agency/vote?id=0&term=3", std::make_shared(body), - headerFields, nullptr, .75*_agent->config().min_ping, true); - LOG(WARN) << "+++ " << i << " +++ " << results[i].operationID ; + headerFields, nullptr, 0./*.75*_agent->config().min_ping*/, true); } -/* std::this_thread::sleep_for(Constituent::duration_t( .85*_agent->config().min_ping)); -/* for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { + for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { ClusterCommResult res = arangodb::ClusterComm::instance()-> enquire(results[i].operationID); - - //TRI_ASSERT(res.status == CL_COMM_SENT); + TRI_ASSERT(res.status == CL_COMM_SENT); if (res.status == CL_COMM_SENT) { res = arangodb::ClusterComm::instance()->wait("1", 1, results[i].operationID, "1"); - //std::shared_ptr< arangodb::velocypack::Builder > answer = res.answer->toVelocyPack(&opts); - //LOG(WARN) << answer->toString(); + std::shared_ptr< arangodb::velocypack::Builder > answer = res.answer->toVelocyPack(&opts); + /*LOG(WARN) << answer->toString();*/ + } else { + /**/ } - }*/ -/* for (auto& i : _agent->config().end_points) { - ClusterCommResult res = arangodb::ClusterComm::instance()-> - enquire(results[i.id].operationID); - //LOG(WARN) << res.answer_code << "\n"; - }*/ - // time out - //count votes + } + // count votes + } void Constituent::run() { //while (_run) { + if (_state == FOLLOWER) { LOG(WARN) << "sleeping ... "; std::this_thread::sleep_for(sleepFor()); From 70b62fdde907586b5cc3b3cca10f48d91150a812 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Thu, 4 Feb 2016 11:44:44 +0100 Subject: [PATCH 12/61] Pulled up to devel --- arangod/Agency/ApplicationAgency.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index b9acabba5d..ef6d28cf17 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -25,8 +25,7 @@ #include "Basics/win-utils.h" #endif - -#include "Basics/logging.h" +#include "Basics/Logger.h" #include "Scheduler/PeriodicTask.h" #include "ApplicationAgency.h" From eccfa530b22986e8ef84452fb095a852ce2ddf74 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Tue, 16 Feb 2016 14:08:59 +0100 Subject: [PATCH 13/61] Merging from devel --- arangod/Agency/AgencyCommon.h | 5 +- arangod/Agency/ApplicationAgency.cpp | 5 +- arangod/Agency/ApplicationAgency.h | 1 + arangod/Agency/Constituent.cpp | 95 ++++++++++++++++++---------- arangod/Agency/Constituent.h | 10 ++- arangod/CMakeLists.txt | 5 ++ 6 files changed, 82 insertions(+), 39 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 9aff4feff5..0d6d75913c 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -36,10 +36,11 @@ namespace consensus { template struct Config { T min_ping; T max_ping; + uint32_t id; std::vector end_points; Config () : min_ping(.15), max_ping(.3) {}; - Config (T min_p, T max_p, std::vector& end_p) : - min_ping(min_p), max_ping(max_p), end_points(end_p) {} + Config (uint32_t i, T min_p, T max_p, std::vector& end_p) : + id(i), min_ping(min_p), max_ping(max_p), end_points(end_p) {} }; }} diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index ef6d28cf17..383b986337 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -53,6 +53,7 @@ ApplicationAgency::~ApplicationAgency() {} void ApplicationAgency::setupOptions( std::map& 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 " @@ -68,8 +69,8 @@ bool ApplicationAgency::prepare() { return true; } _agent = std::unique_ptr(new consensus::Agent( - consensus::Config(_min_election_timeout, _max_election_timeout, - _agency_endpoints))); + consensus::Config(_agent_id, _min_election_timeout, + _max_election_timeout, _agency_endpoints))); return true; } diff --git a/arangod/Agency/ApplicationAgency.h b/arangod/Agency/ApplicationAgency.h index c62406c8a0..cd8039127a 100644 --- a/arangod/Agency/ApplicationAgency.h +++ b/arangod/Agency/ApplicationAgency.h @@ -115,6 +115,7 @@ class Task; double _max_election_timeout; /**< @brief: max election timeout */ std::vector _agency_endpoints; std::unique_ptr _agent; + uint32_t _agent_id; }; } diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index f5077044c0..f63463574a 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -35,10 +35,11 @@ using namespace arangodb::rest; void Constituent::configure(Agent* agent) { _agent = agent; + _votes.resize(_agent->config().end_points.size()); } -Constituent::Constituent() : Thread("Constituent"), _term(0), - _gen(std::random_device()()), _mode(LEADER), _run(true), _agent(0) {} +Constituent::Constituent() : Thread("Constituent"), _term(0), _id(0), + _gen(std::random_device()()), _mode(CANDIDATE), _run(true), _agent(0) {} Constituent::~Constituent() {} @@ -62,14 +63,28 @@ Constituent::mode_t Constituent::mode () const { return _mode; } -bool Constituent::vote (id_t id, term_t term) { - LOG(WARN) << _id << "," << _term << " " << id << "," << term; - if (id == _id) +void Constituent::becomeFollower() { + _votes.assign(_votes.size(),false); // void all votes + _mode = FOLLOWER; +} + +void Constituent::becomeLeader() { + +} + +void Constituent::becomeCadidate() { + +} + +// Vote for the requester if and only if I am follower +bool Constituent::vote(id_t id, term_t term) { + if (id == _id) { // Won't vote for myself return false; - else { - if (term > _term) { - _state = FOLLOWER; - _term = term; + } else { + if (term > _term) { // Candidate with higher term: ALWAYS turn + if (_mode > FOLLOWER) + LOG(WARN) << "Cadidate with higher term. Becoming follower"; + becomeFollower (); return true; } else { return false; @@ -91,53 +106,69 @@ bool Constituent::leader() const { } void Constituent::callElection() { + + _votes[_id] = true; // vote for myself + _term++; - LOG(WARN) << "Calling for election " << _agent->config().end_points.size(); std::string body; arangodb::velocypack::Options opts; std::unique_ptr> headerFields = - std::make_unique >(); - (new std::map()); + std::make_unique >(); std::vector results(_agent->config().end_points.size()); + std::stringstream path; + path << "/_api/agency/vote?id=" << _id << "&term=" << _term; for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { - results[i] = arangodb::ClusterComm::instance()->asyncRequest( - "1", 1, _agent->config().end_points[i], rest::HttpRequest::HTTP_REQUEST_GET, - "/_api/agency/vote?id=0&term=3", std::make_shared(body), - headerFields, nullptr, 0./*.75*_agent->config().min_ping*/, true); + if (i != _id) { + results[i] = arangodb::ClusterComm::instance()->asyncRequest("1", 1, + _agent->config().end_points[i], rest::HttpRequest::HTTP_REQUEST_GET, + path.str(), std::make_shared(body), headerFields, nullptr, + .75*_agent->config().min_ping, true); + LOG(WARN) << _agent->config().end_points[i]; + } } std::this_thread::sleep_for(Constituent::duration_t( .85*_agent->config().min_ping)); for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { - ClusterCommResult res = arangodb::ClusterComm::instance()-> + if (i != _id) { + ClusterCommResult res = arangodb::ClusterComm::instance()-> enquire(results[i].operationID); - TRI_ASSERT(res.status == CL_COMM_SENT); - if (res.status == CL_COMM_SENT) { - res = arangodb::ClusterComm::instance()->wait("1", 1, results[i].operationID, "1"); - std::shared_ptr< arangodb::velocypack::Builder > answer = res.answer->toVelocyPack(&opts); - /*LOG(WARN) << answer->toString();*/ - } else { - /**/ - } - + + if (res.status == CL_COMM_SENT) { // Request successfully sent + res = arangodb::ClusterComm::instance()->wait("1", 1, results[i].operationID, "1"); + std::shared_ptr< arangodb::velocypack::Builder > body = res.result->getBodyVelocyPack(); + if (body->isEmpty()) { + continue; + } else { + if (!body->slice().hasKey("vote")) { // Answer has no vote. What happened? + _votes[i] = false; + continue; + } else { + LOG(WARN) << body->slice().get("vote"); + } + } + //LOG(WARN) << body->toString(); + } else { // Request failed + _votes[i] = false; + } + } } - // count votes - } void Constituent::run() { - //while (_run) { - - if (_state == FOLLOWER) { + while (true) { + LOG(WARN) << _mode; + if (_mode == FOLLOWER) { LOG(WARN) << "sleeping ... "; std::this_thread::sleep_for(sleepFor()); } else { + LOG(WARN) << "calling ... "; callElection(); } - //} + } }; diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h index 3fafb9921b..db7b51e9dd 100644 --- a/arangod/Agency/Constituent.h +++ b/arangod/Agency/Constituent.h @@ -49,7 +49,7 @@ class Constituent : public arangodb::basics::Thread { public: enum mode_t { - LEADER, CANDIDATE, FOLLOWER + FOLLOWER, CANDIDATE, LEADER }; typedef std::chrono::duration duration_t; typedef uint64_t term_t; @@ -95,10 +95,14 @@ public: void run(); - void voteCallBack(); - private: + void becomeFollower (); + + void becomeCadidate (); + + void becomeLeader (); + /** * @brief Call for vote (by leader or candidates after timeout) */ diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index 32efba2ff4..ec8fbd150c 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -60,6 +60,10 @@ add_executable( ${ProductVersionFiles} Actions/actions.cpp Actions/RestActionHandler.cpp + Agency/Agent.cpp + Agency/ApplicationAgency.cpp + Agency/Constituent.cpp + Agency/Log.cpp ApplicationServer/ApplicationFeature.cpp ApplicationServer/ApplicationServer.cpp Aql/Aggregator.cpp @@ -176,6 +180,7 @@ add_executable( Replication/Syncer.cpp Rest/AnyServer.cpp RestHandler/RestAdminLogHandler.cpp + RestHandler/RestAgencyHandler.cpp RestHandler/RestBaseHandler.cpp RestHandler/RestBatchHandler.cpp RestHandler/RestDebugHandler.cpp From b639d79f177ec1a11d4c0ba198e76fac23de36b2 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Fri, 19 Feb 2016 11:51:39 +0100 Subject: [PATCH 14/61] Agency on --- arangod/Agency/AgencyCommon.h | 44 ++++++++++++++++++++++++-- arangod/Agency/Agent.cpp | 11 ++++++- arangod/Agency/Agent.h | 46 ++++++++++++++++++++++------ arangod/Agency/ApplicationAgency.cpp | 26 +++++++++++++--- arangod/Agency/Constituent.cpp | 34 +++++++++++--------- arangod/Agency/Constituent.h | 17 +++++----- 6 files changed, 136 insertions(+), 42 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 0d6d75913c..060afee69c 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -24,12 +24,24 @@ #ifndef __ARANGODB_CONSENSUS_AGENCY_COMMON__ #define __ARANGODB_CONSENSUS_AGENCY_COMMON__ +#include "Basics/Logger.h" + #include #include 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! + } status_t; + + + /** * @brief Agent configuration */ @@ -41,8 +53,34 @@ namespace consensus { Config () : min_ping(.15), max_ping(.3) {}; Config (uint32_t i, T min_p, T max_p, std::vector& end_p) : id(i), min_ping(min_p), max_ping(max_p), end_points(end_p) {} - }; + void print (arangodb::LoggerStream& l) const { + l << "Config: " + << "min_ping(" << min_ping << ")" + << "max_ping(" << max_ping << ")" + << "size(" << end_points.size() << ")" + << end_points; + } + }; + + using config_t = Config; -}} + typedef uint64_t term_t; + typedef uint32_t id_t; + struct constituent_t { + id_t id; + std::string endpoint; + }; + typedef std::vector constituency_t; + typedef uint32_t state_t; + + +} + + template LoggerStream& operator<< (LoggerStream& l, + arangodb::consensus::Config const& c) { + + } + +} #endif // __ARANGODB_CONSENSUS_AGENT__ diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index eb1933ae84..6e7276baa6 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -29,7 +29,7 @@ using namespace arangodb::velocypack; Agent::Agent () { } -Agent::Agent (Config const& config) : _config(config) { +Agent::Agent (config_t const& config) : _config(config) { _constituent.configure(this); } @@ -57,3 +57,12 @@ Log::ret_t Agent::log (std::shared_ptr const builder) { Config const& Agent::config () const { return _config; } + +void Agent::print (arangodb::LoggerStream& logger) const { + logger << _config; +} + +arangodb::LoggerStream& operator<< (arangodb::LoggerStream& l, Agent const& a) { + a.print(l); + return l; +} diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index d3e865e399..f856b1fdad 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -41,10 +41,9 @@ namespace consensus { Agent (); /** - * @brief Construct with program options \n - * One process starts with options, the \n remaining start with list of peers and gossip. + * @brief Construct with program options */ - Agent (Config const&); + Agent (config_t const&); /** * @brief Clean up @@ -59,17 +58,38 @@ namespace consensus { /** * @brief */ - Log::ret_t - log (std::shared_ptr const); - - Slice const& redirect (Slice const&); + Log::ret_t log (std::shared_ptr const); + /** + * @brief Vote request + */ bool vote(Constituent::id_t, Constituent::term_t); + /** + * @brief Provide configuration + */ Config 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 + */ + bool report ( ) const; + private: Constituent _constituent; /**< @brief Leader election delegate */ Log _log; /**< @brief Log replica */ @@ -77,6 +97,12 @@ namespace consensus { }; -}} +} + + LoggerStream& operator<< (LoggerStream&, arangodb::consensus::Agent const&); + +} + + #endif diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index 383b986337..ae82485b2d 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -35,10 +35,8 @@ using namespace arangodb::basics; using namespace arangodb::rest; ApplicationAgency::ApplicationAgency() - : ApplicationFeature("agency"), _size(5), _min_election_timeout(.15), - _max_election_timeout(.3) /*, _agent(new consensus::Agent())*/{ - //arangodb::consensus::Config config (5, .15, .9); - //_agent = std::uniqe_ptr(new config); + : ApplicationFeature("agency"), _size(5), _min_election_timeout(.15), + _max_election_timeout(.3) { } @@ -63,15 +61,33 @@ void ApplicationAgency::setupOptions( - bool ApplicationAgency::prepare() { + if (_disabled) { return true; } + + 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!"; + } + _agent = std::unique_ptr(new consensus::Agent( consensus::Config(_agent_id, _min_election_timeout, _max_election_timeout, _agency_endpoints))); + return true; + } diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index f63463574a..bfc3aeac6b 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -36,10 +36,12 @@ using namespace arangodb::rest; void Constituent::configure(Agent* agent) { _agent = agent; _votes.resize(_agent->config().end_points.size()); + if (_agent->config().id == (_votes.size()-1)) // Last will notify eveyone + notifyAll(); } Constituent::Constituent() : Thread("Constituent"), _term(0), _id(0), - _gen(std::random_device()()), _mode(CANDIDATE), _run(true), _agent(0) {} + _gen(std::random_device()()), _mode(APPRENTICE), _run(true), _agent(0) {} Constituent::~Constituent() {} @@ -63,30 +65,35 @@ Constituent::mode_t Constituent::mode () const { return _mode; } -void Constituent::becomeFollower() { +void Constituent::follow() { _votes.assign(_votes.size(),false); // void all votes _mode = FOLLOWER; } -void Constituent::becomeLeader() { - +void Constituent::lead() { + _mode = LEADER; } -void Constituent::becomeCadidate() { +void Constituent::candidate() { + _mode = CANDIDATE; +} +size_t Constituent::notifyAll () { } // Vote for the requester if and only if I am follower bool Constituent::vote(id_t id, term_t term) { - if (id == _id) { // Won't vote for myself + if (id == _id) { // Won't vote for myself return false; } else { - if (term > _term) { // Candidate with higher term: ALWAYS turn - if (_mode > FOLLOWER) + if (term > _term) { // Candidate with higher term: ALWAYS turn follower + if (_mode > FOLLOWER) { LOG(WARN) << "Cadidate with higher term. Becoming follower"; - becomeFollower (); + } + follow (); + _term = term; // Raise term return true; - } else { + } else { // Myself running or leading return false; } } @@ -146,14 +153,13 @@ void Constituent::callElection() { _votes[i] = false; continue; } else { - LOG(WARN) << body->slice().get("vote"); + _votes[i] = (body->slice().get("vote").isEqualString("TRUE")); // Record vote } } - //LOG(WARN) << body->toString(); + LOG(WARN) << body->toString(); } else { // Request failed _votes[i] = false; } - } } } @@ -161,7 +167,7 @@ void Constituent::callElection() { void Constituent::run() { while (true) { LOG(WARN) << _mode; - if (_mode == FOLLOWER) { + if (_mode == FOLLOWER || _mode == APPRENTICE) { LOG(WARN) << "sleeping ... "; std::this_thread::sleep_for(sleepFor()); } else { diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h index db7b51e9dd..79e8689c30 100644 --- a/arangod/Agency/Constituent.h +++ b/arangod/Agency/Constituent.h @@ -49,17 +49,9 @@ class Constituent : public arangodb::basics::Thread { public: enum mode_t { - FOLLOWER, CANDIDATE, LEADER + APPRENTICE = -1, FOLLOWER, CANDIDATE, LEADER }; typedef std::chrono::duration duration_t; - typedef uint64_t term_t; - typedef uint32_t id_t; - struct constituent_t { - id_t id; - std::string endpoint; - }; - typedef std::vector constituency_t; - typedef uint32_t state_t; typedef std::uniform_real_distribution dist_t; Constituent (); @@ -112,6 +104,13 @@ private: * @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 From 1f9414f3ea82c8f565661401dafd9debf0c6345b Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Fri, 19 Feb 2016 18:58:53 +0100 Subject: [PATCH 15/61] Agencing on --- arangod/Agency/AgencyCommon.h | 31 +++-- arangod/Agency/Agent.cpp | 33 +++--- arangod/Agency/Agent.h | 11 +- arangod/Agency/ApplicationAgency.cpp | 11 +- arangod/Agency/ApplicationAgency.h | 1 + arangod/Agency/Constituent.cpp | 162 ++++++++++++++++----------- arangod/Agency/Constituent.h | 87 +++++++------- arangod/Agency/Log.cpp | 8 +- arangod/Agency/Log.h | 29 ++++- lib/Rest/SslInterface.cpp | 12 +- 10 files changed, 237 insertions(+), 148 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 060afee69c..7e6f2cab95 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -26,8 +26,9 @@ #include "Basics/Logger.h" -#include +#include #include +#include namespace arangodb { namespace consensus { @@ -40,15 +41,21 @@ namespace consensus { // while leading. Very bad! } status_t; + typedef uint64_t term_t; // Term type + typedef uint32_t id_t; // Id type + + enum role_t { // Role + APPRENTICE = -1, FOLLOWER, CANDIDATE, LEADER + }; - /** * @brief Agent configuration */ - template struct Config { + template struct Config { T min_ping; T max_ping; - uint32_t id; + T election_timeout; + id_t id; std::vector end_points; Config () : min_ping(.15), max_ping(.3) {}; Config (uint32_t i, T min_p, T max_p, std::vector& end_p) : @@ -60,19 +67,19 @@ namespace consensus { << "size(" << end_points.size() << ")" << end_points; } + inline size_t size() const {return end_points.size();} }; - - using config_t = Config; - typedef uint64_t term_t; - typedef uint32_t id_t; - struct constituent_t { + using config_t = Config; // Configuration type + + struct constituent_t { // Constituent type id_t id; std::string endpoint; }; - typedef std::vector constituency_t; - typedef uint32_t state_t; - + typedef std::vector constituency_t; // Constituency type + typedef uint32_t state_t; // State type + typedef std::chrono::duration duration_t; // Duration type + } diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 6e7276baa6..c5372e1433 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -23,37 +23,30 @@ #include "Agent.h" -using namespace arangodb::consensus; using namespace arangodb::velocypack; +namespace arangodb { + namespace consensus { -Agent::Agent () { -} +Agent::Agent () {} Agent::Agent (config_t const& config) : _config(config) { _constituent.configure(this); - } -Agent::~Agent () { - _constituent.stop(); -} +Agent::~Agent () {} void Agent::start() { _constituent.start(); } -Constituent::term_t Agent::term () const { +term_t Agent::term () const { return _constituent.term(); } -bool Agent::vote(Constituent::id_t id, Constituent::term_t term) { +bool Agent::vote(id_t id, term_t term) { return _constituent.vote(id, term); } -Log::ret_t Agent::log (std::shared_ptr const builder) { - return _log.log(builder); -} - Config const& Agent::config () const { return _config; } @@ -62,7 +55,21 @@ void Agent::print (arangodb::LoggerStream& logger) const { logger << _config; } +void Agent::report(status_t status) { + _status = status; +} + arangodb::LoggerStream& operator<< (arangodb::LoggerStream& l, Agent const& a) { a.print(l); return l; } + + +template<> Log::ret_t Agent::log (std::shared_ptr const& builder) { + if (_constituent.leading()) + return _log.log(builder); + else + return redirect; +} + + }} diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index f856b1fdad..41c93c65f6 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -53,17 +53,17 @@ namespace consensus { /** * @brief Get current term */ - Constituent::term_t term() const; + term_t term() const; /** * @brief */ - Log::ret_t log (std::shared_ptr const); + template Log::ret_t log (T const&); /** * @brief Vote request */ - bool vote(Constituent::id_t, Constituent::term_t); + bool vote(id_t, term_t); /** * @brief Provide configuration @@ -88,12 +88,13 @@ namespace consensus { /** * @brief */ - bool report ( ) const; + void report (status_t); private: Constituent _constituent; /**< @brief Leader election delegate */ Log _log; /**< @brief Log replica */ - Config _config; + config_t _config; + status_t _status; }; diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index ae82485b2d..3feebe060e 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -35,9 +35,9 @@ using namespace arangodb::basics; using namespace arangodb::rest; ApplicationAgency::ApplicationAgency() - : ApplicationFeature("agency"), _size(5), _min_election_timeout(.15), - _max_election_timeout(.3) { - + : ApplicationFeature("agency"), _size(5), _min_election_timeout(.5), + _max_election_timeout(1.), _election_call_rate_mul(.75) { + } @@ -56,7 +56,10 @@ void ApplicationAgency::setupOptions( "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.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"); } diff --git a/arangod/Agency/ApplicationAgency.h b/arangod/Agency/ApplicationAgency.h index cd8039127a..fc0c57584a 100644 --- a/arangod/Agency/ApplicationAgency.h +++ b/arangod/Agency/ApplicationAgency.h @@ -113,6 +113,7 @@ class Task; 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: */ std::vector _agency_endpoints; std::unique_ptr _agent; uint32_t _agent_id; diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index bfc3aeac6b..594d720e09 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -41,90 +41,59 @@ void Constituent::configure(Agent* agent) { } Constituent::Constituent() : Thread("Constituent"), _term(0), _id(0), - _gen(std::random_device()()), _mode(APPRENTICE), _run(true), _agent(0) {} + _gen(std::random_device()()), _role(APPRENTICE), _agent(0) {} Constituent::~Constituent() {} -Constituent::duration_t Constituent::sleepFor () { +duration_t Constituent::sleepFor () { dist_t dis(_agent->config().min_ping, _agent->config().max_ping); - return Constituent::duration_t(dis(_gen)); + return duration_t(dis(_gen)); } -Constituent::term_t Constituent::term() const { +term_t Constituent::term() const { return _term; } -void Constituent::runForLeaderShip (bool b) { +role_t Constituent::role () const { + return _role; } -Constituent::state_t Constituent::state () const { - return _state; -} - -Constituent::mode_t Constituent::mode () const { - return _mode; -} - -void Constituent::follow() { +void Constituent::follow(term_t term) { + _term = term; _votes.assign(_votes.size(),false); // void all votes - _mode = FOLLOWER; + _role = FOLLOWER; } void Constituent::lead() { - _mode = LEADER; + _role = LEADER; } void Constituent::candidate() { - _mode = CANDIDATE; + _role = CANDIDATE; +} + +bool Constituent::leading () const { + return _role == LEADER; +} + +bool Constituent::following () const { + return _role == FOLLOWER; +} + +bool Constituent::running () const { + return _role == CANDIDATE; } size_t Constituent::notifyAll () { -} - -// Vote for the requester if and only if I am follower -bool Constituent::vote(id_t id, term_t term) { - if (id == _id) { // Won't vote for myself - return false; - } else { - if (term > _term) { // Candidate with higher term: ALWAYS turn follower - if (_mode > FOLLOWER) { - LOG(WARN) << "Cadidate with higher term. Becoming follower"; - } - follow (); - _term = term; // Raise term - return true; - } else { // Myself running or leading - return false; - } - } -} - -void Constituent::gossip (const Constituent::constituency_t& constituency) { - // Talk to all peers i have not talked to - // Answer by sending my complete list of peers -} - -const Constituent::constituency_t& Constituent::gossip () { - return _constituency; -} - -bool Constituent::leader() const { - return _mode==LEADER; -} - -void Constituent::callElection() { - - _votes[_id] = true; // vote for myself - _term++; - + // Last process notifies everyone std::string body; arangodb::velocypack::Options opts; std::unique_ptr> headerFields = std::make_unique >(); std::vector results(_agent->config().end_points.size()); std::stringstream path; - path << "/_api/agency/vote?id=" << _id << "&term=" << _term; - + path << "/_api/agency/notify?agency_size=" << _id << "&term=" << _term; + for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { if (i != _id) { results[i] = arangodb::ClusterComm::instance()->asyncRequest("1", 1, @@ -134,11 +103,71 @@ void Constituent::callElection() { LOG(WARN) << _agent->config().end_points[i]; } } +} - std::this_thread::sleep_for(Constituent::duration_t( - .85*_agent->config().min_ping)); - for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { +bool Constituent::vote(id_t id, term_t term) { + if (id == _id) { // Won't vote for myself should never happen. + return false; // TODO: Assertion? + } else { + if (term > _term) { // Candidate with higher term: ALWAYS turn follower if not already + switch(_role) { + case(LEADER): // I was leading. What happened? + LOG(WARN) << "Cadidate with higher term. Becoming follower."; + _agent->report(RESIGNED_LEADERSHIP_FOR_HIGHER_TERM); + break; + case(CANDIDATE): // I was candidate. What happened? + LOG(WARN) << "Cadidate with higher term. Becoming follower. Something bad has happened?"; + _agent->report(RETRACTED_CANDIDACY_FOR_HIGHER_TERM); + break; + default: + break; + } + _cast = true; // Note that I voted this time around. + _leader_id = id; // The guy I voted for I assume leader. + follow (term); + 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 + _term++; // raise my term + + std::string body; + arangodb::velocypack::Options opts; + std::unique_ptr> headerFields = + std::make_unique >(); + std::vector results(_agent->config().end_points.size()); + std::stringstream path; + path << "/_api/agency/vote?id=" << _id << "&term=" << _term; + + for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { // Ask everyone for their vote + if (i != _id) { + results[i] = arangodb::ClusterComm::instance()->asyncRequest("1", 1, + _agent->config().end_points[i], rest::HttpRequest::HTTP_REQUEST_GET, + path.str(), std::make_shared(body), headerFields, nullptr, + .75*_agent->config().min_ping, true); + LOG(WARN) << _agent->config().end_points[i]; + } + } + + std::this_thread::sleep_for(duration_t(.85*_agent->config().min_ping)); // Wait timeout + + for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { // Collect votes if (i != _id) { ClusterCommResult res = arangodb::ClusterComm::instance()-> enquire(results[i].operationID); @@ -165,15 +194,18 @@ void Constituent::callElection() { } void Constituent::run() { - while (true) { - LOG(WARN) << _mode; - if (_mode == FOLLOWER || _mode == APPRENTICE) { - LOG(WARN) << "sleeping ... "; + if (_id == _agent->config().size()-1) + notifyAll(); + while (true) { + if (_role == FOLLOWER || _role == APPRENTICE) { + _cast = false; // New round set not cast vote std::this_thread::sleep_for(sleepFor()); + if (!_cast) + candidate(); } else { - LOG(WARN) << "calling ... "; callElection(); } + } }; diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h index 79e8689c30..8766adbcd7 100644 --- a/arangod/Agency/Constituent.h +++ b/arangod/Agency/Constituent.h @@ -25,7 +25,6 @@ #define __ARANGODB_CONSENSUS_CONSTITUENT__ #include -#include #include #include #include @@ -34,8 +33,6 @@ #include "AgencyCommon.h" #include "Basics/Thread.h" - - namespace arangodb { namespace consensus { @@ -44,61 +41,72 @@ class Agent; /** * @brief Raft leader election */ -class Constituent : public arangodb::basics::Thread { +class Constituent : public arangodb::Thread { public: - enum mode_t { - APPRENTICE = -1, FOLLOWER, CANDIDATE, LEADER - }; - typedef std::chrono::duration duration_t; typedef std::uniform_real_distribution dist_t; - Constituent (); + Constituent(); /** * @brief Clean up and exit election */ - virtual ~Constituent (); + virtual ~Constituent(); - void configure (Agent*); + void configure(Agent*); term_t term() const; void runForLeaderShip (bool b); - state_t state () const; - - mode_t mode () const; + role_t role() const; /** * @brief Gossip protocol: listen */ - void gossip (Constituent::constituency_t const& constituency); + void gossip(constituency_t const&); /** * @brief Gossip protocol: talk */ - const Constituent::constituency_t& gossip (); - - bool leader() const; + const constituency_t& gossip(); + bool leading() const; + bool following() const; + bool running() const; + + /** + * @brief Called by REST handler + */ bool vote(id_t, term_t); + /** + * @brief My daily business + */ void run(); private: - void becomeFollower (); + /** + * @brief Become follower + */ + void follow(term_t); - void becomeCadidate (); + /** + * @brief Run for leadership + */ + void candidate(); - void becomeLeader (); + /** + * @brief Become leader + */ + void lead(); /** * @brief Call for vote (by leader or candidates after timeout) */ - void callElection (); + void callElection(); /** * @brief Count my votes @@ -110,31 +118,26 @@ private: * This is the task of the last process starting up. * Will be taken care of by gossip */ - size_t notifyAll (); + size_t notifyAll(); /** * @brief Sleep for how long */ - duration_t sleepFor (); - - term_t _term; /**< @brief term number */ - id_t _leader_id; /**< @brief Current leader */ - id_t _cur_vote; /**< @brief My current vote */ - id_t _id; - constituency_t _constituency; /**< @brief List of consituents */ - uint32_t _nvotes; /**< @brief Votes in my favour - * (candidate/leader) */ - state_t _state; /**< @brief State (follower, - * candidate, leader)*/ - std::mt19937 _gen; - - mode_t _mode; - - bool _run; + duration_t sleepFor(); - std::vector _votes; - - Agent* _agent; + // mission critical + std::atomic _term; /**< @brief term number */ + std::atomic _cast; /**< @brief cast a vote this term */ + std::atomic _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 _votes; /**< @brief My list of votes cast in my favour*/ + Agent* _agent; /**< @brief My boss */ }; diff --git a/arangod/Agency/Log.cpp b/arangod/Agency/Log.cpp index 40ec3aa386..9680bd493e 100644 --- a/arangod/Agency/Log.cpp +++ b/arangod/Agency/Log.cpp @@ -23,11 +23,15 @@ #include "Log.h" -using namespace arangodb::consensus; +namespace arangodb { +namespace consensus { Log::Log() {} Log::~Log() {} -Log::ret_t Log::log (std::shared_ptr const builder) { +template<> Log::ret_t Log::log ( + std::shared_ptr const& builder) { return OK; } + +}} diff --git a/arangod/Agency/Log.h b/arangod/Agency/Log.h index 28d2c0e2a9..56ec622f4a 100644 --- a/arangod/Agency/Log.h +++ b/arangod/Agency/Log.h @@ -54,10 +54,35 @@ public: /** * @brief Log */ - - ret_t log (std::shared_ptr const); + template ret_t log (T const&); private: + + /** + * @brief Write transaction + * @param state State demanded + * @param expiry Time of expiration + * @param update Update state + */ + template std::shared_ptr readTransaction ( + T const& state, T const& update); + + /** + * @brief Write transaction + * @param state State demanded + * @param expiry Time of expiration + * @param update Update state + */ + template std::shared_ptr writeTransaction ( + T const& state, duration_t expiry, T const& update); + + /** + * @brief Check transaction condition + * @param state State demanded + * @param pre Prerequisite + */ + template bool checkTransactionPrecondition ( + T const& state, T const& pre); index_t _commit_id; /**< @brief: index of highest log entry known to be committed (initialized to 0, increases monotonically) */ diff --git a/lib/Rest/SslInterface.cpp b/lib/Rest/SslInterface.cpp index 2602423565..d801755e53 100644 --- a/lib/Rest/SslInterface.cpp +++ b/lib/Rest/SslInterface.cpp @@ -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; } From 77c95803443184ba2c44c16cfbee4acbef07167d Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 22 Feb 2016 16:53:02 +0100 Subject: [PATCH 16/61] agency needs some reworking --- arangod/Agency/AgencyCommon.h | 17 ++++--- arangod/Agency/Agent.cpp | 14 ++++-- arangod/Agency/Agent.h | 5 ++ arangod/Agency/Constituent.cpp | 11 +++-- arangod/Agency/Constituent.h | 5 ++ arangod/Agency/Log.cpp | 51 ++++++++++++++++++-- arangod/Agency/Log.h | 57 +++++++++++++++++++---- arangod/CMakeLists.txt | 1 + arangod/RestHandler/RestAgencyHandler.cpp | 7 ++- 9 files changed, 132 insertions(+), 36 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 7e6f2cab95..88a6110538 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -24,7 +24,7 @@ #ifndef __ARANGODB_CONSENSUS_AGENCY_COMMON__ #define __ARANGODB_CONSENSUS_AGENCY_COMMON__ -#include "Basics/Logger.h" +#include #include #include @@ -60,13 +60,13 @@ namespace consensus { Config () : min_ping(.15), max_ping(.3) {}; Config (uint32_t i, T min_p, T max_p, std::vector& end_p) : id(i), min_ping(min_p), max_ping(max_p), end_points(end_p) {} - void print (arangodb::LoggerStream& l) const { +/* void print (arangodb::LoggerStream& l) const { l << "Config: " << "min_ping(" << min_ping << ")" << "max_ping(" << max_ping << ")" << "size(" << end_points.size() << ")" << end_points; - } + }*/ inline size_t size() const {return end_points.size();} }; @@ -81,13 +81,12 @@ namespace consensus { typedef std::chrono::duration duration_t; // Duration type -} - - template LoggerStream& operator<< (LoggerStream& l, - arangodb::consensus::Config const& c) { - - } + }} +arangodb::LoggerStream& operator<< ( + arangodb::LoggerStream& l, arangodb::consensus::config_t const& c) { + } + #endif // __ARANGODB_CONSENSUS_AGENT__ diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index c5372e1433..859778c0d6 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -25,12 +25,13 @@ using namespace arangodb::velocypack; namespace arangodb { - namespace consensus { +namespace consensus { Agent::Agent () {} Agent::Agent (config_t const& config) : _config(config) { _constituent.configure(this); + _log.configure(this); } Agent::~Agent () {} @@ -52,24 +53,27 @@ Config const& Agent::config () const { } void Agent::print (arangodb::LoggerStream& logger) const { - logger << _config; + //logger << _config; } void Agent::report(status_t status) { _status = status; } +id_t Agent::leaderID () const { + return _constituent.leaderID(); +} + arangodb::LoggerStream& operator<< (arangodb::LoggerStream& l, Agent const& a) { a.print(l); return l; } - template<> Log::ret_t Agent::log (std::shared_ptr const& builder) { if (_constituent.leading()) return _log.log(builder); else - return redirect; + return _constituent.leaderID(); } - }} +}} diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 41c93c65f6..ed5efc7008 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -89,6 +89,11 @@ namespace consensus { * @brief */ void report (status_t); + + /** + * @brief Leader ID + */ + id_t leaderID () const; private: Constituent _constituent; /**< @brief Leader election delegate */ diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index 594d720e09..b0e0f1ae21 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -84,6 +84,10 @@ bool Constituent::running () const { return _role == CANDIDATE; } +id_t Constituent::leaderID () const { + return _leader_id; +} + size_t Constituent::notifyAll () { // Last process notifies everyone std::string body; @@ -199,13 +203,12 @@ void Constituent::run() { while (true) { if (_role == FOLLOWER || _role == APPRENTICE) { _cast = false; // New round set not cast vote - std::this_thread::sleep_for(sleepFor()); + std::this_thread::sleep_for(sleepFor()); // Sleep for random time if (!_cast) - candidate(); + candidate(); // Next round, we are running } else { - callElection(); + callElection(); // Run for office } - } }; diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h index 8766adbcd7..4b4b72ec2a 100644 --- a/arangod/Agency/Constituent.h +++ b/arangod/Agency/Constituent.h @@ -86,6 +86,11 @@ public: */ void run(); + /** + * @brief Who is leading + */ + id_t leaderID () const; + private: /** diff --git a/arangod/Agency/Log.cpp b/arangod/Agency/Log.cpp index 9680bd493e..af15e8d9ea 100644 --- a/arangod/Agency/Log.cpp +++ b/arangod/Agency/Log.cpp @@ -26,12 +26,55 @@ namespace arangodb { namespace consensus { -Log::Log() {} +Log::Log() : Thread("Log") {} Log::~Log() {} -template<> Log::ret_t Log::log ( - std::shared_ptr const& builder) { - return OK; +void Log::configure(Agent* agent) { + _agent = agent; } +void Log::respHandler (index_t idx) { + // Handle responses + +} + + virtual bool Log::operator()(ClusterCommResult*) { + +}; + +template<> Log::id_t Log::log ( + std::shared_ptr const& builder) { + + // Write transaction 1ST! + if (_state.write (builder)) { + + // Tell everyone else + std::string body = builder.toString(); + arangodb::velocypack::Options opts; + std::unique_ptr> headerFields = + std::make_unique >(); + std::vector results(_agent->config().end_points.size()); + std::stringstream path; + path << "/_api/agency/log?id=" << _id << "&term=" << _term; + + for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { // Ask everyone for their vote + if (i != _id) { + results[i] = arangodb::ClusterComm::instance()->asyncRequest("1", 1, + _agent->config().end_points[i], rest::HttpRequest::HTTP_REQUEST_GET, + path.str(), std::make_shared(body), headerFields, + std::make_shared(), + _timeout, true); + LOG(WARN) << _agent->config().end_points[i]; + } + } + + return 0; +} + + +void Log::run() { + +} + + }} diff --git a/arangod/Agency/Log.h b/arangod/Agency/Log.h index 56ec622f4a..c5315455bb 100644 --- a/arangod/Agency/Log.h +++ b/arangod/Agency/Log.h @@ -21,9 +21,20 @@ /// @author Kaveh Vahedipour //////////////////////////////////////////////////////////////////////////////// -#include -#include +#ifndef __ARANGODB_CONSENSUS_LOG__ +#define __ARANGODB_CONSENSUS_LOG__ + + #include "AgencyCommon.h" +#include "State.h" + +#include +#include +#include + +#include +#include + //using namespace arangodb::velocypack; @@ -32,30 +43,50 @@ class Slice {}; namespace arangodb { namespace consensus { +class Agent; + /** - * @brief Log repilca + * @brief Log replica */ -class Log { + class Log : public arangodb::Thread, public arangodb::ClusterCommCallback, + std::enable_shared_from_this { public: typedef uint64_t index_t; - enum ret_t {OK, REDIRECT}; + typedef int32_t ret_t; /** * @brief Default constructor */ - Log (); + Log (); /** * @brief Default Destructor */ virtual ~Log(); + void configure(Agent* agent); + /** * @brief Log */ template ret_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); + private: /** @@ -84,9 +115,15 @@ private: template bool checkTransactionPrecondition ( T const& state, T const& pre); - index_t _commit_id; /**< @brief: index of highest log entry known - to be committed (initialized to 0, increases monotonically) */ - index_t _last_applied; /**< @brief: index of highest log entry applied to state machine */ + index_t _commit_id; /**< @brief index of highest log entry known + to be committed (initialized to 0, increases monotonically) */ + index_t _last_applied; /**< @brief index of highest log entry applied to state machine */ + State _state; /**< @brief State machine */ + + Agent* _agent; /**< @brief My boss */ + }; }} + +#endif diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index ec8fbd150c..cdf28a1e74 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -64,6 +64,7 @@ add_executable( Agency/ApplicationAgency.cpp Agency/Constituent.cpp Agency/Log.cpp + Agency/State.cpp ApplicationServer/ApplicationFeature.cpp ApplicationServer/ApplicationServer.cpp Aql/Aggregator.cpp diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index 4e3439b8aa..ed2046d553 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -91,10 +91,9 @@ HttpHandler::status_t RestAgencyHandler::execute() { } } else if (_request->suffix()[0].compare("log") == 0) { // log replication - _agent->log(_request->toVelocyPack(&opts)); - } /*else if (_request->suffix()[0].compare("configure") == 0) { // cluster conf - _agent->configure(_request->toVelocyPack(&opts)); - } */else { + if (_agent->log(_request->toVelocyPack(&opts))>0); + generateError(HttpResponse::TEMPORARY_REDIRECT,307); + } else { generateError(HttpResponse::METHOD_NOT_ALLOWED,405); return status_t(HANDLER_DONE); } From 3689ffaf6c47efb1a6937610c498525748eb8d29 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 22 Feb 2016 16:53:52 +0100 Subject: [PATCH 17/61] agency needs some reworking --- arangod/Agency/State.cpp | 94 ++++++++++++++++++++++++++++++++++++++++ arangod/Agency/State.h | 70 ++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 arangod/Agency/State.cpp create mode 100644 arangod/Agency/State.h diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp new file mode 100644 index 0000000000..3a70ca74ef --- /dev/null +++ b/arangod/Agency/State.cpp @@ -0,0 +1,94 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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" + +using namespace arangodb::consensus; + +State::State() {} +State::~State() {} + +void State::write (store_t const&) { + + std::string body; + arangodb::velocypack::Options opts; + std::unique_ptr> headerFields = + std::make_unique >(); + std::vector results(_agent->config().end_points.size()); + std::stringstream path; + path << DATABASE_PATH << store_t.toString(); + + ClusterCommResult result = arangodb::ClusterComm::instance()->asyncRequest("1", 1, + _agent->config().end_points[i], rest::HttpRequest::HTTP_REQUEST_GET, + path.str(), std::make_shared(body), headerFields, nullptr, + 1.0, true); + + if (res.status == CL_COMM_SENT) { // Request successfully sent + res = arangodb::ClusterComm::instance()->wait("1", 1, results[i].operationID, "1"); + std::shared_ptr< arangodb::velocypack::Builder > body = res.result->getBodyVelocyPack(); + if (body->isEmpty()) { + continue; + } else { + if (!body->slice().hasKey("vote")) { // Answer has no vote. What happened? + _votes[i] = false; + continue; + } else { + _votes[i] = (body->slice().get("vote").isEqualString("TRUE")); // Record vote + } + } + } + +}; + +store_t State::get (std::string const&) const { + + std::string body; + arangodb::velocypack::Options opts; + std::unique_ptr> headerFields = + std::make_unique >(); + std::vector results(_agent->config().end_points.size()); + std::stringstream path; + path << DATABASE_PATH << store_t.toString(); + + ClusterCommResult result = arangodb::ClusterComm::instance()->asyncRequest("1", 1, + _agent->config().end_points[i], rest::HttpRequest::HTTP_REQUEST_GET, + path.str(), std::make_shared(body), headerFields, nullptr, + 1.0, true); + + if (res.status == CL_COMM_SENT) { // Request successfully sent + res = arangodb::ClusterComm::instance()->wait("1", 1, results[i].operationID, "1"); + std::shared_ptr< arangodb::velocypack::Builder > body = res.result->getBodyVelocyPack(); + if (body->isEmpty()) { + continue; + } else { + if (!body->slice().hasKey("vote")) { // Answer has no vote. What happened? + _votes[i] = false; + continue; + } else { + _votes[i] = (body->slice().get("vote").isEqualString("TRUE")); // Record vote + } + } + } +}; + + diff --git a/arangod/Agency/State.h b/arangod/Agency/State.h new file mode 100644 index 0000000000..c3f33fbf70 --- /dev/null +++ b/arangod/Agency/State.h @@ -0,0 +1,70 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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_STATE_H +#define ARANGOD_AGENCY_STATE_H 1 + +#include +#include +#include "AgencyCommon.h" + +namespace arangodb { + namespace consensus { + + typedef std::shared_ptr store_t; + typedef std::pair entry_t; + typedef std::map container_t; + +class State { + +public: + + /** + *@ brief Default ctor + */ + State(); + + /** + *@ brief Default dtor + */ + ~State(); + + /** + * @brief Append entry + */ + bool write (store_t const&); + + /** + * @brief Get entry + */ + store_t get (std::string const&) const; + + +private: + container_t _container; + +}; + +}} + +#endif From 1de36c5ba70e1e1911718f0246bf2c3fdf8c771c Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Tue, 23 Feb 2016 16:54:00 +0100 Subject: [PATCH 18/61] agency on --- arangod/Agency/AgencyCommon.h | 28 +- arangod/Agency/Agent.cpp | 16 +- arangod/Agency/Agent.h | 12 +- arangod/Agency/ApplicationAgency.cpp | 16 +- arangod/Agency/ApplicationAgency.h | 28 +- arangod/Agency/Log.cpp | 14 +- arangod/Agency/Log.h | 4 +- arangod/Agency/State.cpp | 10 +- arangod/CMakeLists.txt | 1 + arangod/Makefile.files | 303 ------------------ arangod/RestHandler/RestAgencyHandler.cpp | 83 ++--- arangod/RestHandler/RestAgencyHandler.h | 6 +- .../RestHandler/RestVocbaseBaseHandler.cpp | 8 +- arangod/RestHandler/RestVocbaseBaseHandler.h | 8 +- arangod/RestServer/ArangoServer.cpp | 10 +- 15 files changed, 155 insertions(+), 392 deletions(-) delete mode 100644 arangod/Makefile.files diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 88a6110538..f1bb204a54 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -48,6 +48,8 @@ namespace consensus { APPRENTICE = -1, FOLLOWER, CANDIDATE, LEADER }; + typedef std::shared_ptr agency_io_t; + /** * @brief Agent configuration */ @@ -55,11 +57,13 @@ namespace consensus { T min_ping; T max_ping; T election_timeout; + T append_entries_retry_interval; id_t id; std::vector end_points; Config () : min_ping(.15), max_ping(.3) {}; - Config (uint32_t i, T min_p, T max_p, std::vector& end_p) : - id(i), min_ping(min_p), max_ping(max_p), end_points(end_p) {} + Config (uint32_t i, T min_p, T max_p, T appent_i, + std::vector& end_p) : id(i), min_ping(min_p), max_ping(max_p), + append_entries_retry_interval(appent_i), end_points(end_p) {} /* void print (arangodb::LoggerStream& l) const { l << "Config: " << "min_ping(" << min_ping << ")" @@ -70,20 +74,28 @@ namespace consensus { inline size_t size() const {return end_points.size();} }; - using config_t = Config; // Configuration type + using config_t = Config; // Configuration type - struct constituent_t { // Constituent type + struct constituent_t { // Constituent type id_t id; std::string endpoint; }; - typedef std::vector constituency_t; // Constituency type - typedef uint32_t state_t; // State type - typedef std::chrono::duration duration_t; // Duration type + typedef std::vector constituency_t; // Constituency type + typedef uint32_t state_t; // State type + typedef std::chrono::duration duration_t; // Duration type + + struct query_ret_t { + bool accepted; + id_t redirect; + agency_io_t result; + QueryResult () (bool a, redirect_to id, agency_io_t result) : accepted(a), + redirect_to(id), result(res) {} + }; }} -arangodb::LoggerStream& operator<< ( +inline arangodb::LoggerStream& operator<< ( arangodb::LoggerStream& l, arangodb::consensus::config_t const& c) { } diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 859778c0d6..320cd5e4a5 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -69,11 +69,17 @@ arangodb::LoggerStream& operator<< (arangodb::LoggerStream& l, Agent const& a) { return l; } -template<> Log::ret_t Agent::log (std::shared_ptr const& builder) { - if (_constituent.leading()) - return _log.log(builder); - else - return _constituent.leaderID(); +write_ret_t Agent::write (std::shared_ptr builder) const { + if (_constituent.leader()) { // We are leading + return _log.write (builder); + } else { // We are not + return write_ret_t(false,_constituent.leaderID()); + } +} + +std::shared_ptr builder + Agent::read (std::shared_ptr builder) const { + return builder; } }} diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index ed5efc7008..0cfe3c49b7 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -94,7 +94,17 @@ namespace consensus { * @brief Leader ID */ id_t leaderID () const; - + + /** + * @brief Attempt write + */ + query_ret_t write (agency_io_t) const; + + /** + * @brief Read from agency + */ + query_ret_t read (std::shared_ptr) const; + private: Constituent _constituent; /**< @brief Leader election delegate */ Log _log; /**< @brief Log replica */ diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index 3feebe060e..937dfc6298 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -36,7 +36,8 @@ using namespace arangodb::rest; ApplicationAgency::ApplicationAgency() : ApplicationFeature("agency"), _size(5), _min_election_timeout(.5), - _max_election_timeout(1.), _election_call_rate_mul(.75) { + _max_election_timeout(1.), _election_call_rate_mul(.75), + _append_entries_retry_interval(1.0) { } @@ -59,7 +60,10 @@ void ApplicationAgency::setupOptions( ("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"); + "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"); } @@ -85,9 +89,9 @@ bool ApplicationAgency::prepare() { LOG(WARN) << "agency.election-timeout-max should probably be chosen longer!"; } - _agent = std::unique_ptr(new consensus::Agent( - consensus::Config(_agent_id, _min_election_timeout, - _max_election_timeout, _agency_endpoints))); + _agent = std::unique_ptr(new agent_t(config_t + (_agent_id, _min_election_timeout, _max_election_timeout, + _append_entries_retry_interval, _agency_endpoints))); return true; @@ -119,7 +123,7 @@ void ApplicationAgency::stop() { } } -arangodb::consensus::Agent* ApplicationAgency::agent () const { +agent_t* ApplicationAgency::agent () const { return _agent.get(); } diff --git a/arangod/Agency/ApplicationAgency.h b/arangod/Agency/ApplicationAgency.h index fc0c57584a..fba8706d45 100644 --- a/arangod/Agency/ApplicationAgency.h +++ b/arangod/Agency/ApplicationAgency.h @@ -38,8 +38,11 @@ class Task; //////////////////////////////////////////////////////////////////////////////// /// @brief application server with agency //////////////////////////////////////////////////////////////////////////////// +using agent_t = consensus::Agent; +using config_t = consensus::Config; - class ApplicationAgency : virtual public arangodb::rest::ApplicationFeature { + +class ApplicationAgency : virtual public arangodb::rest::ApplicationFeature { private: ApplicationAgency(ApplicationAgency const&); ApplicationAgency& operator=(ApplicationAgency const&); @@ -88,34 +91,27 @@ class Task; public: - void setupOptions(std::map&); - + void setupOptions(std::map&); bool prepare(); - - bool start(); - - bool open(); - - void close(); - - void stop(); - consensus::Agent* agent() const; - + 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: */ - std::vector _agency_endpoints; - std::unique_ptr _agent; + double _election_call_rate_mul; /**< @brief: */ + double _append_entries_retry_interval; + /**< @brief interval between retry to slaves*/ + std::vector _agency_endpoints; /**< @brief agency adresses */ + std::unique_ptr _agent; uint32_t _agent_id; }; diff --git a/arangod/Agency/Log.cpp b/arangod/Agency/Log.cpp index af15e8d9ea..b95d8c3351 100644 --- a/arangod/Agency/Log.cpp +++ b/arangod/Agency/Log.cpp @@ -23,6 +23,9 @@ #include "Log.h" +#include +#include + namespace arangodb { namespace consensus { @@ -38,16 +41,16 @@ void Log::respHandler (index_t idx) { } - virtual bool Log::operator()(ClusterCommResult*) { +bool Log::operator()(ClusterCommResult*) { }; -template<> Log::id_t Log::log ( +template<> id_t Log::log ( std::shared_ptr const& builder) { // Write transaction 1ST! if (_state.write (builder)) { - +/* // Tell everyone else std::string body = builder.toString(); arangodb::velocypack::Options opts; @@ -66,6 +69,7 @@ template<> Log::id_t Log::log ( _timeout, true); LOG(WARN) << _agent->config().end_points[i]; } +*/ } return 0; @@ -73,7 +77,9 @@ template<> Log::id_t Log::log ( void Log::run() { - + while (true) { + std::this_thread::sleep_for(duration_t(1.0)); + } } diff --git a/arangod/Agency/Log.h b/arangod/Agency/Log.h index c5315455bb..fb3027ff6f 100644 --- a/arangod/Agency/Log.h +++ b/arangod/Agency/Log.h @@ -58,7 +58,7 @@ public: /** * @brief Default constructor */ - Log (); + Log (); /** * @brief Default Destructor @@ -70,7 +70,7 @@ public: /** * @brief Log */ - template ret_t log (T const&); + template id_t log (T const&); /** * @brief Call back for log results from slaves diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index 3a70ca74ef..5b515a64d0 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -22,14 +22,16 @@ //////////////////////////////////////////////////////////////////////////////// #include "State.h" +#include "Cluster/ClusterComm.h" using namespace arangodb::consensus; State::State() {} State::~State() {} -void State::write (store_t const&) { +bool State::write (store_t const&) { + /* std::string body; arangodb::velocypack::Options opts; std::unique_ptr> headerFields = @@ -56,12 +58,13 @@ void State::write (store_t const&) { _votes[i] = (body->slice().get("vote").isEqualString("TRUE")); // Record vote } } - } + }*/ + return OK; }; store_t State::get (std::string const&) const { - +/* std::string body; arangodb::velocypack::Options opts; std::unique_ptr> headerFields = @@ -89,6 +92,7 @@ store_t State::get (std::string const&) const { } } } +*/ }; diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index cdf28a1e74..4fe596f8a2 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -182,6 +182,7 @@ add_executable( Rest/AnyServer.cpp RestHandler/RestAdminLogHandler.cpp RestHandler/RestAgencyHandler.cpp + RestHandler/RestAgencyPrivHandler.cpp RestHandler/RestBaseHandler.cpp RestHandler/RestBatchHandler.cpp RestHandler/RestDebugHandler.cpp diff --git a/arangod/Makefile.files b/arangod/Makefile.files deleted file mode 100644 index ce9e1554fe..0000000000 --- a/arangod/Makefile.files +++ /dev/null @@ -1,303 +0,0 @@ -# -*- mode: Makefile; -*- - -## ----------------------------------------------------------------------------- -## --SECTION-- LIBRARY -## ----------------------------------------------------------------------------- - -################################################################################ -### @brief library "libarangod.a" -################################################################################ - -arangod_libarangod_a_CPPFLAGS = \ - -I@top_srcdir@/arangod \ - $(AM_CPPFLAGS) - -arangod_libarangod_a_SOURCES = \ - arangod/Actions/actions.cpp \ - arangod/Actions/RestActionHandler.cpp \ - arangod/Agency/ApplicationAgency.cpp \ - arangod/Agency/Agent.cpp \ - arangod/Agency/Constituent.cpp \ - arangod/Agency/Log.cpp \ - arangod/ApplicationServer/ApplicationFeature.cpp \ - arangod/ApplicationServer/ApplicationServer.cpp \ - arangod/Aql/Aggregator.cpp \ - arangod/Aql/AqlItemBlock.cpp \ - arangod/Aql/AqlItemBlockManager.cpp \ - arangod/Aql/AqlValue.cpp \ - arangod/Aql/Ast.cpp \ - arangod/Aql/AstNode.cpp \ - arangod/Aql/AttributeAccessor.cpp \ - arangod/Aql/BasicBlocks.cpp \ - arangod/Aql/BindParameters.cpp \ - arangod/Aql/CalculationBlock.cpp \ - arangod/Aql/ClusterBlocks.cpp \ - arangod/Aql/ClusterNodes.cpp \ - arangod/Aql/Collection.cpp \ - arangod/Aql/Collections.cpp \ - arangod/Aql/CollectionScanner.cpp \ - arangod/Aql/CollectBlock.cpp \ - arangod/Aql/CollectNode.cpp \ - arangod/Aql/CollectOptions.cpp \ - arangod/Aql/Condition.cpp \ - arangod/Aql/ConditionFinder.cpp \ - arangod/Aql/EnumerateCollectionBlock.cpp \ - arangod/Aql/EnumerateListBlock.cpp \ - arangod/Aql/ExecutionBlock.cpp \ - arangod/Aql/ExecutionEngine.cpp \ - arangod/Aql/ExecutionNode.cpp \ - arangod/Aql/ExecutionPlan.cpp \ - arangod/Aql/ExecutionStats.cpp \ - arangod/Aql/Executor.cpp \ - arangod/Aql/Expression.cpp \ - arangod/Aql/Function.cpp \ - arangod/Aql/Functions.cpp \ - arangod/Aql/grammar.cpp \ - arangod/Aql/Graphs.cpp \ - arangod/Aql/Index.cpp \ - arangod/Aql/IndexBlock.cpp \ - arangod/Aql/IndexNode.cpp \ - arangod/Aql/ModificationBlocks.cpp \ - arangod/Aql/ModificationNodes.cpp \ - arangod/Aql/ModificationOptions.cpp \ - arangod/Aql/NodeFinder.cpp \ - arangod/Aql/Optimizer.cpp \ - arangod/Aql/OptimizerRules.cpp \ - arangod/Aql/Parser.cpp \ - arangod/Aql/Quantifier.cpp \ - arangod/Aql/Query.cpp \ - arangod/Aql/QueryCache.cpp \ - arangod/Aql/QueryList.cpp \ - arangod/Aql/QueryRegistry.cpp \ - arangod/Aql/Range.cpp \ - arangod/Aql/RestAqlHandler.cpp \ - arangod/Aql/Scopes.cpp \ - arangod/Aql/ShortStringStorage.cpp \ - arangod/Aql/SortBlock.cpp \ - arangod/Aql/SortCondition.cpp \ - arangod/Aql/SortNode.cpp \ - arangod/Aql/SubqueryBlock.cpp \ - arangod/Aql/tokens.cpp \ - arangod/Aql/TraversalConditionFinder.cpp \ - arangod/Aql/TraversalBlock.cpp \ - arangod/Aql/TraversalNode.cpp \ - arangod/Aql/V8Expression.cpp \ - arangod/Aql/Variable.cpp \ - arangod/Aql/VariableGenerator.cpp \ - arangod/Cluster/AgencyComm.cpp \ - arangod/Cluster/ApplicationCluster.cpp \ - arangod/Cluster/ClusterComm.cpp \ - arangod/Cluster/ClusterInfo.cpp \ - arangod/Cluster/ClusterTraverser.cpp \ - arangod/Cluster/HeartbeatThread.cpp \ - arangod/Cluster/RestShardHandler.cpp \ - arangod/Cluster/ServerJob.cpp \ - arangod/Cluster/ServerState.cpp \ - arangod/Cluster/v8-cluster.cpp \ - arangod/Cluster/ClusterMethods.cpp \ - arangod/Dispatcher/ApplicationDispatcher.cpp \ - arangod/Dispatcher/Dispatcher.cpp \ - arangod/Dispatcher/DispatcherQueue.cpp \ - arangod/Dispatcher/DispatcherThread.cpp \ - arangod/Dispatcher/Job.cpp \ - arangod/FulltextIndex/fulltext-handles.cpp \ - arangod/FulltextIndex/fulltext-index.cpp \ - arangod/FulltextIndex/fulltext-list.cpp \ - arangod/FulltextIndex/fulltext-query.cpp \ - arangod/FulltextIndex/fulltext-result.cpp \ - arangod/FulltextIndex/fulltext-wordlist.cpp \ - arangod/GeoIndex/GeoIndex.cpp \ - arangod/HttpServer/ApplicationEndpointServer.cpp \ - arangod/HttpServer/AsyncJobManager.cpp \ - arangod/HttpServer/HttpCommTask.cpp \ - arangod/HttpServer/HttpHandler.cpp \ - arangod/HttpServer/HttpHandlerFactory.cpp \ - arangod/HttpServer/HttpListenTask.cpp \ - arangod/HttpServer/HttpServer.cpp \ - arangod/HttpServer/HttpServerJob.cpp \ - arangod/HttpServer/HttpsCommTask.cpp \ - arangod/HttpServer/HttpsServer.cpp \ - arangod/HttpServer/PathHandler.cpp \ - arangod/Indexes/CapConstraint.cpp \ - arangod/Indexes/EdgeIndex.cpp \ - arangod/Indexes/FulltextIndex.cpp \ - arangod/Indexes/GeoIndex2.cpp \ - arangod/Indexes/HashIndex.cpp \ - arangod/Indexes/Index.cpp \ - arangod/Indexes/IndexIterator.cpp \ - arangod/Indexes/PathBasedIndex.cpp \ - arangod/Indexes/PrimaryIndex.cpp \ - arangod/Indexes/SimpleAttributeEqualityMatcher.cpp \ - arangod/Indexes/SkiplistIndex.cpp \ - arangod/IndexOperators/index-operator.cpp \ - arangod/Replication/ContinuousSyncer.cpp \ - arangod/Replication/InitialSyncer.cpp \ - arangod/Replication/Syncer.cpp \ - arangod/Rest/AnyServer.cpp \ - arangod/RestHandler/RestAdminLogHandler.cpp \ - arangod/RestHandler/RestAgencyHandler.cpp \ - arangod/RestHandler/RestBaseHandler.cpp \ - arangod/RestHandler/RestBatchHandler.cpp \ - arangod/RestHandler/RestCursorHandler.cpp \ - arangod/RestHandler/RestDebugHandler.cpp \ - arangod/RestHandler/RestDocumentHandler.cpp \ - arangod/RestHandler/RestEdgeHandler.cpp \ - arangod/RestHandler/RestEdgesHandler.cpp \ - arangod/RestHandler/RestExportHandler.cpp \ - arangod/RestHandler/RestImportHandler.cpp \ - arangod/RestHandler/RestJobHandler.cpp \ - arangod/RestHandler/RestPleaseUpgradeHandler.cpp \ - arangod/RestHandler/RestQueryCacheHandler.cpp \ - arangod/RestHandler/RestQueryHandler.cpp \ - arangod/RestHandler/RestReplicationHandler.cpp \ - arangod/RestHandler/RestShutdownHandler.cpp \ - arangod/RestHandler/RestSimpleHandler.cpp \ - arangod/RestHandler/RestSimpleQueryHandler.cpp \ - arangod/RestHandler/RestUploadHandler.cpp \ - arangod/RestHandler/RestVersionHandler.cpp \ - arangod/RestHandler/RestVocbaseBaseHandler.cpp \ - arangod/RestHandler/WorkMonitorHandler.cpp \ - arangod/RestServer/ArangoServer.cpp \ - arangod/RestServer/ConsoleThread.cpp \ - arangod/RestServer/VocbaseContext.cpp \ - arangod/RestServer/arangod.cpp \ - arangod/Scheduler/ApplicationScheduler.cpp \ - arangod/Scheduler/ListenTask.cpp \ - arangod/Scheduler/PeriodicTask.cpp \ - arangod/Scheduler/Scheduler.cpp \ - arangod/Scheduler/SchedulerLibev.cpp \ - arangod/Scheduler/SchedulerThread.cpp \ - arangod/Scheduler/SignalTask.cpp \ - arangod/Scheduler/SocketTask.cpp \ - arangod/Scheduler/Task.cpp \ - arangod/Scheduler/TaskManager.cpp \ - arangod/Scheduler/TimerTask.cpp \ - arangod/Statistics/statistics.cpp \ - arangod/Storage/Marker.cpp \ - arangod/Storage/Options.cpp \ - arangod/Utils/CollectionExport.cpp \ - arangod/Utils/CollectionKeys.cpp \ - arangod/Utils/CollectionKeysRepository.cpp \ - arangod/Utils/Cursor.cpp \ - arangod/Utils/CursorRepository.cpp \ - arangod/Utils/DocumentHelper.cpp \ - arangod/Utils/ShapedJsonTransformer.cpp \ - arangod/Utils/StandaloneTransactionContext.cpp \ - arangod/Utils/Transaction.cpp \ - arangod/Utils/TransactionContext.cpp \ - arangod/Utils/V8TransactionContext.cpp \ - arangod/Utils/WorkMonitorArangod.cpp \ - arangod/V8Server/ApplicationV8.cpp \ - arangod/V8Server/V8Job.cpp \ - arangod/V8Server/V8PeriodicTask.cpp \ - arangod/V8Server/V8QueueJob.cpp \ - arangod/V8Server/V8TimerTask.cpp \ - arangod/V8Server/V8Traverser.cpp \ - arangod/V8Server/v8-actions.cpp \ - arangod/V8Server/v8-collection.cpp \ - arangod/V8Server/v8-collection-util.cpp \ - arangod/V8Server/v8-dispatcher.cpp \ - arangod/V8Server/v8-query.cpp \ - arangod/V8Server/v8-replication.cpp \ - arangod/V8Server/v8-shape-conv.cpp \ - arangod/V8Server/v8-statistics.cpp \ - arangod/V8Server/v8-vocbase.cpp \ - arangod/V8Server/v8-vocindex.cpp \ - arangod/V8Server/v8-voccursor.cpp \ - arangod/V8Server/v8-user-structures.cpp \ - arangod/V8Server/v8-util.cpp \ - arangod/V8Server/v8-wrapshapedjson.cpp \ - arangod/VocBase/auth.cpp \ - arangod/VocBase/cleanup.cpp \ - arangod/VocBase/collection.cpp \ - arangod/VocBase/compactor.cpp \ - arangod/VocBase/datafile.cpp \ - arangod/VocBase/DatafileStatistics.cpp \ - arangod/VocBase/Ditch.cpp \ - arangod/VocBase/document-collection.cpp \ - arangod/VocBase/DocumentAccessor.cpp \ - arangod/VocBase/ExampleMatcher.cpp \ - arangod/VocBase/edge-collection.cpp \ - arangod/VocBase/Graphs.cpp \ - arangod/VocBase/headers.cpp \ - arangod/VocBase/KeyGenerator.cpp \ - arangod/VocBase/Legends.cpp \ - arangod/VocBase/replication-applier.cpp \ - arangod/VocBase/replication-common.cpp \ - arangod/VocBase/replication-dump.cpp \ - arangod/VocBase/server.cpp \ - arangod/VocBase/shape-accessor.cpp \ - arangod/VocBase/shaped-json.cpp \ - arangod/VocBase/Shaper.cpp \ - arangod/VocBase/transaction.cpp \ - arangod/VocBase/Traverser.cpp \ - arangod/VocBase/vocbase.cpp \ - arangod/VocBase/vocbase-defaults.cpp \ - arangod/VocBase/VocShaper.cpp \ - arangod/Wal/AllocatorThread.cpp \ - arangod/Wal/CollectorThread.cpp \ - arangod/Wal/LogfileManager.cpp \ - arangod/Wal/Logfile.cpp \ - arangod/Wal/Marker.cpp \ - arangod/Wal/RecoverState.cpp \ - arangod/Wal/RemoverThread.cpp \ - arangod/Wal/Slot.cpp \ - arangod/Wal/Slots.cpp \ - arangod/Wal/SynchronizerThread.cpp - -## ----------------------------------------------------------------------------- -## --SECTION-- PROGRAM -## ----------------------------------------------------------------------------- - -################################################################################ -### @brief program "arangod" -################################################################################ - -bin_arangod_CPPFLAGS = \ - -I@top_srcdir@/arangod \ - $(AM_CPPFLAGS) - -bin_arangod_LDADD = \ - arangod/libarangod.a \ - lib/libarango_v8.a \ - lib/libarango.a \ - $(LIBS) \ - @V8_LIBS@ - -bin_arangod_SOURCES = \ - arangod/RestServer/arangod.cpp - -################################################################################ -## --SECTION-- SCANNER & PARSER -################################################################################ - -################################################################################ -### @brief flex -################################################################################ - -FLEXXX_FILES += \ - arangod/Aql/tokens.cpp - -################################################################################ -### @brief bison -################################################################################ - -BISON_FILES += \ - arangod/Aql/grammar.cpp - -if ENABLE_MAINTAINER_MODE - -CLEANUP += \ - arangod/Aql/grammar.h \ - arangod/Aql/grammar.cpp - -endif - -################################################################################ -## --SECTION-- END-OF-FILE -################################################################################ - -## Local Variables: -## mode: outline-minor -## outline-regexp: "^\\(### @brief\\|## --SECTION--\\|# -\\*- \\)" -## End: diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index ed2046d553..6febc4c113 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -52,58 +52,63 @@ RestAgencyHandler::RestAgencyHandler(HttpRequest* request, Agent* agent) bool RestAgencyHandler::isDirect() const { return false; } +inline HttpHandler::status_t RestAgencyHandler::reportErrorEmptyRequest () + const { + 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::unknownMethod () { + 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); +} + HttpHandler::status_t RestAgencyHandler::execute() { + try { - VPackBuilder result; - result.add(VPackValue(VPackValueType::Object)); + std::shared_ptr result; + result->add(VPackValue(VPackValueType::Object)); arangodb::velocypack::Options opts; // Empty request if (_request->suffix().size() == 0) { - LOG(WARN) << "Empty request to agency. Must ask for vote, log or " - "configure"; - generateError(HttpResponse::NOT_FOUND,404); - return status_t(HANDLER_DONE); + return reportErrorEmptyRequest(); } else if (_request->suffix().size() > 1) { // path size >= 2 - LOG(WARN) << "Agency handles a single suffix: vote, log or configure"; - generateError(HttpResponse::NOT_FOUND,404); - return status_t(HANDLER_DONE); + return reportTooManySuffices(); } else { - if (_request->suffix()[0].compare("vote") == 0) { //vote4me - LOG(WARN) << "Vote request"; - bool found; - char const* termStr = _request->value("term", found); - if (!found) { // For now: don't like this - LOG(WARN) << "No term specified"; // should be handled by - generateError(HttpResponse::BAD,400); // Agent rather than Rest - return status_t(HANDLER_DONE); // handler. - } - char const* idStr = _request->value("id", found); - if (!found) { - LOG(WARN) << "No id specified"; - generateError(HttpResponse::BAD,400); - return status_t(HANDLER_DONE); - } - if (_agent->vote(std::stoul(idStr), std::stoul(termStr))) { - result.add("vote", VPackValue("YES")); - } else { - result.add("vote", VPackValue("NO")); - } - - } else if (_request->suffix()[0].compare("log") == 0) { // log replication - if (_agent->log(_request->toVelocyPack(&opts))>0); - generateError(HttpResponse::TEMPORARY_REDIRECT,307); + if (_request->suffix()[0] == "write") { // write to state machine + write_ret_t w = _agent.write(_request->toVelocyPack()); + if (!w.accepted) + redirect(w.redirect_to); + } else (_request->suffix()[0] == "read") { // read from state machine + read_ret_t r = _agent.read(_request->toVelocyPack()); + if (!r.accepted) + redirect(r.redirect_to); + result = r.result; } else { - generateError(HttpResponse::METHOD_NOT_ALLOWED,405); - return status_t(HANDLER_DONE); + return reportUnknownMethod(); } } - result.close(); - VPackSlice s = result.slice(); - generateResult(s); + result->close(); + generateResult(result->slice()); + } catch (...) { // Ignore this error } - return status_t(HANDLER_DONE); + return HttpHandler::status_t(HANDLER_DONE); } diff --git a/arangod/RestHandler/RestAgencyHandler.h b/arangod/RestHandler/RestAgencyHandler.h index 6a4d3cc4a2..668f6b533d 100644 --- a/arangod/RestHandler/RestAgencyHandler.h +++ b/arangod/RestHandler/RestAgencyHandler.h @@ -30,7 +30,7 @@ namespace arangodb { //////////////////////////////////////////////////////////////////////////////// -/// @brief version request handler +/// @brief REST handler for outside calls to agency (write & read) //////////////////////////////////////////////////////////////////////////////// class RestAgencyHandler : public arangodb::RestBaseHandler { @@ -45,6 +45,10 @@ class RestAgencyHandler : public arangodb::RestBaseHandler { private: + status_t reportErrorEmptyRequest() ; + status_t reportTooManySuffices() ; + status_t unknownMethod() ; + consensus::Agent* _agent; }; diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.cpp b/arangod/RestHandler/RestVocbaseBaseHandler.cpp index 660d3f0b18..dd08d3b744 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.cpp +++ b/arangod/RestHandler/RestVocbaseBaseHandler.cpp @@ -42,11 +42,17 @@ using namespace arangodb::basics; using namespace arangodb::rest; //////////////////////////////////////////////////////////////////////////////// -/// @brief batch path +/// @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"; + //////////////////////////////////////////////////////////////////////////////// /// @brief batch path //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.h b/arangod/RestHandler/RestVocbaseBaseHandler.h index 8b9c7ca7ee..5a1e1ad521 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.h +++ b/arangod/RestHandler/RestVocbaseBaseHandler.h @@ -50,11 +50,17 @@ class RestVocbaseBaseHandler : public RestBaseHandler { public: ////////////////////////////////////////////////////////////////////////////// - /// @brief agency path + /// @brief agency public path ////////////////////////////////////////////////////////////////////////////// static std::string const AGENCY_PATH; + ////////////////////////////////////////////////////////////////////////////// + /// @brief agency private path + ////////////////////////////////////////////////////////////////////////////// + + static std::string const AGENCY_PRIV_PATH; + ////////////////////////////////////////////////////////////////////////////// /// @brief batch path ////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index f25f91b5ee..08316c706f 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -58,6 +58,7 @@ #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" @@ -130,8 +131,13 @@ void ArangoServer::defineHandlers(HttpHandlerFactory* factory) { // add "/agency" handler factory->addPrefixHandler(RestVocbaseBaseHandler::AGENCY_PATH, - RestHandlerCreator::createData< - consensus::Agent*>, _applicationAgency->agent()); + RestHandlerCreator::createData, + _applicationAgency->agent()); + + // add "/agency" handler + factory->addPrefixHandler(RestVocbaseBaseHandler::AGENCY_PRIV_PATH, + RestHandlerCreator::createData, + _applicationAgency->agent()); // add "/batch" handler factory->addPrefixHandler(RestVocbaseBaseHandler::BATCH_PATH, From 75f429dd3feb9cb172fa6f768aec7d6b0fad5624 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 24 Feb 2016 18:35:27 +0100 Subject: [PATCH 19/61] agency on --- arangod/RestHandler/RestAgencyPrivHandler.cpp | 104 ++++++++++++++++++ arangod/RestHandler/RestAgencyPrivHandler.h | 58 ++++++++++ 2 files changed, 162 insertions(+) create mode 100644 arangod/RestHandler/RestAgencyPrivHandler.cpp create mode 100644 arangod/RestHandler/RestAgencyPrivHandler.h diff --git a/arangod/RestHandler/RestAgencyPrivHandler.cpp b/arangod/RestHandler/RestAgencyPrivHandler.cpp new file mode 100644 index 0000000000..3f7e93c7c9 --- /dev/null +++ b/arangod/RestHandler/RestAgencyPrivHandler.cpp @@ -0,0 +1,104 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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 "Rest/AnyServer.h" +#include "Rest/HttpRequest.h" +#include "Rest/Version.h" +#include "RestAgencyPrivHandler.h" + +#include "Agency/Agent.h" + +#include +#include + +#include "Basics/Logger.h" + +using namespace arangodb; + +using namespace arangodb::basics; +using namespace arangodb::rest; +using namespace arangodb::consensus; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief ArangoDB server +//////////////////////////////////////////////////////////////////////////////// + +extern AnyServer* 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::unknownMethod () { + LOG(WARN) << "Too many suffixes. Agency public interface takes one path."; + generateError(HttpResponse::NOT_FOUND,404); + 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 + reportErrorEmptyRequest(); + } else if (_request->suffix().size() > 1) { // request too long + reportTooManySuffices(); + } else { + if (_request->suffix()[0] == "appendEntries") { // replication + _agent->appendEntries (_request->headers(), _request->toVelocyPack()); + } else if (_request->suffix()[0] == "requestVote") { // election + _agent->vote (_request->headers(), _request->toVelocyPack()); + } else { + generateError(HttpResponse::NOT_FOUND,404); // nothing + return HttpHandler::status_t(HANDLER_DONE); + } + + } + + result.close(); + VPackSlice s = result.slice(); + generateResult(s); + } catch (...) { + // Ignore this error + } + return HttpHandler::status_t(HANDLER_DONE); +} + + diff --git a/arangod/RestHandler/RestAgencyPrivHandler.h b/arangod/RestHandler/RestAgencyPrivHandler.h new file mode 100644 index 0000000000..17231d91e0 --- /dev/null +++ b/arangod/RestHandler/RestAgencyPrivHandler.h @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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: + + status_t reportErrorEmptyRequest() ; + status_t reportTooManySuffices() ; + status_t unknownMethod() ; + + consensus::Agent* _agent; + +}; +} + +#endif From eda8260f3749a3288f7eb6eb1e54d37d386af1cf Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Thu, 25 Feb 2016 15:06:39 +0100 Subject: [PATCH 20/61] agency on --- arangod/Agency/AgencyCommon.h | 15 ++- arangod/Agency/Agent.cpp | 61 ++++++++---- arangod/Agency/Agent.h | 173 +++++++++++++++++---------------- arangod/Agency/Constituent.cpp | 2 +- arangod/Agency/Constituent.h | 4 +- arangod/Agency/State.cpp | 32 ++---- arangod/Agency/State.h | 52 ++-------- 7 files changed, 162 insertions(+), 177 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index e32310ae52..8ac04e4f1d 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -100,11 +100,24 @@ struct query_ret_t { accepted(a), redirect(id), result(res) {} }; +typedef uint64_t index_t; + +/** + * @brief State entry + */ +struct log_t { + term_t term; + id_t leaderId; + index_t index; + std::string entry; +}; + + }} inline arangodb::LoggerStream& operator<< ( arangodb::LoggerStream& l, arangodb::consensus::config_t const& c) { - + return l; } #endif // __ARANGODB_CONSENSUS_AGENT__ diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index ac1b5e2da6..918e6936cf 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -27,9 +27,9 @@ using namespace arangodb::velocypack; namespace arangodb { namespace consensus { -Agent::Agent () {} +Agent::Agent () : Thread ("Agent"){} -Agent::Agent (config_t const& config) : _config(config) { +Agent::Agent (config_t const& config) : _config(config), Thread ("Agent") { //readPersistence(); // Read persistence (log ) _constituent.configure(this); _state.configure(this); @@ -50,7 +50,7 @@ term_t Agent::term () const { query_t Agent::requestVote(term_t t, id_t id, index_t lastLogIndex, index_t lastLogTerm) { Builder builder; builder.add("term", Value(term())); - builder.add("voteGranted", Value(_constituent.vote(id, t))); + builder.add("voteGranted", Value(_constituent.vote(id, t, lastLogIndex, lastLogTerm))); builder.close(); return std::make_shared(builder); } @@ -79,34 +79,53 @@ arangodb::LoggerStream& operator<< (arangodb::LoggerStream& l, Agent const& a) { query_ret_t Agent::appendEntries ( term_t term, id_t leaderId, index_t prevLogIndex, term_t prevLogTerm, index_t leadersLastCommitIndex, query_t const& query) { - - if (query->isEmpty()) { // heartbeat - return query_ret_t( - true, id(), _constituent.vote(term, leaderID, prevLogIndex, term_t, - leadersLastCommitIndex); - } else if (_constituent.leader()) { // We are leading - _constituent.follow(); - return _state.appendEntries(term, leaderID, prevLogIndex, term_t, - leadersLastCommitIndex); - } else { // We redirect + + if (query->isEmpty()) { // heartbeat received + Builder builder; + builder.add("term", Value(this->term())); // Our own term + builder.add("voteFor", Value( // Our vite + _constituent.vote(term, leaderId, prevLogIndex, leadersLastCommitIndex))); + return query_ret_t(true, id(), std::make_shared(builder)); + } else if (_constituent.leading()) { // We are leading + _constituent.follow(term); + return _state.log(term, leaderId, prevLogIndex, term, leadersLastCommitIndex); + } else { // We redirect return query_ret_t(false,_constituent.leaderID()); } } -query_ret_t Agent::write (query_t const& query) const { - if (_constituent.leader()) { // We are leading - return _state.write (slice); - } else { // We redirect +query_ret_t Agent::write (query_t const& query) { + if (_constituent.leading()) { // We are leading + return _state.write (query); + } else { // We redirect return query_ret_t(false,_constituent.leaderID()); } } -query_ret_t Agent::read (Slice const& slice) const { - if (_constituent.leader()) { // We are leading - return _state.read (slice); - } else { // We redirect +query_ret_t Agent::read (query_t const& query) const { + if (_constituent.leading()) { // We are leading + return _state.read (query); + } else { // We redirect return query_ret_t(false,_constituent.leaderID()); } } +void State::run() { + /* + * - poll through the least of append entries + * - if last round of polling took less than poll interval sleep + * - else just start from top of list and work the + */ + while (true) { + std::this_thread::sleep_for(duration_t(_poll_interval)); + for (auto& i : ) + } +} + +bool State::operator()(ClusterCommResult* ccr) { + +} + + + }} diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 06b4edc7c6..d9b4e29892 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -27,98 +27,107 @@ #include "AgencyCommon.h" #include "Constituent.h" #include "State.h" +#include "Store.h" namespace arangodb { namespace consensus { - class Agent { - - public: +class Agent : public arangodb::Thread { // We need to asynchroneously append entries + +public: + + /** + * @brief Default ctor + */ + Agent (); + + /** + * @brief Construct with program options + */ + Agent (config_t const&); - /** - * @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 + */ + query_t requestVote(term_t , id_t, index_t, index_t); + + /** + * @brief Provide configuration + */ + Config 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; - /** - * @brief Clean up - */ - virtual ~Agent(); - - /** - * @brief Get current term - */ - term_t term() const; - - /** - * @brief Get current term - */ - id_t id() const; - - /** - * @brief Vote request - */ - query_t requestVote(term_t , id_t, index_t, 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 Invoked by leader to replicate log entries (§5.3); + * also used as heartbeat (§5.2). + */ + bool log (std::string const& tolog); - /** - * @brief Provide configuration - */ - Config 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; - - /** - * @brief Attempt write - */ - query_ret_t write (query_t const&); - - /** - * @brief Read from agency - */ - query_ret_t read (query_t const&) const; - - /** - * @brief Invoked by leader to replicate log entries (§5.3); - * also used as heartbeat (§5.2). - */ - query_ret_t appendEntries (term_t, id_t, index_t, term_t, index_t, query_t const&); + /** + * @brief 1. Deal with appendEntries to slaves. 2. Report success of write processes. + */ + void run (); private: - Constituent _constituent; /**< @brief Leader election delegate */ - State _state; /**< @brief Log replica */ - config_t _config; - status_t _status; - - }; + Constituent _constituent; /**< @brief Leader election delegate */ + State _state; /**< @brief Log replica */ + config_t _config; + status_t _status; + store _spear_head; + store _read_db; + +}; + } LoggerStream& operator<< (LoggerStream&, arangodb::consensus::Agent const&); diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index 79fad1781f..7ce14780cc 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -41,7 +41,7 @@ void Constituent::configure(Agent* agent) { } Constituent::Constituent() : Thread("Constituent"), _term(0), _id(0), - _gen(std::random_device()()), _role(APPRENTICE), _agent(0) {} + _gen(std::random_device()()), _role(FOLLOWER), _agent(0) {} Constituent::~Constituent() {} diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h index 78ac32d205..03b3b8f4fa 100644 --- a/arangod/Agency/Constituent.h +++ b/arangod/Agency/Constituent.h @@ -91,13 +91,13 @@ public: */ id_t leaderID () const; -private: - /** * @brief Become follower */ void follow(term_t); +private: + /** * @brief Run for leadership */ diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index 31b209d575..ad791e6af8 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -28,36 +28,18 @@ using namespace arangodb::consensus; -State::State() : Thread("State") {} +State::State() {} State::~State() { - + save(); } -void State::configure(Agent* agent) { - _agent = agent; +void State::log (query_t const& query) { + log.push_back(query); } -void State::respHandler (index_t idx) { - // Handle responses - -} +bool save (std::string const& ep) {}; + +bool load (std::string const& ep) {}; -bool State::operator()(ClusterCommResult* ccr) { - -}; - -query_ret_t State::write (Slice const&) { - -}; - -query_ret_t State::read (Slice const&) const { -}; - -void State::run() { - while (true) { - std::this_thread::sleep_for(duration_t(1.0)); - } -} - diff --git a/arangod/Agency/State.h b/arangod/Agency/State.h index 03da394372..67ba5e8152 100644 --- a/arangod/Agency/State.h +++ b/arangod/Agency/State.h @@ -43,25 +43,13 @@ 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 { +class State : public arangodb::ClusterCommCallback, // We need to provide callBack + std::enable_shared_from_this { // For making shared_ptr from this class public: @@ -75,51 +63,25 @@ public: */ virtual ~State(); - void configure(Agent* agent); - /** * @brief State */ - template id_t log (T const&); + template id_t log (std::string const&); /** - * @brief Call back for log results from slaves + * @brief Save currentTerm, votedFor, log entries */ - virtual bool operator()(ClusterCommResult*); - - /** - * @brief My daily business - */ - void run(); + bool save (std::string const& ep = "tcp://localhost:8529"); /** - * @brief + * @brief Load persisted data from above or start with empty log */ - 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&); + bool load (std::string const& ep = "tcp://localhost:8529"); private: -// State _spear_head; /**< @brief Spear head */ - Agent* _agent; /**< @brief My boss */ log_t _log; /**< @brief State entries */ - term_t _term; - }; From 84e5c306d4bceb9d8ab4b96db553233eae273a82 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Fri, 26 Feb 2016 08:29:01 +0100 Subject: [PATCH 21/61] agency on --- arangod/Agency/AgencyCommon.h | 17 +- arangod/Agency/Agent.cpp | 44 +++- arangod/Agency/Agent.h | 4 +- arangod/Agency/State.cpp | 2 +- arangod/Agency/State.h | 11 +- arangod/CMakeLists.txt | 411 ---------------------------------- 6 files changed, 60 insertions(+), 429 deletions(-) delete mode 100644 arangod/CMakeLists.txt diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 8ac04e4f1d..bcdab4358e 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -102,14 +102,29 @@ struct query_ret_t { typedef uint64_t index_t; +using namespace std::chrono; /** * @brief State entry */ struct log_t { - term_t term; + term_t term; id_t leaderId; index_t index; std::string entry; + std::vector ack; + milliseconds timestamp; + log_t (term_t t, id_t lid, index_t idx, std::string const& e, + std::vector const& r) : + term(t), leaderId(lid), index(idx) entry(e), results(r), timestamp ( + duration_cast(system_clock::now().time_since_epoch()) {} +}; + + +enum AGENCY_EXCEPTION { + QUERY_NOT_APPLICABLE; +} + + }; diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 918e6936cf..c58997131f 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -23,6 +23,8 @@ #include "Agent.h" +#include + using namespace arangodb::velocypack; namespace arangodb { namespace consensus { @@ -84,7 +86,7 @@ query_ret_t Agent::appendEntries ( Builder builder; builder.add("term", Value(this->term())); // Our own term builder.add("voteFor", Value( // Our vite - _constituent.vote(term, leaderId, prevLogIndex, leadersLastCommitIndex))); + _constituent.vote(term, leaderId, prevLogIndex, leadersLastCommitIndex))); return query_ret_t(true, id(), std::make_shared(builder)); } else if (_constituent.leading()) { // We are leading _constituent.follow(term); @@ -94,9 +96,16 @@ query_ret_t Agent::appendEntries ( } } -query_ret_t Agent::write (query_t const& query) { - if (_constituent.leading()) { // We are leading - return _state.write (query); +//query_ret_t +bool Agent::write (query_t const& query) { + if (_constituent.leading()) { // We are leading + if (_spear_head.apply(query)) { // We could apply to spear head? + std::vector indices = // otherwise through + _state.log (term(), id(), query); // Append to my own log + remotelyAppendEntries (indicies); // Schedule appendEntries RPCs. + } else { + throw QUERY_NOT_APPLICABLE; + } } else { // We redirect return query_ret_t(false,_constituent.leaderID()); } @@ -111,15 +120,26 @@ query_ret_t Agent::read (query_t const& query) const { } void State::run() { - /* - * - poll through the least of append entries - * - if last round of polling took less than poll interval sleep - * - else just start from top of list and work the - */ + while (true) { - std::this_thread::sleep_for(duration_t(_poll_interval)); - for (auto& i : ) - } + + auto dur = std::chrono::system_clock::now(); + + std::vector> work(_setup.size()); + + for (auto& i : State.log()) // Collect all unacknowledged + for (size_t j = 0; j < _setup.size(); ++j) + if (!i.ack[j]) + work[j].push_back(i.index); + + for (size_t j = 0; j < _setup.size(); ++j) // (re-)attempt RPCs + appendEntriesRPC (work[j]); + + if (dur = std::chrono::system_clock::now() - dur < _poll_interval) // We were too fast? + std::this_thread::sleep_for (_poll_interval - dur); + + } + } bool State::operator()(ClusterCommResult* ccr) { diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index d9b4e29892..46c904c8bd 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -110,7 +110,7 @@ public: * @brief Invoked by leader to replicate log entries (§5.3); * also used as heartbeat (§5.2). */ - bool log (std::string const& tolog); + query_ret_t appendEntries (term_t, id_t, index_t, term_t, index_t, query_t const&); /** * @brief 1. Deal with appendEntries to slaves. 2. Report success of write processes. @@ -125,6 +125,8 @@ public: store _spear_head; store _read_db; + + arangodb::basics::ConditionVariable cv; }; diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index ad791e6af8..9888f1943a 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -34,7 +34,7 @@ State::~State() { } void State::log (query_t const& query) { - log.push_back(query); + _log.push_back(query); } bool save (std::string const& ep) {}; diff --git a/arangod/Agency/State.h b/arangod/Agency/State.h index 67ba5e8152..74d52e8ebb 100644 --- a/arangod/Agency/State.h +++ b/arangod/Agency/State.h @@ -49,6 +49,7 @@ class Agent; * @brief State replica */ class State : public arangodb::ClusterCommCallback, // We need to provide callBack +xb std::enable_shared_from_this { // For making shared_ptr from this class public: @@ -64,9 +65,13 @@ public: virtual ~State(); /** - * @brief State + * @brief Append log entry + */ + void append (query_t const& query); + + /** + * @brief Update log entry */ - template id_t log (std::string const&); /** * @brief Save currentTerm, votedFor, log entries @@ -81,7 +86,7 @@ public: private: - log_t _log; /**< @brief State entries */ + std::vector _log; /**< @brief State entries */ }; diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt deleted file mode 100644 index 533aac3e9c..0000000000 --- a/arangod/CMakeLists.txt +++ /dev/null @@ -1,411 +0,0 @@ -# -*- mode: CMAKE; -*- - -## ----------------------------------------------------------------------------- -## --SECTION-- COMMON INCLUDES -## ----------------------------------------------------------------------------- - -################################################################################ -### @brief local directory -################################################################################ - -include_directories(.) - -################################################################################ -### @brief library source -################################################################################ - -include_directories(${PROJECT_SOURCE_DIR}/lib) - -## ----------------------------------------------------------------------------- -## --SECTION-- EXECUTABLES -## ----------------------------------------------------------------------------- - -################################################################################ -### @brief output directory -################################################################################ - -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") - -################################################################################ -### @brief arangod -################################################################################ - -include_directories(${PROJECT_SOURCE_DIR}/3rdParty/velocypack/include) -include_directories(${V8_INCLUDE_DIR}) -include_directories(${PROJECT_SOURCE_DIR}/3rdParty/boost/1.58.0) - -if (MSVC) - SET(ARANGO_MSVC - RestServer/WindowsServiceUtils.cpp - ) - ################################################################################ - ### @brief give the binary a version information - ################################################################################ - - generate_product_version( - ProductVersionFiles - NAME arangod - FILE_DESCRIPTION ${ARANGODB_FRIENDLY_STRING} - ICON ${ARANGO_ICON} - VERSION_MAJOR ${CPACK_PACKAGE_VERSION_MAJOR} - VERSION_MINOR ${CPACK_PACKAGE_VERSION_MINOR} - VERSION_PATCH ${CPACK_PACKAGE_VERSION_PATCH} - VERSION_REVISION ${BUILD_ID} - ) -endif () - -# note that we check-in the generated FLEX/BISON files, therefore they are -# generate inside the source tree - -if (USE_MAINTAINER_MODE) - add_custom_command( - OUTPUT - ${CMAKE_CURRENT_SOURCE_DIR}/Aql/tokens.cpp - WORKING_DIRECTORY - ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND - ${CMAKE_SOURCE_DIR}/utils/flex-c++.sh - ${FLEX_EXECUTABLE} - Aql/tokens.cpp - Aql/tokens.ll - MAIN_DEPENDENCY - ${CMAKE_CURRENT_SOURCE_DIR}/Aql/tokens.ll - VERBATIM - ) - - add_custom_command( - OUTPUT - ${CMAKE_CURRENT_SOURCE_DIR}/Aql/grammar.cpp - WORKING_DIRECTORY - ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND - ${CMAKE_SOURCE_DIR}/utils/bison-c.sh - ${BISON_EXECUTABLE} - Aql/grammar.cpp - Aql/grammar.y - MAIN_DEPENDENCY - ${CMAKE_CURRENT_SOURCE_DIR}/Aql/grammar.y - VERBATIM - ) -endif () - -add_executable( - ${BIN_ARANGOD} - ${ARANGO_MSVC} - ${ProductVersionFiles} - Actions/RestActionHandler.cpp - Agency/Agent.cpp - Agency/ApplicationAgency.cpp - Agency/Constituent.cpp - Agency/State.cpp - Actions/actions.cpp - ApplicationServer/ApplicationFeature.cpp - ApplicationServer/ApplicationServer.cpp - Aql/Aggregator.cpp - Aql/AqlItemBlock.cpp - Aql/AqlItemBlockManager.cpp - Aql/AqlValue.cpp - Aql/Ast.cpp - Aql/AstNode.cpp - Aql/AttributeAccessor.cpp - Aql/BasicBlocks.cpp - Aql/BindParameters.cpp - Aql/CalculationBlock.cpp - Aql/ClusterBlocks.cpp - Aql/ClusterNodes.cpp - Aql/CollectBlock.cpp - Aql/CollectNode.cpp - Aql/CollectOptions.cpp - Aql/Collection.cpp - Aql/CollectionScanner.cpp - Aql/Collections.cpp - Aql/Condition.cpp - Aql/ConditionFinder.cpp - Aql/EnumerateCollectionBlock.cpp - Aql/EnumerateListBlock.cpp - Aql/ExecutionBlock.cpp - Aql/ExecutionEngine.cpp - Aql/ExecutionNode.cpp - Aql/ExecutionPlan.cpp - Aql/ExecutionStats.cpp - Aql/Executor.cpp - Aql/Expression.cpp - Aql/Function.cpp - Aql/Functions.cpp - Aql/Graphs.cpp - Aql/Index.cpp - Aql/IndexBlock.cpp - Aql/IndexNode.cpp - Aql/ModificationBlocks.cpp - Aql/ModificationNodes.cpp - Aql/ModificationOptions.cpp - Aql/NodeFinder.cpp - Aql/Optimizer.cpp - Aql/OptimizerRules.cpp - Aql/Parser.cpp - Aql/Quantifier.cpp - Aql/Query.cpp - Aql/QueryCache.cpp - Aql/QueryList.cpp - Aql/QueryRegistry.cpp - Aql/Range.cpp - Aql/RestAqlHandler.cpp - Aql/Scopes.cpp - Aql/ShortStringStorage.cpp - Aql/SortBlock.cpp - Aql/SortCondition.cpp - Aql/SortNode.cpp - Aql/SubqueryBlock.cpp - Aql/TraversalBlock.cpp - Aql/TraversalConditionFinder.cpp - Aql/TraversalNode.cpp - Aql/V8Expression.cpp - Aql/Variable.cpp - Aql/VariableGenerator.cpp - Aql/grammar.cpp - Aql/tokens.cpp - Cluster/AgencyComm.cpp - Cluster/ApplicationCluster.cpp - Cluster/ClusterComm.cpp - Cluster/ClusterInfo.cpp - Cluster/ClusterMethods.cpp - Cluster/ClusterTraverser.cpp - Cluster/HeartbeatThread.cpp - Cluster/RestShardHandler.cpp - Cluster/ServerJob.cpp - Cluster/ServerState.cpp - Cluster/v8-cluster.cpp - Dispatcher/ApplicationDispatcher.cpp - Dispatcher/Dispatcher.cpp - Dispatcher/DispatcherQueue.cpp - Dispatcher/DispatcherThread.cpp - Dispatcher/Job.cpp - FulltextIndex/fulltext-handles.cpp - FulltextIndex/fulltext-index.cpp - FulltextIndex/fulltext-list.cpp - FulltextIndex/fulltext-query.cpp - FulltextIndex/fulltext-result.cpp - FulltextIndex/fulltext-wordlist.cpp - GeoIndex/GeoIndex.cpp - HttpServer/ApplicationEndpointServer.cpp - HttpServer/AsyncJobManager.cpp - HttpServer/HttpCommTask.cpp - HttpServer/HttpHandler.cpp - HttpServer/HttpHandlerFactory.cpp - HttpServer/HttpListenTask.cpp - HttpServer/HttpServer.cpp - HttpServer/HttpServerJob.cpp - HttpServer/HttpsCommTask.cpp - HttpServer/HttpsServer.cpp - HttpServer/PathHandler.cpp - IndexOperators/index-operator.cpp - Indexes/CapConstraint.cpp - Indexes/EdgeIndex.cpp - Indexes/FulltextIndex.cpp - Indexes/GeoIndex2.cpp - Indexes/HashIndex.cpp - Indexes/Index.cpp - Indexes/IndexIterator.cpp - Indexes/PathBasedIndex.cpp - Indexes/PrimaryIndex.cpp - Indexes/SimpleAttributeEqualityMatcher.cpp - Indexes/SkiplistIndex.cpp - Replication/ContinuousSyncer.cpp - Replication/InitialSyncer.cpp - Replication/Syncer.cpp - RestHandler/RestAdminLogHandler.cpp - RestHandler/RestAgencyHandler.cpp - RestHandler/RestAgencyPrivHandler.cpp - RestHandler/RestBaseHandler.cpp - RestHandler/RestBatchHandler.cpp - RestHandler/RestCursorHandler.cpp - RestHandler/RestDebugHandler.cpp - RestHandler/RestDocumentHandler.cpp - RestHandler/RestEdgeHandler.cpp - RestHandler/RestEdgesHandler.cpp - RestHandler/RestExportHandler.cpp - RestHandler/RestImportHandler.cpp - RestHandler/RestJobHandler.cpp - RestHandler/RestPleaseUpgradeHandler.cpp - RestHandler/RestQueryCacheHandler.cpp - RestHandler/RestQueryHandler.cpp - RestHandler/RestReplicationHandler.cpp - RestHandler/RestShutdownHandler.cpp - RestHandler/RestSimpleHandler.cpp - RestHandler/RestSimpleQueryHandler.cpp - RestHandler/RestUploadHandler.cpp - RestHandler/RestVersionHandler.cpp - RestHandler/RestVocbaseBaseHandler.cpp - RestHandler/WorkMonitorHandler.cpp - RestServer/ArangoServer.cpp - RestServer/ConsoleThread.cpp - RestServer/VocbaseContext.cpp - RestServer/arangod.cpp - Scheduler/ApplicationScheduler.cpp - Scheduler/ListenTask.cpp - Scheduler/PeriodicTask.cpp - Scheduler/Scheduler.cpp - Scheduler/SchedulerLibev.cpp - Scheduler/SchedulerThread.cpp - Scheduler/SignalTask.cpp - Scheduler/SocketTask.cpp - Scheduler/Task.cpp - Scheduler/TaskManager.cpp - Scheduler/TimerTask.cpp - Statistics/statistics.cpp - Storage/Marker.cpp - Storage/Options.cpp - Utils/CollectionExport.cpp - Utils/CollectionKeys.cpp - Utils/CollectionKeysRepository.cpp - Utils/Cursor.cpp - Utils/CursorRepository.cpp - Utils/DocumentHelper.cpp - Utils/ShapedJsonTransformer.cpp - Utils/StandaloneTransactionContext.cpp - Utils/Transaction.cpp - Utils/TransactionContext.cpp - Utils/V8TransactionContext.cpp - Utils/WorkMonitorArangod.cpp - V8Server/ApplicationV8.cpp - V8Server/V8Job.cpp - V8Server/V8PeriodicTask.cpp - V8Server/V8QueueJob.cpp - V8Server/V8TimerTask.cpp - V8Server/V8Traverser.cpp - V8Server/v8-actions.cpp - V8Server/v8-collection-util.cpp - V8Server/v8-collection.cpp - V8Server/v8-dispatcher.cpp - V8Server/v8-query.cpp - V8Server/v8-replication.cpp - V8Server/v8-shape-conv.cpp - V8Server/v8-statistics.cpp - V8Server/v8-user-structures.cpp - V8Server/v8-util.cpp - V8Server/v8-vocbase.cpp - V8Server/v8-voccursor.cpp - V8Server/v8-vocindex.cpp - V8Server/v8-wrapshapedjson.cpp - VocBase/DatafileStatistics.cpp - VocBase/Ditch.cpp - VocBase/DocumentAccessor.cpp - VocBase/ExampleMatcher.cpp - VocBase/Graphs.cpp - VocBase/KeyGenerator.cpp - VocBase/Legends.cpp - VocBase/Shaper.cpp - VocBase/Traverser.cpp - VocBase/VocShaper.cpp - VocBase/auth.cpp - VocBase/cleanup.cpp - VocBase/collection.cpp - VocBase/compactor.cpp - VocBase/datafile.cpp - VocBase/document-collection.cpp - VocBase/edge-collection.cpp - VocBase/headers.cpp - VocBase/replication-applier.cpp - VocBase/replication-common.cpp - VocBase/replication-dump.cpp - VocBase/server.cpp - VocBase/shape-accessor.cpp - VocBase/shaped-json.cpp - VocBase/transaction.cpp - VocBase/vocbase-defaults.cpp - VocBase/vocbase.cpp - Wal/AllocatorThread.cpp - Wal/CollectorThread.cpp - Wal/Logfile.cpp - Wal/LogfileManager.cpp - Wal/Marker.cpp - Wal/RecoverState.cpp - Wal/RemoverThread.cpp - Wal/Slot.cpp - Wal/Slots.cpp - Wal/SynchronizerThread.cpp -) - -target_link_libraries( - ${BIN_ARANGOD} - ${LIB_ARANGO_FE} - ${LIB_ARANGO_V8} - ${LIB_ARANGO} - ${LIBEV_LIBS} - ${V8_LIBS} # need this for rest::Version - ${ICU_LIBS} - ${BT_LIBS} - ${ZLIB_LIBS} - ${LINENOISE_LIBS} - ${OPENSSL_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} - ${CMAKE_DL_LIBS} - ${MSVC_LIBS} -) - -################################################################################ -### @brief install arangod binary -################################################################################ - -install( - TARGETS ${BIN_ARANGOD} - RUNTIME DESTINATION ${TRI_SBINDIR_INSTALL}) - -################################################################################ -### @brief install arangod config -################################################################################ - -install_config(arangod) - -################################################################################ -### @brief install arango-dfdb binary -################################################################################ - -install_command_alias(${BIN_ARANGOD} ${TRI_SBINDIR_INSTALL} arango-dfdb) - -################################################################################ -### @brief install arango-dfdb config -################################################################################ - -install_config(arango-dfdb) - -################################################################################ -### @brief install server-side JavaScript files -################################################################################ - -install( - DIRECTORY ${PROJECT_BINARY_DIR}/js - DESTINATION share/arangodb) - -################################################################################ -### @brief install log directory -################################################################################ - -install( - DIRECTORY ${PROJECT_BINARY_DIR}/var/log/arangodb - DESTINATION ${VARDIR_INSTALL}/log) - -################################################################################ -### @brief install database directory -################################################################################ - -install( - DIRECTORY ${PROJECT_BINARY_DIR}/var/lib/arangodb - DESTINATION ${VARDIR_INSTALL}/lib) - -################################################################################ -### @brief install apps directory -################################################################################ - -install( - DIRECTORY ${PROJECT_BINARY_DIR}/var/lib/arangodb-apps - DESTINATION ${VARDIR_INSTALL}/lib) - -## ----------------------------------------------------------------------------- -## --SECTION-- END-OF-FILE -## ----------------------------------------------------------------------------- - -## Local Variables: -## mode: outline-minor -## outline-regexp: "### @brief\\|## --SECTION--\\|# -\\*- " -## End: From 2b1c12c0186ef00fa04cd455f133e1d87d32454c Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Fri, 26 Feb 2016 08:29:42 +0100 Subject: [PATCH 22/61] agency on --- arangod/Agency/Store.h | 2706 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2706 insertions(+) create mode 100644 arangod/Agency/Store.h diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h new file mode 100644 index 0000000000..6307bf22b2 --- /dev/null +++ b/arangod/Agency/Store.h @@ -0,0 +1,2706 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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 +#include +#include +#include +#include +#include +#include +#include + + +/** + * @brief Node + */ +template +class store_node_ { + public: + store_node_(); + store_node_(const T&); + + store_node_ *parent; + store_node_ *first_child, *last_child; + store_node_ *prev_sibling, *next_sibling; + T data; +}; + +template +store_node_::store_node_() + : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0) + { + } + +template +store_node_::store_node_(const T& val) + : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0), data(val) + { + } + +/** + * @brief Store + */ +template > > +class store { + protected: + typedef store_node_ store_node; + public: + + typedef T value_type; + + class iterator_base; + class pre_order_iterator; + class post_order_iterator; + class sibling_iterator; + class leaf_iterator; + + store(); // empty constructor + store(const T&); // constructor setting given element as head + store(const iterator_base&); + store(const store&); // copy constructor + store(store&&); // move constructor + ~store(); + store& operator=(const store&); // copy assignment + store& operator=(store&&); // move assignment + + class iterator_base { + public: + typedef T value_type; + typedef T* pointer; + typedef T& reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + iterator_base(); + iterator_base(store_node *); + + T& operator*() const; + T* operator->() const; + + /// When called, the next increment/decrement skips children of this node. + void skip_children(); + void skip_children(bool skip); + /// Number of children of the node pointed to by the iterator. + unsigned int number_of_children() const; + + sibling_iterator begin() const; + sibling_iterator end() const; + + store_node *node; + protected: + bool skip_current_children_; + }; + + // Depth-first iterator, first accessing the node, then its children. + class pre_order_iterator : public iterator_base { + public: + pre_order_iterator(); + pre_order_iterator(store_node *); + pre_order_iterator(const iterator_base&); + pre_order_iterator(const sibling_iterator&); + + bool operator==(const pre_order_iterator&) const; + bool operator!=(const pre_order_iterator&) const; + pre_order_iterator& operator++(); + pre_order_iterator& operator--(); + pre_order_iterator operator++(int); + pre_order_iterator operator--(int); + pre_order_iterator& operator+=(unsigned int); + pre_order_iterator& operator-=(unsigned int); + + pre_order_iterator& next_skip_children(); + }; + + // Depth-first iterator, first accessing the children, then the node itself. + class post_order_iterator : public iterator_base { + public: + post_order_iterator(); + post_order_iterator(store_node *); + post_order_iterator(const iterator_base&); + post_order_iterator(const sibling_iterator&); + + bool operator==(const post_order_iterator&) const; + bool operator!=(const post_order_iterator&) const; + post_order_iterator& operator++(); + post_order_iterator& operator--(); + post_order_iterator operator++(int); + post_order_iterator operator--(int); + post_order_iterator& operator+=(unsigned int); + post_order_iterator& operator-=(unsigned int); + + // Set iterator to the first child as deep as possible down the store. + void descend_all(); + }; + + // Breadth-first iterator, using a queue + class breadth_first_queued_iterator : public iterator_base { + public: + breadth_first_queued_iterator(); + breadth_first_queued_iterator(store_node *); + breadth_first_queued_iterator(const iterator_base&); + + bool operator==(const breadth_first_queued_iterator&) const; + bool operator!=(const breadth_first_queued_iterator&) const; + breadth_first_queued_iterator& operator++(); + breadth_first_queued_iterator operator++(int); + breadth_first_queued_iterator& operator+=(unsigned int); + + private: + std::queue traversal_queue; + }; + + // The default iterator types throughout the store class. + typedef pre_order_iterator iterator; + typedef breadth_first_queued_iterator breadth_first_iterator; + + // Iterator which traverses only the nodes at a given depth from the root. + class fixed_depth_iterator : public iterator_base { + public: + fixed_depth_iterator(); + fixed_depth_iterator(store_node *); + fixed_depth_iterator(const iterator_base&); + fixed_depth_iterator(const sibling_iterator&); + fixed_depth_iterator(const fixed_depth_iterator&); + + bool operator==(const fixed_depth_iterator&) const; + bool operator!=(const fixed_depth_iterator&) const; + fixed_depth_iterator& operator++(); + fixed_depth_iterator& operator--(); + fixed_depth_iterator operator++(int); + fixed_depth_iterator operator--(int); + fixed_depth_iterator& operator+=(unsigned int); + fixed_depth_iterator& operator-=(unsigned int); + + store_node *top_node; + }; + + // Iterator which traverses only the nodes which are siblings of each other. + class sibling_iterator : public iterator_base { + public: + sibling_iterator(); + sibling_iterator(store_node *); + sibling_iterator(const sibling_iterator&); + sibling_iterator(const iterator_base&); + + bool operator==(const sibling_iterator&) const; + bool operator!=(const sibling_iterator&) const; + sibling_iterator& operator++(); + sibling_iterator& operator--(); + sibling_iterator operator++(int); + sibling_iterator operator--(int); + sibling_iterator& operator+=(unsigned int); + sibling_iterator& operator-=(unsigned int); + + store_node *range_first() const; + store_node *range_last() const; + store_node *parent_; + private: + void set_parent_(); + }; + + // Iterator which traverses only the leaves. + class leaf_iterator : public iterator_base { + public: + leaf_iterator(); + leaf_iterator(store_node *, store_node *top=0); + leaf_iterator(const sibling_iterator&); + leaf_iterator(const iterator_base&); + + bool operator==(const leaf_iterator&) const; + bool operator!=(const leaf_iterator&) const; + leaf_iterator& operator++(); + leaf_iterator& operator--(); + leaf_iterator operator++(int); + leaf_iterator operator--(int); + leaf_iterator& operator+=(unsigned int); + leaf_iterator& operator-=(unsigned int); + private: + store_node *top_node; + }; + + inline pre_order_iterator begin() const; + inline pre_order_iterator end() const; + post_order_iterator begin_post() const; + post_order_iterator end_post() const; + fixed_depth_iterator begin_fixed(const iterator_base&, unsigned int) const; + fixed_depth_iterator end_fixed(const iterator_base&, unsigned int) const; + breadth_first_queued_iterator begin_breadth_first() const; + breadth_first_queued_iterator end_breadth_first() const; + sibling_iterator begin(const iterator_base&) const; + sibling_iterator end(const iterator_base&) const; + leaf_iterator begin_leaf() const; + leaf_iterator end_leaf() const; + leaf_iterator begin_leaf(const iterator_base& top) const; + leaf_iterator end_leaf(const iterator_base& top) const; + + template static iter parent(iter); + template static iter previous_sibling(iter); + template static iter next_sibling(iter); + template iter next_at_same_depth(iter) const; + + void clear(); + template iter erase(iter); + void erase_children(const iterator_base&); + + template iter append_child(iter position); + template iter prepend_child(iter position); + template iter append_child(iter position, const T& x); + template iter prepend_child(iter position, const T& x); + template iter append_child(iter position, iter other_position); + template iter prepend_child(iter position, iter other_position); + template iter append_children(iter position, sibling_iterator from, sibling_iterator to); + template iter prepend_children(iter position, sibling_iterator from, sibling_iterator to); + + pre_order_iterator set_head(const T& x); + template iter insert(iter position, const T& x); + sibling_iterator insert(sibling_iterator position, const T& x); + template iter insert_substore(iter position, const iterator_base& substore); + template iter insert_after(iter position, const T& x); + template iter insert_substore_after(iter position, const iterator_base& substore); + + template iter replace(iter position, const T& x); + template iter replace(iter position, const iterator_base& from); + sibling_iterator replace(sibling_iterator orig_begin, sibling_iterator orig_end, + sibling_iterator new_begin, sibling_iterator new_end); + + template iter flatten(iter position); + template iter reparent(iter position, sibling_iterator begin, sibling_iterator end); + template iter reparent(iter position, iter from); + + template iter wrap(iter position, const T& x); + + template iter move_after(iter target, iter source); + template iter move_before(iter target, iter source); + sibling_iterator move_before(sibling_iterator target, sibling_iterator source); + template iter move_ontop(iter target, iter source); + + store move_out(iterator); + template iter move_in(iter, store&); + template iter move_in_below(iter, store&); + template iter move_in_as_nth_child(iter, size_t, store&); + + void merge(sibling_iterator, sibling_iterator, sibling_iterator, sibling_iterator, + bool duplicate_leaves=false); + void sort(sibling_iterator from, sibling_iterator to, bool deep=false); + template + void sort(sibling_iterator from, sibling_iterator to, StrictWeakOrdering comp, bool deep=false); + template + bool equal(const iter& one, const iter& two, const iter& three) const; + template + bool equal(const iter& one, const iter& two, const iter& three, BinaryPredicate) const; + template + bool equal_substore(const iter& one, const iter& two) const; + template + bool equal_substore(const iter& one, const iter& two, BinaryPredicate) const; + store substore(sibling_iterator from, sibling_iterator to) const; + void substore(store&, sibling_iterator from, sibling_iterator to) const; + void swap(sibling_iterator it); + void swap(iterator, iterator); + + size_t size() const; + size_t size(const iterator_base&) const; + bool empty() const; + static int depth(const iterator_base&); + static int depth(const iterator_base&, const iterator_base&); + int max_depth() const; + int max_depth(const iterator_base&) const; + static unsigned int number_of_children(const iterator_base&); + unsigned int number_of_siblings(const iterator_base&) const; + bool is_in_substore(const iterator_base& position, const iterator_base& begin, + const iterator_base& end) const; + bool is_valid(const iterator_base&) const; + iterator lowest_common_ancestor(const iterator_base&, const iterator_base &) const; + + unsigned int index(sibling_iterator it) const; + static sibling_iterator child(const iterator_base& position, unsigned int); + sibling_iterator sibling(const iterator_base& position, unsigned int); + + void debug_verify_consistency() const; + + class iterator_base_less { + public: + bool operator()(const typename store::iterator_base& one, + const typename store::iterator_base& two) const + { + return one.node < two.node; + } + }; + store_node *head, *feet; + private: + store_node_allocator alloc_; + void head_initialise_(); + void copy_(const store& other); + + template + class compare_nodes { + public: + compare_nodes(StrictWeakOrdering comp) : comp_(comp) {}; + + bool operator()(const store_node *a, const store_node *b) + { + return comp_(a->data, b->data); + } + private: + StrictWeakOrdering comp_; + }; +}; + + +template +store::store() + { + head_initialise_(); + } + +template +store::store(const T& x) + { + head_initialise_(); + set_head(x); + } + +template +store::store(store&& x) + { + head_initialise_(); + head->next_sibling=x.head->next_sibling; + feet->prev_sibling=x.head->prev_sibling; + x.head->next_sibling->prev_sibling=head; + x.feet->prev_sibling->next_sibling=feet; + x.head->next_sibling=x.feet; + x.feet->prev_sibling=x.head; + } + +template +store::store(const iterator_base& other) + { + head_initialise_(); + set_head((*other)); + replace(begin(), other); + } + +template +store::~store() + { + clear(); + alloc_.destroy(head); + alloc_.destroy(feet); + alloc_.deallocate(head,1); + alloc_.deallocate(feet,1); + } + +template +void store::head_initialise_() + { + head = alloc_.allocate(1,0); + feet = alloc_.allocate(1,0); + alloc_.construct(head, store_node_()); + alloc_.construct(feet, store_node_()); + + head->parent=0; + head->first_child=0; + head->last_child=0; + head->prev_sibling=0; + head->next_sibling=feet; + + feet->parent=0; + feet->first_child=0; + feet->last_child=0; + feet->prev_sibling=head; + feet->next_sibling=0; + } + +template +store& store::operator=(const store& other) + { + if(this != &other) + copy_(other); + return *this; + } + +template +store& store::operator=(store&& x) + { + if(this != &x) { + head->next_sibling=x.head->next_sibling; + feet->prev_sibling=x.head->prev_sibling; + x.head->next_sibling->prev_sibling=head; + x.feet->prev_sibling->next_sibling=feet; + x.head->next_sibling=x.feet; + x.feet->prev_sibling=x.head; + } + return *this; + } + +template +store::store(const store& other) + { + head_initialise_(); + copy_(other); + } + +template +void store::copy_(const store& other) + { + clear(); + pre_order_iterator it=other.begin(), to=begin(); + while(it!=other.end()) { + to=insert(to, (*it)); + it.skip_children(); + ++it; + } + to=begin(); + it=other.begin(); + while(it!=other.end()) { + to=replace(to, it); + to.skip_children(); + it.skip_children(); + ++to; + ++it; + } + } + +template +void store::clear() + { + if(head) + while(head->next_sibling!=feet) + erase(pre_order_iterator(head->next_sibling)); + } + +template +void store::erase_children(const iterator_base& it) + { + if(it.node==0) return; + + store_node *cur=it.node->first_child; + store_node *prev=0; + + while(cur!=0) { + prev=cur; + cur=cur->next_sibling; + erase_children(pre_order_iterator(prev)); + alloc_.destroy(prev); + alloc_.deallocate(prev,1); + } + it.node->first_child=0; + it.node->last_child=0; + } + +template +template +iter store::erase(iter it) + { + store_node *cur=it.node; + assert(cur!=head); + iter ret=it; + ret.skip_children(); + ++ret; + erase_children(it); + if(cur->prev_sibling==0) { + cur->parent->first_child=cur->next_sibling; + } + else { + cur->prev_sibling->next_sibling=cur->next_sibling; + } + if(cur->next_sibling==0) { + cur->parent->last_child=cur->prev_sibling; + } + else { + cur->next_sibling->prev_sibling=cur->prev_sibling; + } + + alloc_.destroy(cur); + alloc_.deallocate(cur,1); + return ret; + } + +template +typename store::pre_order_iterator store::begin() const + { + return pre_order_iterator(head->next_sibling); + } + +template +typename store::pre_order_iterator store::end() const + { + return pre_order_iterator(feet); + } + +template +typename store::breadth_first_queued_iterator store::begin_breadth_first() const + { + return breadth_first_queued_iterator(head->next_sibling); + } + +template +typename store::breadth_first_queued_iterator store::end_breadth_first() const + { + return breadth_first_queued_iterator(); + } + +template +typename store::post_order_iterator store::begin_post() const + { + store_node *tmp=head->next_sibling; + if(tmp!=feet) { + while(tmp->first_child) + tmp=tmp->first_child; + } + return post_order_iterator(tmp); + } + +template +typename store::post_order_iterator store::end_post() const + { + return post_order_iterator(feet); + } + +template +typename store::fixed_depth_iterator store::begin_fixed(const iterator_base& pos, unsigned int dp) const + { + typename store::fixed_depth_iterator ret; + ret.top_node=pos.node; + + store_node *tmp=pos.node; + unsigned int curdepth=0; + while(curdepthfirst_child==0) { + if(tmp->next_sibling==0) { + + do { + if(tmp==ret.top_node) + throw std::range_error("store: begin_fixed out of range"); + tmp=tmp->parent; + if(tmp==0) + throw std::range_error("store: begin_fixed out of range"); + --curdepth; + } while(tmp->next_sibling==0); + } + tmp=tmp->next_sibling; + } + tmp=tmp->first_child; + ++curdepth; + } + + ret.node=tmp; + return ret; + } + +template +typename store::fixed_depth_iterator store::end_fixed(const iterator_base& pos, unsigned int dp) const + { + assert(1==0); + store_node *tmp=pos.node; + unsigned int curdepth=1; + while(curdepthfirst_child==0) { + tmp=tmp->next_sibling; + if(tmp==0) + throw std::range_error("store: end_fixed out of range"); + } + tmp=tmp->first_child; + ++curdepth; + } + return tmp; + } + +template +typename store::sibling_iterator store::begin(const iterator_base& pos) const + { + assert(pos.node!=0); + if(pos.node->first_child==0) { + return end(pos); + } + return pos.node->first_child; + } + +template +typename store::sibling_iterator store::end(const iterator_base& pos) const + { + sibling_iterator ret(0); + ret.parent_=pos.node; + return ret; + } + +template +typename store::leaf_iterator store::begin_leaf() const + { + store_node *tmp=head->next_sibling; + if(tmp!=feet) { + while(tmp->first_child) + tmp=tmp->first_child; + } + return leaf_iterator(tmp); + } + +template +typename store::leaf_iterator store::end_leaf() const + { + return leaf_iterator(feet); + } + +template +typename store::leaf_iterator store::begin_leaf(const iterator_base& top) const + { + store_node *tmp=top.node; + while(tmp->first_child) + tmp=tmp->first_child; + return leaf_iterator(tmp, top.node); + } + +template +typename store::leaf_iterator store::end_leaf(const iterator_base& top) const + { + return leaf_iterator(top.node, top.node); + } + +template +template +iter store::parent(iter position) + { + assert(position.node!=0); + return iter(position.node->parent); + } + +template +template +iter store::previous_sibling(iter position) + { + assert(position.node!=0); + iter ret(position); + ret.node=position.node->prev_sibling; + return ret; + } + +template +template +iter store::next_sibling(iter position) + { + assert(position.node!=0); + iter ret(position); + ret.node=position.node->next_sibling; + return ret; + } + +template +template +iter store::next_at_same_depth(iter position) const + { + // We make use of a temporary fixed_depth iterator to implement this. + + typename store::fixed_depth_iterator tmp(position.node); + + ++tmp; + return iter(tmp); + + } + +template +template +iter store::append_child(iter position) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + store_node *tmp=alloc_.allocate(1,0); + alloc_.construct(tmp, store_node_()); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->last_child!=0) { + position.node->last_child->next_sibling=tmp; + } + else { + position.node->first_child=tmp; + } + tmp->prev_sibling=position.node->last_child; + position.node->last_child=tmp; + tmp->next_sibling=0; + return tmp; + } + +template +template +iter store::prepend_child(iter position) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + store_node *tmp=alloc_.allocate(1,0); + alloc_.construct(tmp, store_node_()); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->first_child!=0) { + position.node->first_child->prev_sibling=tmp; + } + else { + position.node->last_child=tmp; + } + tmp->next_sibling=position.node->first_child; + position.node->prev_child=tmp; + tmp->prev_sibling=0; + return tmp; + } + +template +template +iter store::append_child(iter position, const T& x) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + store_node* tmp = alloc_.allocate(1,0); + alloc_.construct(tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->last_child!=0) { + position.node->last_child->next_sibling=tmp; + } + else { + position.node->first_child=tmp; + } + tmp->prev_sibling=position.node->last_child; + position.node->last_child=tmp; + tmp->next_sibling=0; + return tmp; + } + +template +template +iter store::prepend_child(iter position, const T& x) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + store_node* tmp = alloc_.allocate(1,0); + alloc_.construct(tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->first_child!=0) { + position.node->first_child->prev_sibling=tmp; + } + else { + position.node->last_child=tmp; + } + tmp->next_sibling=position.node->first_child; + position.node->first_child=tmp; + tmp->prev_sibling=0; + return tmp; + } + +template +template +iter store::append_child(iter position, iter other) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + sibling_iterator aargh=append_child(position, value_type()); + return replace(aargh, other); + } + +template +template +iter store::prepend_child(iter position, iter other) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + sibling_iterator aargh=prepend_child(position, value_type()); + return replace(aargh, other); + } + +template +template +iter store::append_children(iter position, sibling_iterator from, sibling_iterator to) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + iter ret=from; + + while(from!=to) { + insert_substore(position.end(), from); + ++from; + } + return ret; + } + +template +template +iter store::prepend_children(iter position, sibling_iterator from, sibling_iterator to) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + iter ret=from; + + while(from!=to) { + insert_substore(position.begin(), from); + ++from; + } + return ret; + } + +template +typename store::pre_order_iterator store::set_head(const T& x) + { + assert(head->next_sibling==feet); + return insert(iterator(feet), x); + } + +template +template +iter store::insert(iter position, const T& x) + { + if(position.node==0) { + position.node=feet; + + } + store_node* tmp = alloc_.allocate(1,0); + alloc_.construct(tmp, x); + + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node->parent; + tmp->next_sibling=position.node; + tmp->prev_sibling=position.node->prev_sibling; + position.node->prev_sibling=tmp; + + if(tmp->prev_sibling==0) { + if(tmp->parent) + tmp->parent->first_child=tmp; + } + else + tmp->prev_sibling->next_sibling=tmp; + return tmp; + } + +template +typename store::sibling_iterator store::insert(sibling_iterator position, const T& x) + { + store_node* tmp = alloc_.allocate(1,0); + alloc_.construct(tmp, x); + + tmp->first_child=0; + tmp->last_child=0; + + tmp->next_sibling=position.node; + if(position.node==0) { + tmp->parent=position.parent_; + tmp->prev_sibling=position.range_last(); + tmp->parent->last_child=tmp; + } + else { + tmp->parent=position.node->parent; + tmp->prev_sibling=position.node->prev_sibling; + position.node->prev_sibling=tmp; + } + + if(tmp->prev_sibling==0) { + if(tmp->parent) + tmp->parent->first_child=tmp; + } + else + tmp->prev_sibling->next_sibling=tmp; + return tmp; + } + +template +template +iter store::insert_after(iter position, const T& x) + { + store_node* tmp = alloc_.allocate(1,0); + alloc_.construct(tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node->parent; + tmp->prev_sibling=position.node; + tmp->next_sibling=position.node->next_sibling; + position.node->next_sibling=tmp; + + if(tmp->next_sibling==0) { + if(tmp->parent) + tmp->parent->last_child=tmp; + } + else { + tmp->next_sibling->prev_sibling=tmp; + } + return tmp; + } + +template +template +iter store::insert_substore(iter position, const iterator_base& substore) + { + iter it=insert(position, value_type()); + return replace(it, substore); + } + +template +template +iter store::insert_substore_after(iter position, const iterator_base& substore) + { + iter it=insert_after(position, value_type()); + return replace(it, substore); + } + + +template +template +iter store::replace(iter position, const T& x) + { + position.node->data=x; + return position; + } + +template +template +iter store::replace(iter position, const iterator_base& from) + { + assert(position.node!=head); + store_node *current_from=from.node; + store_node *start_from=from.node; + store_node *current_to =position.node; + + erase_children(position); + store_node* tmp = alloc_.allocate(1,0); + alloc_.construct(tmp, (*from)); + tmp->first_child=0; + tmp->last_child=0; + if(current_to->prev_sibling==0) { + if(current_to->parent!=0) + current_to->parent->first_child=tmp; + } + else { + current_to->prev_sibling->next_sibling=tmp; + } + tmp->prev_sibling=current_to->prev_sibling; + if(current_to->next_sibling==0) { + if(current_to->parent!=0) + current_to->parent->last_child=tmp; + } + else { + current_to->next_sibling->prev_sibling=tmp; + } + tmp->next_sibling=current_to->next_sibling; + tmp->parent=current_to->parent; + alloc_.destroy(current_to); + alloc_.deallocate(current_to,1); + current_to=tmp; + + store_node *last=from.node->next_sibling; + + pre_order_iterator toit=tmp; + do { + assert(current_from!=0); + if(current_from->first_child != 0) { + current_from=current_from->first_child; + toit=append_child(toit, current_from->data); + } + else { + while(current_from->next_sibling==0 && current_from!=start_from) { + current_from=current_from->parent; + toit=parent(toit); + assert(current_from!=0); + } + current_from=current_from->next_sibling; + if(current_from!=last) { + toit=append_child(parent(toit), current_from->data); + } + } + } while(current_from!=last); + + return current_to; + } + +template +typename store::sibling_iterator store::replace( + sibling_iterator orig_begin, + sibling_iterator orig_end, + sibling_iterator new_begin, + sibling_iterator new_end) + { + store_node *orig_first=orig_begin.node; + store_node *new_first=new_begin.node; + store_node *orig_last=orig_first; + while((++orig_begin)!=orig_end) + orig_last=orig_last->next_sibling; + store_node *new_last=new_first; + while((++new_begin)!=new_end) + new_last=new_last->next_sibling; + + bool first=true; + pre_order_iterator ret; + while(1==1) { + pre_order_iterator tt=insert_substore(pre_order_iterator(orig_first), pre_order_iterator(new_first)); + if(first) { + ret=tt; + first=false; + } + if(new_first==new_last) + break; + new_first=new_first->next_sibling; + } + + bool last=false; + store_node *next=orig_first; + while(1==1) { + if(next==orig_last) + last=true; + next=next->next_sibling; + erase((pre_order_iterator)orig_first); + if(last) + break; + orig_first=next; + } + return ret; + } + +template +template +iter store::flatten(iter position) + { + if(position.node->first_child==0) + return position; + + store_node *tmp=position.node->first_child; + while(tmp) { + tmp->parent=position.node->parent; + tmp=tmp->next_sibling; + } + if(position.node->next_sibling) { + position.node->last_child->next_sibling=position.node->next_sibling; + position.node->next_sibling->prev_sibling=position.node->last_child; + } + else { + position.node->parent->last_child=position.node->last_child; + } + position.node->next_sibling=position.node->first_child; + position.node->next_sibling->prev_sibling=position.node; + position.node->first_child=0; + position.node->last_child=0; + + return position; + } + + +template +template +iter store::reparent(iter position, sibling_iterator begin, sibling_iterator end) + { + store_node *first=begin.node; + store_node *last=first; + + assert(first!=position.node); + + if(begin==end) return begin; + while((++begin)!=end) { + last=last->next_sibling; + } + if(first->prev_sibling==0) { + first->parent->first_child=last->next_sibling; + } + else { + first->prev_sibling->next_sibling=last->next_sibling; + } + if(last->next_sibling==0) { + last->parent->last_child=first->prev_sibling; + } + else { + last->next_sibling->prev_sibling=first->prev_sibling; + } + if(position.node->first_child==0) { + position.node->first_child=first; + position.node->last_child=last; + first->prev_sibling=0; + } + else { + position.node->last_child->next_sibling=first; + first->prev_sibling=position.node->last_child; + position.node->last_child=last; + } + last->next_sibling=0; + + store_node *pos=first; + for(;;) { + pos->parent=position.node; + if(pos==last) break; + pos=pos->next_sibling; + } + + return first; + } + +template +template iter store::reparent(iter position, iter from) + { + if(from.node->first_child==0) return position; + return reparent(position, from.node->first_child, end(from)); + } + +template +template iter store::wrap(iter position, const T& x) + { + assert(position.node!=0); + sibling_iterator fr=position, to=position; + ++to; + iter ret = insert(position, x); + reparent(ret, fr, to); + return ret; + } + +template +template iter store::move_after(iter target, iter source) + { + store_node *dst=target.node; + store_node *src=source.node; + assert(dst); + assert(src); + + if(dst==src) return source; + if(dst->next_sibling) + if(dst->next_sibling==src) + return source; + + if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; + else src->parent->first_child=src->next_sibling; + if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; + else src->parent->last_child=src->prev_sibling; + + if(dst->next_sibling!=0) dst->next_sibling->prev_sibling=src; + else dst->parent->last_child=src; + src->next_sibling=dst->next_sibling; + dst->next_sibling=src; + src->prev_sibling=dst; + src->parent=dst->parent; + return src; + } + +template +template iter store::move_before(iter target, iter source) + { + store_node *dst=target.node; + store_node *src=source.node; + assert(dst); + assert(src); + + if(dst==src) return source; + if(dst->prev_sibling) + if(dst->prev_sibling==src) + return source; + + if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; + else src->parent->first_child=src->next_sibling; + if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; + else src->parent->last_child=src->prev_sibling; + + if(dst->prev_sibling!=0) dst->prev_sibling->next_sibling=src; + else dst->parent->first_child=src; + src->prev_sibling=dst->prev_sibling; + dst->prev_sibling=src; + src->next_sibling=dst; + src->parent=dst->parent; + return src; + } + +template +typename store::sibling_iterator store::move_before(sibling_iterator target, + sibling_iterator source) + { + store_node *dst=target.node; + store_node *src=source.node; + store_node *dst_prev_sibling; + if(dst==0) { + dst_prev_sibling=target.parent_->last_child; + assert(dst_prev_sibling); + } + else dst_prev_sibling=dst->prev_sibling; + assert(src); + + if(dst==src) return source; + if(dst_prev_sibling) + if(dst_prev_sibling==src) + return source; + + if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; + else src->parent->first_child=src->next_sibling; + if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; + else src->parent->last_child=src->prev_sibling; + + if(dst_prev_sibling!=0) dst_prev_sibling->next_sibling=src; + else target.parent_->first_child=src; + src->prev_sibling=dst_prev_sibling; + if(dst) { + dst->prev_sibling=src; + src->parent=dst->parent; + } + src->next_sibling=dst; + return src; + } + +template +template iter store::move_ontop(iter target, iter source) + { + store_node *dst=target.node; + store_node *src=source.node; + assert(dst); + assert(src); + + if(dst==src) return source; + + store_node *b_prev_sibling=dst->prev_sibling; + store_node *b_next_sibling=dst->next_sibling; + store_node *b_parent=dst->parent; + + erase(target); + + if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; + else src->parent->first_child=src->next_sibling; + if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; + else src->parent->last_child=src->prev_sibling; + + if(b_prev_sibling!=0) b_prev_sibling->next_sibling=src; + else b_parent->first_child=src; + if(b_next_sibling!=0) b_next_sibling->prev_sibling=src; + else b_parent->last_child=src; + src->prev_sibling=b_prev_sibling; + src->next_sibling=b_next_sibling; + src->parent=b_parent; + return src; + } + + +template +store store::move_out(iterator source) + { + store ret; + + ret.head->next_sibling = source.node; + ret.feet->prev_sibling = source.node; + source.node->parent=0; + + if(source.node->prev_sibling!=0) + source.node->prev_sibling->next_sibling = source.node->next_sibling; + + if(source.node->next_sibling!=0) + source.node->next_sibling->prev_sibling = source.node->prev_sibling; + + source.node->prev_sibling = ret.head; + source.node->next_sibling = ret.feet; + + return ret; + } + +template +template iter store::move_in(iter loc, store& other) + { + if(other.head->next_sibling==other.feet) return loc; + + store_node *other_first_head = other.head->next_sibling; + store_node *other_last_head = other.feet->prev_sibling; + + sibling_iterator prev(loc); + --prev; + + prev.node->next_sibling = other_first_head; + loc.node->prev_sibling = other_last_head; + other_first_head->prev_sibling = prev.node; + other_last_head->next_sibling = loc.node; + + store_node *walk=other_first_head; + while(true) { + walk->parent=loc.node->parent; + if(walk==other_last_head) + break; + walk=walk->next_sibling; + } + + // Close other store. + other.head->next_sibling=other.feet; + other.feet->prev_sibling=other.head; + + return other_first_head; + } + +template +template iter store::move_in_as_nth_child(iter loc, size_t n, store& other) + { + if(other.head->next_sibling==other.feet) return loc; // other store is empty + + store_node *other_first_head = other.head->next_sibling; + store_node *other_last_head = other.feet->prev_sibling; + + if(n==0) { + if(loc.node->first_child==0) { + loc.node->first_child=other_first_head; + loc.node->last_child=other_last_head; + other_last_head->next_sibling=0; + other_first_head->prev_sibling=0; + } + else { + loc.node->first_child->prev_sibling=other_last_head; + other_last_head->next_sibling=loc.node->first_child; + loc.node->first_child=other_first_head; + other_first_head->prev_sibling=0; + } + } + else { + --n; + store_node *walk = loc.node->first_child; + while(true) { + if(walk==0) + throw std::range_error("store: move_in_as_nth_child position " + +std::to_string(n+1) + +" out of range; only " + +std::to_string(number_of_children(loc)) + +" child nodes present"); + if(n==0) + break; + --n; + walk = walk->next_sibling; + } + if(walk->next_sibling==0) + loc.node->last_child=other_last_head; + else + walk->next_sibling->prev_sibling=other_last_head; + other_last_head->next_sibling=walk->next_sibling; + walk->next_sibling=other_first_head; + other_first_head->prev_sibling=walk; + } + + // Adjust parent pointers. + store_node *walk=other_first_head; + while(true) { + walk->parent=loc.node; + if(walk==other_last_head) + break; + walk=walk->next_sibling; + } + + // Close other store. + other.head->next_sibling=other.feet; + other.feet->prev_sibling=other.head; + + return other_first_head; + } + + +template +void store::merge(sibling_iterator to1, sibling_iterator to2, + sibling_iterator from1, sibling_iterator from2, + bool duplicate_leaves) + { + sibling_iterator fnd; + while(from1!=from2) { + if((fnd=std::find(to1, to2, (*from1))) != to2) { // element found + if(from1.begin()==from1.end()) { // full depth reached + if(duplicate_leaves) + append_child(parent(to1), (*from1)); + } + else { // descend further + merge(fnd.begin(), fnd.end(), from1.begin(), from1.end(), duplicate_leaves); + } + } + else { // element missing + insert_substore(to2, from1); + } + ++from1; + } + } + + +template +void store::sort(sibling_iterator from, sibling_iterator to, bool deep) + { + std::less comp; + sort(from, to, comp, deep); + } + +template +template +void store::sort(sibling_iterator from, sibling_iterator to, + StrictWeakOrdering comp, bool deep) + { + if(from==to) return; + + std::multiset > nodes(comp); + sibling_iterator it=from, it2=to; + while(it != to) { + nodes.insert(it.node); + ++it; + } + // reassemble + --it2; + + // prev and next are the nodes before and after the sorted range + store_node *prev=from.node->prev_sibling; + store_node *next=it2.node->next_sibling; + typename std::multiset >::iterator nit=nodes.begin(), eit=nodes.end(); + if(prev==0) { + if((*nit)->parent!=0) // to catch "sorting the head" situations, when there is no parent + (*nit)->parent->first_child=(*nit); + } + else prev->next_sibling=(*nit); + + --eit; + while(nit!=eit) { + (*nit)->prev_sibling=prev; + if(prev) + prev->next_sibling=(*nit); + prev=(*nit); + ++nit; + } + if(prev) + prev->next_sibling=(*eit); + + (*eit)->next_sibling=next; + (*eit)->prev_sibling=prev; // missed in the loop above + if(next==0) { + if((*eit)->parent!=0) + (*eit)->parent->last_child=(*eit); + } + else next->prev_sibling=(*eit); + + if(deep) { + sibling_iterator bcs(*nodes.begin()); + sibling_iterator ecs(*eit); + ++ecs; + while(bcs!=ecs) { + sort(begin(bcs), end(bcs), comp, deep); + ++bcs; + } + } + } + +template +template +bool store::equal(const iter& one_, const iter& two, const iter& three_) const + { + std::equal_to comp; + return equal(one_, two, three_, comp); + } + +template +template +bool store::equal_substore(const iter& one_, const iter& two_) const + { + std::equal_to comp; + return equal_substore(one_, two_, comp); + } + +template +template +bool store::equal(const iter& one_, const iter& two, const iter& three_, BinaryPredicate fun) const + { + pre_order_iterator one(one_), three(three_); + + while(one!=two && is_valid(three)) { + if(!fun(*one,*three)) + return false; + if(one.number_of_children()!=three.number_of_children()) + return false; + ++one; + ++three; + } + return true; + } + +template +template +bool store::equal_substore(const iter& one_, const iter& two_, BinaryPredicate fun) const + { + pre_order_iterator one(one_), two(two_); + + if(!fun(*one,*two)) return false; + if(number_of_children(one)!=number_of_children(two)) return false; + return equal(begin(one),end(one),begin(two),fun); + } + +template +store store::substore(sibling_iterator from, sibling_iterator to) const + { + assert(from!=to); + + store tmp; + tmp.set_head(value_type()); + tmp.replace(tmp.begin(), tmp.end(), from, to); + return tmp; + } + +template +void store::substore(store& tmp, sibling_iterator from, sibling_iterator to) const + { + assert(from!=to); // if from==to, the range is empty, hence no store to return. + + tmp.set_head(value_type()); + tmp.replace(tmp.begin(), tmp.end(), from, to); + } + +template +size_t store::size() const + { + size_t i=0; + pre_order_iterator it=begin(), eit=end(); + while(it!=eit) { + ++i; + ++it; + } + return i; + } + +template +size_t store::size(const iterator_base& top) const + { + size_t i=0; + pre_order_iterator it=top, eit=top; + eit.skip_children(); + ++eit; + while(it!=eit) { + ++i; + ++it; + } + return i; + } + +template +bool store::empty() const + { + pre_order_iterator it=begin(), eit=end(); + return (it==eit); + } + +template +int store::depth(const iterator_base& it) + { + store_node* pos=it.node; + assert(pos!=0); + int ret=0; + while(pos->parent!=0) { + pos=pos->parent; + ++ret; + } + return ret; + } + +template +int store::depth(const iterator_base& it, const iterator_base& root) + { + store_node* pos=it.node; + assert(pos!=0); + int ret=0; + while(pos->parent!=0 && pos!=root.node) { + pos=pos->parent; + ++ret; + } + return ret; + } + +template +int store::max_depth() const + { + int maxd=-1; + for(store_node *it = head->next_sibling; it!=feet; it=it->next_sibling) + maxd=std::max(maxd, max_depth(it)); + + return maxd; + } + + +template +int store::max_depth(const iterator_base& pos) const + { + store_node *tmp=pos.node; + + if(tmp==0 || tmp==head || tmp==feet) return -1; + + int curdepth=0, maxdepth=0; + while(true) { // try to walk the bottom of the store + while(tmp->first_child==0) { + if(tmp==pos.node) return maxdepth; + if(tmp->next_sibling==0) { + // try to walk up and then right again + do { + tmp=tmp->parent; + if(tmp==0) return maxdepth; + --curdepth; + } while(tmp->next_sibling==0); + } + if(tmp==pos.node) return maxdepth; + tmp=tmp->next_sibling; + } + tmp=tmp->first_child; + ++curdepth; + maxdepth=std::max(curdepth, maxdepth); + } + } + +template +unsigned int store::number_of_children(const iterator_base& it) + { + store_node *pos=it.node->first_child; + if(pos==0) return 0; + + unsigned int ret=1; +// while(pos!=it.node->last_child) { +// ++ret; +// pos=pos->next_sibling; +// } + while((pos=pos->next_sibling)) + ++ret; + return ret; + } + +template +unsigned int store::number_of_siblings(const iterator_base& it) const + { + store_node *pos=it.node; + unsigned int ret=0; + // count forward + while(pos->next_sibling && + pos->next_sibling!=head && + pos->next_sibling!=feet) { + ++ret; + pos=pos->next_sibling; + } + // count backward + pos=it.node; + while(pos->prev_sibling && + pos->prev_sibling!=head && + pos->prev_sibling!=feet) { + ++ret; + pos=pos->prev_sibling; + } + + return ret; + } + +template +void store::swap(sibling_iterator it) + { + store_node *nxt=it.node->next_sibling; + if(nxt) { + if(it.node->prev_sibling) + it.node->prev_sibling->next_sibling=nxt; + else + it.node->parent->first_child=nxt; + nxt->prev_sibling=it.node->prev_sibling; + store_node *nxtnxt=nxt->next_sibling; + if(nxtnxt) + nxtnxt->prev_sibling=it.node; + else + it.node->parent->last_child=it.node; + nxt->next_sibling=it.node; + it.node->prev_sibling=nxt; + it.node->next_sibling=nxtnxt; + } + } + +template +void store::swap(iterator one, iterator two) + { + // if one and two are adjacent siblings, use the sibling swap + if(one.node->next_sibling==two.node) swap(one); + else if(two.node->next_sibling==one.node) swap(two); + else { + store_node *nxt1=one.node->next_sibling; + store_node *nxt2=two.node->next_sibling; + store_node *pre1=one.node->prev_sibling; + store_node *pre2=two.node->prev_sibling; + store_node *par1=one.node->parent; + store_node *par2=two.node->parent; + + // reconnect + one.node->parent=par2; + one.node->next_sibling=nxt2; + if(nxt2) nxt2->prev_sibling=one.node; + else par2->last_child=one.node; + one.node->prev_sibling=pre2; + if(pre2) pre2->next_sibling=one.node; + else par2->first_child=one.node; + + two.node->parent=par1; + two.node->next_sibling=nxt1; + if(nxt1) nxt1->prev_sibling=two.node; + else par1->last_child=two.node; + two.node->prev_sibling=pre1; + if(pre1) pre1->next_sibling=two.node; + else par1->first_child=two.node; + } + } + + +template +bool store::is_in_substore(const iterator_base& it, const iterator_base& begin, + const iterator_base& end) const + { + // FIXME: this should be optimised. + pre_order_iterator tmp=begin; + while(tmp!=end) { + if(tmp==it) return true; + ++tmp; + } + return false; + } + +template +bool store::is_valid(const iterator_base& it) const + { + if(it.node==0 || it.node==feet || it.node==head) return false; + else return true; + } + +template +typename store::iterator store::lowest_common_ancestor( + const iterator_base& one, const iterator_base& two) const + { + std::set parents; + + // Walk up from 'one' storing all parents. + iterator walk=one; + do { + walk=parent(walk); + parents.insert(walk); + } while( is_valid(parent(walk)) ); + + // Walk up from 'two' until we encounter a node in parents. + walk=two; + do { + walk=parent(walk); + if(parents.find(walk) != parents.end()) break; + } while( is_valid(parent(walk)) ); + + return walk; + } + +template +unsigned int store::index(sibling_iterator it) const + { + unsigned int ind=0; + if(it.node->parent==0) { + while(it.node->prev_sibling!=head) { + it.node=it.node->prev_sibling; + ++ind; + } + } + else { + while(it.node->prev_sibling!=0) { + it.node=it.node->prev_sibling; + ++ind; + } + } + return ind; + } + +template +typename store::sibling_iterator store::sibling(const iterator_base& it, unsigned int num) + { + store_node *tmp; + if(it.node->parent==0) { + tmp=head->next_sibling; + while(num) { + tmp = tmp->next_sibling; + --num; + } + } + else { + tmp=it.node->parent->first_child; + while(num) { + assert(tmp!=0); + tmp = tmp->next_sibling; + --num; + } + } + return tmp; + } + +template +void store::debug_verify_consistency() const + { + iterator it=begin(); + while(it!=end()) { + if(it.node->parent!=0) { + if(it.node->prev_sibling==0) + assert(it.node->parent->first_child==it.node); + else + assert(it.node->prev_sibling->next_sibling==it.node); + if(it.node->next_sibling==0) + assert(it.node->parent->last_child==it.node); + else + assert(it.node->next_sibling->prev_sibling==it.node); + } + ++it; + } + } + +template +typename store::sibling_iterator store::child(const iterator_base& it, unsigned int num) + { + store_node *tmp=it.node->first_child; + while(num--) { + assert(tmp!=0); + tmp=tmp->next_sibling; + } + return tmp; + } + + + + +// Iterator base + +template +store::iterator_base::iterator_base() + : node(0), skip_current_children_(false) + { + } + +template +store::iterator_base::iterator_base(store_node *tn) + : node(tn), skip_current_children_(false) + { + } + +template +T& store::iterator_base::operator*() const + { + return node->data; + } + +template +T* store::iterator_base::operator->() const + { + return &(node->data); + } + +template +bool store::post_order_iterator::operator!=(const post_order_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool store::post_order_iterator::operator==(const post_order_iterator& other) const + { + if(other.node==this->node) return true; + else return false; + } + +template +bool store::pre_order_iterator::operator!=(const pre_order_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool store::pre_order_iterator::operator==(const pre_order_iterator& other) const + { + if(other.node==this->node) return true; + else return false; + } + +template +bool store::sibling_iterator::operator!=(const sibling_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool store::sibling_iterator::operator==(const sibling_iterator& other) const + { + if(other.node==this->node) return true; + else return false; + } + +template +bool store::leaf_iterator::operator!=(const leaf_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool store::leaf_iterator::operator==(const leaf_iterator& other) const + { + if(other.node==this->node && other.top_node==this->top_node) return true; + else return false; + } + +template +typename store::sibling_iterator store::iterator_base::begin() const + { + if(node->first_child==0) + return end(); + + sibling_iterator ret(node->first_child); + ret.parent_=this->node; + return ret; + } + +template +typename store::sibling_iterator store::iterator_base::end() const + { + sibling_iterator ret(0); + ret.parent_=node; + return ret; + } + +template +void store::iterator_base::skip_children() + { + skip_current_children_=true; + } + +template +void store::iterator_base::skip_children(bool skip) + { + skip_current_children_=skip; + } + +template +unsigned int store::iterator_base::number_of_children() const + { + store_node *pos=node->first_child; + if(pos==0) return 0; + + unsigned int ret=1; + while(pos!=node->last_child) { + ++ret; + pos=pos->next_sibling; + } + return ret; + } + + + +// Pre-order iterator + +template +store::pre_order_iterator::pre_order_iterator() + : iterator_base(0) + { + } + +template +store::pre_order_iterator::pre_order_iterator(store_node *tn) + : iterator_base(tn) + { + } + +template +store::pre_order_iterator::pre_order_iterator(const iterator_base &other) + : iterator_base(other.node) + { + } + +template +store::pre_order_iterator::pre_order_iterator(const sibling_iterator& other) + : iterator_base(other.node) + { + if(this->node==0) { + if(other.range_last()!=0) + this->node=other.range_last(); + else + this->node=other.parent_; + this->skip_children(); + ++(*this); + } + } + +template +typename store::pre_order_iterator& store::pre_order_iterator::operator++() + { + assert(this->node!=0); + if(!this->skip_current_children_ && this->node->first_child != 0) { + this->node=this->node->first_child; + } + else { + this->skip_current_children_=false; + while(this->node->next_sibling==0) { + this->node=this->node->parent; + if(this->node==0) + return *this; + } + this->node=this->node->next_sibling; + } + return *this; + } + +template +typename store::pre_order_iterator& store::pre_order_iterator::operator--() + { + assert(this->node!=0); + if(this->node->prev_sibling) { + this->node=this->node->prev_sibling; + while(this->node->last_child) + this->node=this->node->last_child; + } + else { + this->node=this->node->parent; + if(this->node==0) + return *this; + } + return *this; +} + +template +typename store::pre_order_iterator store::pre_order_iterator::operator++(int) + { + pre_order_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename store::pre_order_iterator& store::pre_order_iterator::next_skip_children() + { + (*this).skip_children(); + (*this)++; + return *this; + } + +template +typename store::pre_order_iterator store::pre_order_iterator::operator--(int) +{ + pre_order_iterator copy = *this; + --(*this); + return copy; +} + +template +typename store::pre_order_iterator& store::pre_order_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + +template +typename store::pre_order_iterator& store::pre_order_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --num; + } + return (*this); + } + + + +// Post-order iterator + +template +store::post_order_iterator::post_order_iterator() + : iterator_base(0) + { + } + +template +store::post_order_iterator::post_order_iterator(store_node *tn) + : iterator_base(tn) + { + } + +template +store::post_order_iterator::post_order_iterator(const iterator_base &other) + : iterator_base(other.node) + { + } + +template +store::post_order_iterator::post_order_iterator(const sibling_iterator& other) + : iterator_base(other.node) + { + if(this->node==0) { + if(other.range_last()!=0) + this->node=other.range_last(); + else + this->node=other.parent_; + this->skip_children(); + ++(*this); + } + } + +template +typename store::post_order_iterator& store::post_order_iterator::operator++() + { + assert(this->node!=0); + if(this->node->next_sibling==0) { + this->node=this->node->parent; + this->skip_current_children_=false; + } + else { + this->node=this->node->next_sibling; + if(this->skip_current_children_) { + this->skip_current_children_=false; + } + else { + while(this->node->first_child) + this->node=this->node->first_child; + } + } + return *this; + } + +template +typename store::post_order_iterator& store::post_order_iterator::operator--() + { + assert(this->node!=0); + if(this->skip_current_children_ || this->node->last_child==0) { + this->skip_current_children_=false; + while(this->node->prev_sibling==0) + this->node=this->node->parent; + this->node=this->node->prev_sibling; + } + else { + this->node=this->node->last_child; + } + return *this; + } + +template +typename store::post_order_iterator store::post_order_iterator::operator++(int) + { + post_order_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename store::post_order_iterator store::post_order_iterator::operator--(int) + { + post_order_iterator copy = *this; + --(*this); + return copy; + } + + +template +typename store::post_order_iterator& store::post_order_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + +template +typename store::post_order_iterator& store::post_order_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --num; + } + return (*this); + } + +template +void store::post_order_iterator::descend_all() + { + assert(this->node!=0); + while(this->node->first_child) + this->node=this->node->first_child; + } + + +// Breadth-first iterator + +template +store::breadth_first_queued_iterator::breadth_first_queued_iterator() + : iterator_base() + { + } + +template +store::breadth_first_queued_iterator::breadth_first_queued_iterator(store_node *tn) + : iterator_base(tn) + { + traversal_queue.push(tn); + } + +template +store::breadth_first_queued_iterator::breadth_first_queued_iterator(const iterator_base& other) + : iterator_base(other.node) + { + traversal_queue.push(other.node); + } + +template +bool store::breadth_first_queued_iterator::operator!=(const breadth_first_queued_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool store::breadth_first_queued_iterator::operator==(const breadth_first_queued_iterator& other) const + { + if(other.node==this->node) return true; + else return false; + } + +template +typename store::breadth_first_queued_iterator& store::breadth_first_queued_iterator::operator++() + { + assert(this->node!=0); + + // Add child nodes and pop current node + sibling_iterator sib=this->begin(); + while(sib!=this->end()) { + traversal_queue.push(sib.node); + ++sib; + } + traversal_queue.pop(); + if(traversal_queue.size()>0) + this->node=traversal_queue.front(); + else + this->node=0; + return (*this); + } + +template +typename store::breadth_first_queued_iterator store::breadth_first_queued_iterator::operator++(int) + { + breadth_first_queued_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename store::breadth_first_queued_iterator& store::breadth_first_queued_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + + + +// Fixed depth iterator + +template +store::fixed_depth_iterator::fixed_depth_iterator() + : iterator_base() + { + } + +template +store::fixed_depth_iterator::fixed_depth_iterator(store_node *tn) + : iterator_base(tn), top_node(0) + { + } + +template +store::fixed_depth_iterator::fixed_depth_iterator(const iterator_base& other) + : iterator_base(other.node), top_node(0) + { + } + +template +store::fixed_depth_iterator::fixed_depth_iterator(const sibling_iterator& other) + : iterator_base(other.node), top_node(0) + { + } + +template +store::fixed_depth_iterator::fixed_depth_iterator(const fixed_depth_iterator& other) + : iterator_base(other.node), top_node(other.top_node) + { + } + +template +bool store::fixed_depth_iterator::operator==(const fixed_depth_iterator& other) const + { + if(other.node==this->node && other.top_node==top_node) return true; + else return false; + } + +template +bool store::fixed_depth_iterator::operator!=(const fixed_depth_iterator& other) const + { + if(other.node!=this->node || other.top_node!=top_node) return true; + else return false; + } + +template +typename store::fixed_depth_iterator& store::fixed_depth_iterator::operator++() + { + assert(this->node!=0); + + if(this->node->next_sibling) { + this->node=this->node->next_sibling; + } + else { + int relative_depth=0; + upper: + do { + if(this->node==this->top_node) { + this->node=0; // FIXME: return a proper fixed_depth end iterator once implemented + return *this; + } + this->node=this->node->parent; + if(this->node==0) return *this; + --relative_depth; + } while(this->node->next_sibling==0); + lower: + this->node=this->node->next_sibling; + while(this->node->first_child==0) { + if(this->node->next_sibling==0) + goto upper; + this->node=this->node->next_sibling; + if(this->node==0) return *this; + } + while(relative_depth<0 && this->node->first_child!=0) { + this->node=this->node->first_child; + ++relative_depth; + } + if(relative_depth<0) { + if(this->node->next_sibling==0) goto upper; + else goto lower; + } + } + return *this; + } + +template +typename store::fixed_depth_iterator& store::fixed_depth_iterator::operator--() + { + assert(this->node!=0); + + if(this->node->prev_sibling) { + this->node=this->node->prev_sibling; + } + else { + int relative_depth=0; + upper: + do { + if(this->node==this->top_node) { + this->node=0; + return *this; + } + this->node=this->node->parent; + if(this->node==0) return *this; + --relative_depth; + } while(this->node->prev_sibling==0); + lower: + this->node=this->node->prev_sibling; + while(this->node->last_child==0) { + if(this->node->prev_sibling==0) + goto upper; + this->node=this->node->prev_sibling; + if(this->node==0) return *this; + } + while(relative_depth<0 && this->node->last_child!=0) { + this->node=this->node->last_child; + ++relative_depth; + } + if(relative_depth<0) { + if(this->node->prev_sibling==0) goto upper; + else goto lower; + } + } + return *this; + } + +template +typename store::fixed_depth_iterator store::fixed_depth_iterator::operator++(int) + { + fixed_depth_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename store::fixed_depth_iterator store::fixed_depth_iterator::operator--(int) + { + fixed_depth_iterator copy = *this; + --(*this); + return copy; + } + +template +typename store::fixed_depth_iterator& store::fixed_depth_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --(num); + } + return (*this); + } + +template +typename store::fixed_depth_iterator& store::fixed_depth_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --(num); + } + return *this; + } + + +// Sibling iterator + +template +store::sibling_iterator::sibling_iterator() + : iterator_base() + { + set_parent_(); + } + +template +store::sibling_iterator::sibling_iterator(store_node *tn) + : iterator_base(tn) + { + set_parent_(); + } + +template +store::sibling_iterator::sibling_iterator(const iterator_base& other) + : iterator_base(other.node) + { + set_parent_(); + } + +template +store::sibling_iterator::sibling_iterator(const sibling_iterator& other) + : iterator_base(other), parent_(other.parent_) + { + } + +template +void store::sibling_iterator::set_parent_() + { + parent_=0; + if(this->node==0) return; + if(this->node->parent!=0) + parent_=this->node->parent; + } + +template +typename store::sibling_iterator& store::sibling_iterator::operator++() + { + if(this->node) + this->node=this->node->next_sibling; + return *this; + } + +template +typename store::sibling_iterator& store::sibling_iterator::operator--() + { + if(this->node) this->node=this->node->prev_sibling; + else { + assert(parent_); + this->node=parent_->last_child; + } + return *this; +} + +template +typename store::sibling_iterator store::sibling_iterator::operator++(int) + { + sibling_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename store::sibling_iterator store::sibling_iterator::operator--(int) + { + sibling_iterator copy = *this; + --(*this); + return copy; + } + +template +typename store::sibling_iterator& store::sibling_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + +template +typename store::sibling_iterator& store::sibling_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --num; + } + return (*this); + } + +template +typename store::store_node *store::sibling_iterator::range_first() const + { + store_node *tmp=parent_->first_child; + return tmp; + } + +template +typename store::store_node *store::sibling_iterator::range_last() const + { + return parent_->last_child; + } + +// Leaf iterator + +template +store::leaf_iterator::leaf_iterator() + : iterator_base(0), top_node(0) + { + } + +template +store::leaf_iterator::leaf_iterator(store_node *tn, store_node *top) + : iterator_base(tn), top_node(top) + { + } + +template +store::leaf_iterator::leaf_iterator(const iterator_base &other) + : iterator_base(other.node), top_node(0) + { + } + +template +store::leaf_iterator::leaf_iterator(const sibling_iterator& other) + : iterator_base(other.node), top_node(0) + { + if(this->node==0) { + if(other.range_last()!=0) + this->node=other.range_last(); + else + this->node=other.parent_; + ++(*this); + } + } + +template +typename store::leaf_iterator& store::leaf_iterator::operator++() + { + assert(this->node!=0); + if(this->node->first_child!=0) { // current node is no longer leaf (children got added) + while(this->node->first_child) + this->node=this->node->first_child; + } + else { + while(this->node->next_sibling==0) { + if (this->node->parent==0) return *this; + this->node=this->node->parent; + if (top_node != 0 && this->node==top_node) return *this; + } + this->node=this->node->next_sibling; + while(this->node->first_child) + this->node=this->node->first_child; + } + return *this; + } + +template +typename store::leaf_iterator& store::leaf_iterator::operator--() + { + assert(this->node!=0); + while (this->node->prev_sibling==0) { + if (this->node->parent==0) return *this; + this->node=this->node->parent; + if (top_node !=0 && this->node==top_node) return *this; + } + this->node=this->node->prev_sibling; + while(this->node->last_child) + this->node=this->node->last_child; + return *this; + } + +template +typename store::leaf_iterator store::leaf_iterator::operator++(int) + { + leaf_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename store::leaf_iterator store::leaf_iterator::operator--(int) + { + leaf_iterator copy = *this; + --(*this); + return copy; + } + + +template +typename store::leaf_iterator& store::leaf_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + +template +typename store::leaf_iterator& store::leaf_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --num; + } + return (*this); + } + +#endif + From e5c79ea73ccbd81218dd0262238c516d0badbaa0 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Sat, 27 Feb 2016 11:44:21 +0100 Subject: [PATCH 23/61] agency on --- arangod/Agency/Agent.h | 6 +++-- arangod/CMakeLists.txt | 2 ++ arangod/RestHandler/RestAgencyHandler.cpp | 23 +++++++++---------- arangod/RestHandler/RestAgencyHandler.h | 2 ++ arangod/RestHandler/RestAgencyPrivHandler.cpp | 4 ++-- arangod/RestServer/ArangoServer.cpp | 3 --- 6 files changed, 21 insertions(+), 19 deletions(-) diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 67c07c347f..14ba6bc47b 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -110,10 +110,12 @@ public: * @brief Invoked by leader to replicate log entries (§5.3); * also used as heartbeat (§5.2). */ - query_ret_t appendEntries (term_t, id_t, index_t, term_t, index_t, query_t const&); + append_entries_t append_entries_t (term_t, id_t, index_t, term_t, index_t, + query_t const&); /** - * @brief 1. Deal with appendEntries to slaves. 2. Report success of write processes. + * @brief 1. Deal with appendEntries to slaves. + * 2. Report success of write processes. */ void run (); diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index fc30c03535..a95cd80c98 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -183,6 +183,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 diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index 798f28bcef..786c53948c 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -21,7 +21,7 @@ /// @author Kaveh Vahedipour //////////////////////////////////////////////////////////////////////////////// -#include "Rest/AnyServer.h" +#include "RestServer/ArangoServer.h" #include "Rest/HttpRequest.h" #include "Rest/Version.h" #include "RestAgencyHandler.h" @@ -43,8 +43,7 @@ using namespace arangodb::consensus; /// @brief ArangoDB server //////////////////////////////////////////////////////////////////////////////// -extern AnyServer* ArangoInstance; - +extern ArangoServer* ArangoInstance; RestAgencyHandler::RestAgencyHandler(HttpRequest* request, Agent* agent) : RestBaseHandler(request), _agent(agent) { @@ -52,8 +51,7 @@ RestAgencyHandler::RestAgencyHandler(HttpRequest* request, Agent* agent) bool RestAgencyHandler::isDirect() const { return false; } -inline HttpHandler::status_t RestAgencyHandler::reportErrorEmptyRequest () - const { +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); @@ -78,19 +76,20 @@ inline HttpHandler::status_t RestAgencyHandler::redirect (id_t leader_id) { } inline HttpHandler::status_t RestAgencyHandler::handleReadWrite () { - + bool accepted; if (_request->suffix()[0] == "write") { - write_ret_t ret = _agent.write(_request->toVelocyPack()); - _agent.waitFor (ret); - + write_ret_t ret = _agent->write(_request->toVelocyPack()); + accepted = ret.accepted; + _agent->waitFor (ret); } else { - ret = _agent.read(_request->toVelocyPack()); + ret = _agent->read(_request->toVelocyPack()); + accepted = ret.accepted; } - if (ret.accepted) { // We accepted the request + if (accepted) { // We accepted the request ret.result->close(); generateResult(ret.result->slice()); } else { // We redirect the request - _response->setHeader("Location", _agent.config().endpoints[ret.redirect]); + _response->setHeader("Location", _agent->config().endpoints[ret.redirect]); generateError(HttpResponse::TEMPORARY_REDIRECT,307); } return HttpHandler::status_t(HANDLER_DONE); diff --git a/arangod/RestHandler/RestAgencyHandler.h b/arangod/RestHandler/RestAgencyHandler.h index 668f6b533d..623c232edb 100644 --- a/arangod/RestHandler/RestAgencyHandler.h +++ b/arangod/RestHandler/RestAgencyHandler.h @@ -48,6 +48,8 @@ class RestAgencyHandler : public arangodb::RestBaseHandler { status_t reportErrorEmptyRequest() ; status_t reportTooManySuffices() ; status_t unknownMethod() ; + status_t redirect(id_t leader_id) ; + status_t handleReadWrite() ; consensus::Agent* _agent; diff --git a/arangod/RestHandler/RestAgencyPrivHandler.cpp b/arangod/RestHandler/RestAgencyPrivHandler.cpp index 3f7e93c7c9..7d907237ca 100644 --- a/arangod/RestHandler/RestAgencyPrivHandler.cpp +++ b/arangod/RestHandler/RestAgencyPrivHandler.cpp @@ -21,7 +21,7 @@ /// @author Kaveh Vahedipour //////////////////////////////////////////////////////////////////////////////// -#include "Rest/AnyServer.h" +#include "RestServer/ArangoServer.h" #include "Rest/HttpRequest.h" #include "Rest/Version.h" #include "RestAgencyPrivHandler.h" @@ -43,7 +43,7 @@ using namespace arangodb::consensus; /// @brief ArangoDB server //////////////////////////////////////////////////////////////////////////////// -extern AnyServer* ArangoInstance; +extern ArangoServer* ArangoInstance; RestAgencyPrivHandler::RestAgencyPrivHandler(HttpRequest* request, Agent* agent) diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index e54bcdeb00..44c8ad7bc6 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -37,11 +37,8 @@ #include "Actions/RestActionHandler.h" #include "Actions/actions.h" -<<<<<<< HEAD #include "Agency/ApplicationAgency.h" -======= #include "ApplicationServer/ApplicationServer.h" ->>>>>>> cfe62a9f9a3148d031533a899aa3d63523e8eb74 #include "Aql/Query.h" #include "Aql/QueryCache.h" #include "Aql/RestAqlHandler.h" From 98ebbb5b40e7a5073ee452fff4147b68705c913c Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 29 Feb 2016 09:01:09 +0100 Subject: [PATCH 24/61] agency on --- arangod/Agency/AgencyCommon.h | 6 +++--- arangod/Agency/Agent.cpp | 12 +++++++++++- arangod/Agency/State.cpp | 16 +++++++++++++--- arangod/Agency/State.h | 6 +++++- arangod/RestHandler/RestAgencyHandler.cpp | 18 ++++++++++++------ 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 23d11ee97d..071728201d 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -117,15 +117,15 @@ using namespace std::chrono; * @brief State entry */ struct log_t { + index_t index; term_t term; id_t leaderId; - index_t index; std::string entry; std::vector ack; milliseconds timestamp; - log_t (term_t t, id_t lid, index_t idx, std::string const& e, + log_t (index_t idx, term_t t, id_t lid, std::string const& e, std::vector const& r) : - term(t), leaderId(lid), index(idx), entry(e), ack(r), timestamp ( + index(idx), term(t), leaderId(lid), entry(e), ack(r), timestamp ( duration_cast(system_clock::now().time_since_epoch())) {} }; diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index da7c28d4c2..badfd818b5 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -107,8 +107,18 @@ bool waitFor(std::vector& unconfirmed) { append_entries_t Agent::appendEntries ( term_t term, id_t leaderId, index_t prevLogIndex, term_t prevLogTerm, index_t leadersLastCommitIndex, query_t const& query) { - if (term < this->term()) // Reply false if term < currentTerm (§5.1) + + if (term < this->term()) { // Reply false if term < currentTerm (§5.1) + LOG(WARN) << "Term of entry to be appended smaller than my own term (§5.1)"; return append_entries_t(false,this->term()); + } + + if (!_state.findit(prevLogIndex, prevLogTerm)) { // Find entry at pli with plt + LOG(WARN) << "No entry in logs at index " << prevLogIndex + << " and term " prevLogTerm; + return append_entries_t(false,this->term()); + } + /* if (query->isEmpty()) { // heartbeat received diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index 061f439d23..1f87a6f3d2 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -33,9 +33,19 @@ State::~State() { save(); } -void State::log (query_t const& query) { - MUTEX_LOCKER(mutexLocker, _logLock); - _log.push_back(query); +void State::log (query_t const& query, term_t term, id_t lid, size_t size) { + MUTEX_LOCKER(mutexLocker, _logLock); + index_t idx = _log.end().index+1; + _log.push_back(idx, term, lid, query.toString(), std::vector(size)); + // Sync call arango +} + +bool findit (index_t index, term_t term) { + for (auto const& i : _log) { // Find entry matching index and term + if (i.index == index && i.term == term) + return true; + } + return false; } bool save (std::string const& ep) {}; diff --git a/arangod/Agency/State.h b/arangod/Agency/State.h index 3b0b634784..b4ac53c22c 100644 --- a/arangod/Agency/State.h +++ b/arangod/Agency/State.h @@ -80,8 +80,12 @@ public: * @brief Load persisted data from above or start with empty log */ bool load (std::string const& ep = "tcp://localhost:8529"); - + /** + * @brief Find entry at index with term + */ + bool findit (index_t index, term_t term) const; + private: arangodb::Mutex _logLock; /**< @brief Mutex for modifying _log */ diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index 786c53948c..1b20fd8f78 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -1,3 +1,4 @@ + //////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// @@ -77,13 +78,18 @@ inline HttpHandler::status_t RestAgencyHandler::redirect (id_t leader_id) { inline HttpHandler::status_t RestAgencyHandler::handleReadWrite () { bool accepted; - if (_request->suffix()[0] == "write") { - write_ret_t ret = _agent->write(_request->toVelocyPack()); - accepted = ret.accepted; - _agent->waitFor (ret); + typedef arangodb::velocypack::Options opts_t; + opts_t opts; + if (_request->suffix()[0] == "write") { + write_ret_t ret = _agent->write ( + _request->toVelocyPack(std::make_shared(opts))); + accepted = ret.accepted; + _agent->waitFor (ret); // Wait for confirmation + } else { - ret = _agent->read(_request->toVelocyPack()); - accepted = ret.accepted; + ret = _agent->read( + _request->toVelocyPack(std::make_shared(opts)))); + accepted = ret.accepted; } if (accepted) { // We accepted the request ret.result->close(); From 638eda15ede249f2067288f2c642feed5ea63837 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 29 Feb 2016 09:49:23 +0100 Subject: [PATCH 25/61] agency on --- arangod/Agency/Agent.cpp | 52 +++++++++++-------- arangod/Agency/Agent.h | 2 + arangod/Agency/Constituent.cpp | 8 +-- arangod/Agency/State.cpp | 31 ++++++++--- arangod/Agency/State.h | 13 ++++- arangod/RestHandler/RestAgencyPrivHandler.cpp | 4 +- 6 files changed, 77 insertions(+), 33 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index badfd818b5..2f199c27bf 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -119,28 +119,20 @@ append_entries_t Agent::appendEntries ( return append_entries_t(false,this->term()); } + _state.log(query, index_t idx, term, leaderId, _config.size()); // Append all new entries + _read_db.apply(query); // once we become leader we create a new spear head + _last_commit_index = leadersLastCommitIndex; - /* - if (query->isEmpty()) { // heartbeat received - Builder builder; - builder.add("term", Value(this->term())); // Our own term - builder.add("voteFor", Value( // Our vite - _constituent.vote(term, leaderId, prevLogIndex, leadersLastCommitIndex))); - return query_ret_t(true, id(), std::make_shared(builder)); - } else if (_constituent.leading()) { // We are leading - _constituent.follow(term); - return _state.log(term, leaderId, prevLogIndex, term, leadersLastCommitIndex); - } else { // We redirect - return query_ret_t(false,_constituent.leaderID()); - }*/ } + + //query_ret_t write_ret_t Agent::write (query_t const& query) { if (_constituent.leading()) { // We are leading if (_spear_head.apply(query)) { // We could apply to spear head? std::vector indices = // otherwise through - _read_db.log (term(), id(), query); // Append to my own log + _read_db.log (query, term(), id(), _config.size()); // Append to my own log remotelyAppendEntries (indicies); // Schedule appendEntries RPCs. } else { throw QUERY_NOT_APPLICABLE; @@ -162,24 +154,42 @@ void State::run() { while (!_stopping) { auto dur = std::chrono::system_clock::now(); std::vector> work(_config.size()); + // Collect all unacknowledged - for (auto& i : State.log()) - for (size_t j = 0; j < _setup.size(); ++j) - if (!i.ack[j]) + for (auto& i : State.log()) { + for (size_t j = 0; j < _setup.size(); ++j) { + if (!i.ack[j]) { work[j].push_back(i.index); + }}} + // (re-)attempt RPCs - for (size_t j = 0; j < _setup.size(); ++j) + for (size_t j = 0; j < _setup.size(); ++j) { appendEntriesRPC (work[j]); + } + + // catch up read db + catchUpReadDB(); + // We were too fast? - if (dur = std::chrono::system_clock::now() - dur < _poll_interval) + if (dur = std::chrono::system_clock::now() - dur < _poll_interval) { std::this_thread::sleep_for (_poll_interval - dur); + } } } -bool State::operator()(ClusterCommResult* ccr) { - +bool State::operator(id_t, index_t) (ClusterCommResult* ccr) { + CONDITION_LOCKER(guard, _cv); + _state.confirm(id_t, index_t); + guard.broadcast(); } +void shutdown() { + // wake up all blocked rest handlers + _stopping = true; + CONDITION_LOCKER(guard, _cv); + guard.broadcast(); +} + }} diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 14ba6bc47b..777ba07668 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -128,6 +128,8 @@ public: config_t _config; status_t _status; + index_t _last_commit_index; + arangodb::Mutex _uncommitedLock; store _spear_head; diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index 7ce14780cc..8426dafa85 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -66,6 +66,7 @@ void Constituent::follow(term_t term) { void Constituent::lead() { _role = LEADER; + _agent->lead(); // We need to rebuild spear_head and read_db; } void Constituent::candidate() { @@ -199,8 +200,9 @@ void Constituent::callElection() { } void Constituent::run() { -/* - while (true) { + + // Always start off as follower + while (!_stopping) { if (_role == FOLLOWER) { _cast = false; // New round set not cast vote std::this_thread::sleep_for(sleepFor()); // Sleep for random time @@ -210,7 +212,7 @@ void Constituent::run() { callElection(); // Run for office } } -*/ + }; diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index 1f87a6f3d2..06dcba31c5 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -33,17 +33,36 @@ State::~State() { save(); } -void State::log (query_t const& query, term_t term, id_t lid, size_t size) { +std::vector State::log (query_t const& query, term_t term, id_t lid, size_t size) { MUTEX_LOCKER(mutexLocker, _logLock); index_t idx = _log.end().index+1; _log.push_back(idx, term, lid, query.toString(), std::vector(size)); - // Sync call arango } -bool findit (index_t index, term_t term) { - for (auto const& i : _log) { // Find entry matching index and term - if (i.index == index && i.term == term) - return true; +void State::log (query_t const& query, index_t idx, term_t term, id_t lid, size_t size) { + MUTEX_LOCKER(mutexLocker, _logLock); + _log.push_back(idx, term, lid, query.toString(), std::vector(size)); +} + +void State::confirm (id_t id, index_t index) { + MUTEX_LOCKER(mutexLocker, _logLock); + _log[index][id] = true; +} + +bool findit (index_t index, term_t term) { + 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; } diff --git a/arangod/Agency/State.h b/arangod/Agency/State.h index b4ac53c22c..1002f7cc5d 100644 --- a/arangod/Agency/State.h +++ b/arangod/Agency/State.h @@ -68,8 +68,14 @@ public: void append (query_t const& query); /** - * @brief Update log entry + * @brief Log entries (leader) */ + std::vector log (query_t const& query, term_t term, id_t lid, size_t size); + + /** + * @brief + */ + void log (query_t const& query, index_t, term_t term, id_t lid, size_t size); /** * @brief Save currentTerm, votedFor, log entries @@ -85,6 +91,11 @@ public: * @brief Find entry at index with term */ bool findit (index_t index, term_t term) const; + + /** + * @brief Confirm entry index for agent id + */ + void confirm (id_t id, index_t idx) private: diff --git a/arangod/RestHandler/RestAgencyPrivHandler.cpp b/arangod/RestHandler/RestAgencyPrivHandler.cpp index 7d907237ca..401bf27c22 100644 --- a/arangod/RestHandler/RestAgencyPrivHandler.cpp +++ b/arangod/RestHandler/RestAgencyPrivHandler.cpp @@ -82,9 +82,9 @@ HttpHandler::status_t RestAgencyPrivHandler::execute() { reportTooManySuffices(); } else { if (_request->suffix()[0] == "appendEntries") { // replication - _agent->appendEntries (_request->headers(), _request->toVelocyPack()); + _agent->appendEntries (_request->toVelocyPack()); } else if (_request->suffix()[0] == "requestVote") { // election - _agent->vote (_request->headers(), _request->toVelocyPack()); + _agent->requestVote (_request->toVelocyPack()); } else { generateError(HttpResponse::NOT_FOUND,404); // nothing return HttpHandler::status_t(HANDLER_DONE); From ca55688f74dee5359652171177d2a29bf4b93923 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 29 Feb 2016 10:04:09 +0100 Subject: [PATCH 26/61] agency on --- arangod/Agency/State.cpp | 43 +++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index 06dcba31c5..57b9d0ea67 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -33,10 +33,37 @@ State::~State() { save(); } -std::vector State::log (query_t const& query, term_t term, id_t lid, size_t size) { +//Leader +std::vector State::log (query_t const& query, term_t term, id_t lid, size_t size) { MUTEX_LOCKER(mutexLocker, _logLock); - index_t idx = _log.end().index+1; - _log.push_back(idx, term, lid, query.toString(), std::vector(size)); + std::vector idx; + Builder builder; + for (size_t i = 0; i < query->slice().length()) { + idx.push_back(_log.end().index+1); + _log.push_back(idx[i], term, lid, query.toString(), std::vector(size)); + builder.add("query", qyery->Slice()); + builder.add("idx", Value(term)); + builder.add("term", Value(term)); + builder.add("leaderID", Value(lid)); + builder.close(); + } + save (builder.slice()); + return idx; +} + +//Leader +void State::log (query_t const& query, std::vector cont& idx, term_t term, id_t lid, size_t size) { + MUTEX_LOCKER(mutexLocker, _logLock); + Builder builder; + for (size_t i = 0; i < query->slice().length()) { + _log.push_back(idx[i], term, lid, query.toString(), std::vector(size)); + builder.add("query", qyery->Slice()); + builder.add("idx", Value(term)); + builder.add("term", Value(term)); + builder.add("leaderID", Value(lid)); + builder.close(); + } + save (builder.slice()); } void State::log (query_t const& query, index_t idx, term_t term, id_t lid, size_t size) { @@ -67,9 +94,15 @@ bool findit (index_t index, term_t term) { return false; } -bool save (std::string const& ep) {}; +bool save (std::string const& ep) { + // Persist to arango db + +}; -bool load (std::string const& ep) {}; +load_ret_t load (std::string const& ep) { + // Read all from arango db + return load_ret_t (currentTerm, votedFor) +}; From 4860dac5fa94d546ea8eb9787e7f13606c4d7a9c Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 29 Feb 2016 15:09:03 +0100 Subject: [PATCH 27/61] Moving callbacks out of Agent into new class AgentCallbacks --- arangod/Agency/AgencyCommon.h | 8 +++ arangod/Agency/Agent.cpp | 47 +++++++++++++---- arangod/Agency/Agent.h | 18 +++++-- arangod/Agency/AgentCallbacks.cpp | 33 ++++++++++++ arangod/Agency/AgentCallbacks.h | 52 +++++++++++++++++++ arangod/Agency/State.cpp | 46 ++++++++++++---- arangod/Agency/State.h | 14 ++++- arangod/RestHandler/RestAgencyHandler.cpp | 5 +- .../RestHandler/RestVocbaseBaseHandler.cpp | 2 +- 9 files changed, 194 insertions(+), 31 deletions(-) create mode 100644 arangod/Agency/AgentCallbacks.cpp create mode 100644 arangod/Agency/AgentCallbacks.h diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 071728201d..accf2053c3 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -140,6 +140,14 @@ struct append_entries_t { append_entries_t (term_t t, bool s) : term(t), success(s) {} }; +struct collect_ret_t { + prev_log_index; + prev_log_term; + std::vector indices; + collect_ret_t (index_t pli, term_t plt, std::vector idx) : + prev_log_index(pli), prev_log_term(plt), indices(idx) {} +} + }} inline arangodb::LoggerStream& operator<< ( diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 2f199c27bf..551df72b41 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -34,7 +34,7 @@ Agent::Agent () : Thread ("Agent"), _stopping(false) {} Agent::Agent (config_t const& config) : _config(config), Thread ("Agent") { //readPersistence(); // Read persistence (log ) _constituent.configure(this); - _state.configure(this); + _state.configure(_config.size()); } id_t Agent::id() const { return _config.id;} @@ -125,15 +125,42 @@ append_entries_t Agent::appendEntries ( } +append_entries_t Agent::appendEntriesRPC ( + id_t slave_id, collect_ret_t const& entries) { + + std::vector result; + // RPC path + std::stringstream path; + path << "/_api/agency_priv/appendEntries?term=" << _term << "&leaderId=" + << id() << "&prevLogIndex=" << entries.prev_log_index << "&prevLogTerm=" + << entries.prev_log_term << "&leaderCommitId=" << commitId; + + // Headers + std::unique_ptr> headerFields = + std::make_unique >(); + + // Body + Builder builder; + builder.add("term", Value(term())); + builder.add("voteGranted", Value( + _constituent.vote(id, t, lastLogIndex, lastLogTerm))); + builder.close(); + + arangodb::ClusterComm::instance()->asyncRequest + ("1", 1, _config.end_points[slave_id], + rest::HttpRequest::HTTP_REQUEST_GET, + path.str(), std::make_shared(body), headerFields, + std::make_shared(_agent_callbacks), + 1.0, true); +} //query_ret_t write_ret_t Agent::write (query_t const& query) { if (_constituent.leading()) { // We are leading if (_spear_head.apply(query)) { // We could apply to spear head? std::vector indices = // otherwise through - _read_db.log (query, term(), id(), _config.size()); // Append to my own log - remotelyAppendEntries (indicies); // Schedule appendEntries RPCs. + _state.log (query, term(), id(), _config.size()); // Append to my own log } else { throw QUERY_NOT_APPLICABLE; } @@ -156,16 +183,16 @@ void State::run() { std::vector> work(_config.size()); // Collect all unacknowledged - for (auto& i : State.log()) { - for (size_t j = 0; j < _setup.size(); ++j) { - if (!i.ack[j]) { - work[j].push_back(i.index); - }}} + for (size_t i = 0; i < _size() ++i) { + if (i != id()) { + work[i] = _state.collectUnAcked(i); + }} // (re-)attempt RPCs for (size_t j = 0; j < _setup.size(); ++j) { - appendEntriesRPC (work[j]); - } + if (j != id() && work[j].size()) { + appendEntriesRPC(j, work[j]); + }} // catch up read db catchUpReadDB(); diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 777ba07668..a5e72bf2e4 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -24,6 +24,7 @@ #ifndef __ARANGODB_CONSENSUS_AGENT__ #define __ARANGODB_CONSENSUS_AGENT__ +#include "AgentCallbacks.h" #include "AgencyCommon.h" #include "Constituent.h" #include "State.h" @@ -32,7 +33,8 @@ namespace arangodb { namespace consensus { -class Agent : public arangodb::Thread { // We need to asynchroneously append entries +class Agent : public arangodb::Thread, { + // We need to asynchroneously append entries public: @@ -110,8 +112,8 @@ public: * @brief Invoked by leader to replicate log entries (§5.3); * also used as heartbeat (§5.2). */ - append_entries_t append_entries_t (term_t, id_t, index_t, term_t, index_t, - query_t const&); + append_entries_t appendEntries (term_t, id_t, index_t, term_t, index_t, + query_t const&); /** * @brief 1. Deal with appendEntries to slaves. @@ -119,22 +121,28 @@ public: */ void run (); - + void reportIn (id_t id, std::vector idx); + bool waitFor (std::vector entries); + operator (id_t id, index_t idx) (ClusterCommResult *); + private: Constituent _constituent; /**< @brief Leader election delegate */ State _state; /**< @brief Log replica */ config_t _config; status_t _status; - index_t _last_commit_index; + std::atomic _last_commit_index; + index_t _last_commit_index_tmp; arangodb::Mutex _uncommitedLock; store _spear_head; store _read_db; + AgentCallbacks _agent_callbacks; + arangodb::basics::ConditionVariable _cv; std::atomic _stopping; diff --git a/arangod/Agency/AgentCallbacks.cpp b/arangod/Agency/AgentCallbacks.cpp new file mode 100644 index 0000000000..c328dc4dc3 --- /dev/null +++ b/arangod/Agency/AgentCallbacks.cpp @@ -0,0 +1,33 @@ +#include "AgentCallbacks.h" + +AgentCallbacks::AgentCallbacks() : _agent(0) {} + +AgentCallbacks::AgentCallbacks(Agent* agent) : _agent(agent) {} + +AgentCallbacks::shutdown() { + _agent = 0; +} + +bool AgentCallbacks::operator()(ClusterCommResult* res) { + if (res->status == CL_COMM_RECEIVED) { + id_t agent_id; + std::vector idx; + std::shared_ptr< VPackBuilder > builder = res->result->getVelocyPack(); + if (builder->hasKey("agent_id")) { + agent_id = getUInt("agent_id"); + } + if (builder->hasKey("indices")) { + Slice indices = builder.getKey("indices"); + if (indices.isArray()) { + for (size_t i = 0; i < indices.length(); ++i) { + idx.push_back(indices[i].getUInt()); + } + } + } + if(_agent) { + _agent->reportIn (agent_id, idx); + } + } + return true; +} + diff --git a/arangod/Agency/AgentCallbacks.h b/arangod/Agency/AgentCallbacks.h new file mode 100644 index 0000000000..d1675c57fb --- /dev/null +++ b/arangod/Agency/AgentCallbacks.h @@ -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__ +#define __ARANGODB_CONSENSUS_AGENT__ + +#include "Agent.h" + +namespace arangodb { +namespace consensus { + +class AgentCallbacks : public arangodb::ClusterCommCallback { + +public: + + AgentCallback(); + AgentCallback(Agent* agent); + + bool operator ()(ClusterCommResult*); + + void shutdown(); + +private: + Agent* _agent; + +}; + + + +}} // namespace + +#endif diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index 57b9d0ea67..088281572d 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -28,9 +28,13 @@ using namespace arangodb::consensus; -State::State() {} -State::~State() { - save(); +State::State() { +} + +State::~State() {} + +State::configure (size_t size) { + _log.push_back(log_t (0, 0, 0, "", std::vector(size,true))); } //Leader @@ -41,8 +45,9 @@ std::vector State::log (query_t const& query, term_t term, id_t lid, si for (size_t i = 0; i < query->slice().length()) { idx.push_back(_log.end().index+1); _log.push_back(idx[i], term, lid, query.toString(), std::vector(size)); + _log.end().ack[lid] = true; // Leader confirms myself builder.add("query", qyery->Slice()); - builder.add("idx", Value(term)); + builder.add("idx", Value(idx[i])); builder.add("term", Value(term)); builder.add("leaderID", Value(lid)); builder.close(); @@ -51,16 +56,16 @@ std::vector State::log (query_t const& query, term_t term, id_t lid, si return idx; } -//Leader +//Follower void State::log (query_t const& query, std::vector cont& idx, term_t term, id_t lid, size_t size) { MUTEX_LOCKER(mutexLocker, _logLock); Builder builder; for (size_t i = 0; i < query->slice().length()) { _log.push_back(idx[i], term, lid, query.toString(), std::vector(size)); - builder.add("query", qyery->Slice()); - builder.add("idx", Value(term)); - builder.add("term", Value(term)); - builder.add("leaderID", Value(lid)); + builder.add("query", qyery->Slice()); // query + builder.add("idx", Value(idx[i])); // log index + builder.add("term", Value(term)); // term + builder.add("leaderID", Value(lid)); // leader id builder.close(); } save (builder.slice()); @@ -77,6 +82,7 @@ void State::confirm (id_t id, index_t index) { } bool 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) { @@ -94,9 +100,29 @@ bool findit (index_t index, term_t term) { return false; } +collect_ret_t collectUnacked (id_t id) { + // Collect all unacknowledged + MUTEX_LOCKER(mutexLocker, _logLock); + std::vector work; + bool found_first = false; + id_t prev_log_term; + index_t prev_log_index; + for (size_t i = 0; i < _log.end(); ++i) { + if (!_log[i].ack[id]) { + work.push_back(_log[i].index); + if (!found_first) { + prev_log_term = _log[i-1].term; + prev_log_index = _log[i-1].index; + found_first = true; + } + } + } + return collect_ret_t(prev_log_index, prev_log_term, work); +} + bool save (std::string const& ep) { // Persist to arango db - + // AQL votedFor, lastCommit }; load_ret_t load (std::string const& ep) { diff --git a/arangod/Agency/State.h b/arangod/Agency/State.h index 1002f7cc5d..487fee12e6 100644 --- a/arangod/Agency/State.h +++ b/arangod/Agency/State.h @@ -55,13 +55,18 @@ public: /** * @brief Default constructor */ - State (); + State (const size_t size); /** * @brief Default Destructor */ virtual ~State(); + /** + * @brief Configure this state machine + */ + void configure (size_t size); + /** * @brief Append log entry */ @@ -95,7 +100,12 @@ public: /** * @brief Confirm entry index for agent id */ - void confirm (id_t id, index_t idx) + void confirm (id_t id, index_t idx); + + /** + * @brief Collect all unacknowledged for agent id + */ + collect_ret_t collectUnacked (id_t id); private: diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index 1b20fd8f78..063af0405d 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -84,11 +84,10 @@ inline HttpHandler::status_t RestAgencyHandler::handleReadWrite () { write_ret_t ret = _agent->write ( _request->toVelocyPack(std::make_shared(opts))); accepted = ret.accepted; - _agent->waitFor (ret); // Wait for confirmation - + _agent->waitFor (ret.indices); // Wait for confirmation } else { ret = _agent->read( - _request->toVelocyPack(std::make_shared(opts)))); + _request->toVelocyPack(std::make_shared(opts))); accepted = ret.accepted; } if (accepted) { // We accepted the request diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.cpp b/arangod/RestHandler/RestVocbaseBaseHandler.cpp index 20050cd600..4ab71df591 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.cpp +++ b/arangod/RestHandler/RestVocbaseBaseHandler.cpp @@ -51,7 +51,7 @@ std::string const RestVocbaseBaseHandler::AGENCY_PATH = "/_api/agency"; /// @brief agency private path //////////////////////////////////////////////////////////////////////////////// -std::string const RestVocbaseBaseHandler::AGENCY_PRIV_PATH = "/_api/agency"; +std::string const RestVocbaseBaseHandler::AGENCY_PRIV_PATH = "/_api/agency_priv"; //////////////////////////////////////////////////////////////////////////////// /// @brief batch path From 4435236e91b62621a350510c833ba17a8b6653d9 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Tue, 1 Mar 2016 08:52:20 +0100 Subject: [PATCH 28/61] property_map --- CMakeLists.txt | 8 +-- arangod/Agency/AgencyCommon.h | 13 +++-- arangod/Agency/Agent.cpp | 53 +++++++++++-------- arangod/Agency/Agent.h | 27 ++++++---- arangod/Agency/AgentCallback.cpp | 39 ++++++++++++++ .../{AgentCallbacks.h => AgentCallback.h} | 8 +-- arangod/Agency/AgentCallbacks.cpp | 33 ------------ arangod/Agency/State.cpp | 7 ++- arangod/Agency/State.h | 2 +- arangod/CMakeLists.txt | 5 ++ arangod/RestHandler/RestAgencyHandler.cpp | 15 +++--- 11 files changed, 119 insertions(+), 91 deletions(-) create mode 100644 arangod/Agency/AgentCallback.cpp rename arangod/Agency/{AgentCallbacks.h => AgentCallback.h} (92%) delete mode 100644 arangod/Agency/AgentCallbacks.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f4e88eeb43..7c01e2509d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -573,7 +573,7 @@ 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() +#if (USE_MAINTAINER_MODE) +# add_dependencies(basics_suite v8_build) +# add_dependencies(geo_suite v8_build) +#endif() diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index accf2053c3..1761b58159 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -107,9 +107,9 @@ typedef std::initializer_list index_list_t; struct write_ret_t { bool accepted; // Query processed id_t redirect; // Otherwise redirect to - std::vector lindices; // Indices of log entries (if any) to wait for + std::vector indices; // Indices of log entries (if any) to wait for write_ret_t (bool a, id_t id, index_list_t const& idx = index_list_t()) : - accepted(a), redirect(id), lindices(idx) {} + accepted(a), redirect(id), indices(idx) {} }; using namespace std::chrono; @@ -121,11 +121,10 @@ struct log_t { term_t term; id_t leaderId; std::string entry; - std::vector ack; milliseconds timestamp; log_t (index_t idx, term_t t, id_t lid, std::string const& e, std::vector const& r) : - index(idx), term(t), leaderId(lid), entry(e), ack(r), timestamp ( + index(idx), term(t), leaderId(lid), entry(e), timestamp ( duration_cast(system_clock::now().time_since_epoch())) {} }; @@ -141,12 +140,12 @@ struct append_entries_t { }; struct collect_ret_t { - prev_log_index; - prev_log_term; + index_t prev_log_index; + term_t prev_log_term; std::vector indices; collect_ret_t (index_t pli, term_t plt, std::vector idx) : prev_log_index(pli), prev_log_term(plt), indices(idx) {} -} +}; }} diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 551df72b41..1589f9c15e 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -32,9 +32,8 @@ namespace consensus { Agent::Agent () : Thread ("Agent"), _stopping(false) {} Agent::Agent (config_t const& config) : _config(config), Thread ("Agent") { - //readPersistence(); // Read persistence (log ) _constituent.configure(this); - _state.configure(_config.size()); + _state.read(); // read persistent database } id_t Agent::id() const { return _config.id;} @@ -81,23 +80,26 @@ arangodb::LoggerStream& operator<< (arangodb::LoggerStream& l, Agent const& a) { } -bool waitFor(std::vector& unconfirmed) { +bool waitFor (index_t index, std::chrono::duration timeout = 2.0) { + + CONDITION_LOCKER(guard, _cv_rest); + auto start = std::chrono::system_clock::now(); + while (true) { - CONDITION_LOCKER(guard, _cv); - // Shutting down + + _cv.wait(); + + // shutting down if (_stopping) { return false; } - // Remove any unconfirmed which is confirmed - for (size_t i = 0; i < unconfirmed.size(); ++i) { - if (auto found = find (_unconfirmed.begin(), _unconfirmed.end(), - unconfirmed[i])) { - unconfirmed.erase(found); - } - } - // none left? - if (unconfirmed.size() ==0) { - return true; + // timeout? + if (std::chrono::system_clock::now() - start > timeout) + return false; + // more than half have confirmed + if (std::count_if(_confirmed.begin(), _confirmed.end(), + [](index_t i) {return i >= index}) > size()/2) { + return true; } } // We should never get here @@ -156,11 +158,13 @@ append_entries_t Agent::appendEntriesRPC ( } //query_ret_t -write_ret_t Agent::write (query_t const& query) { - if (_constituent.leading()) { // We are leading - if (_spear_head.apply(query)) { // We could apply to spear head? - std::vector indices = // otherwise through +write_ret_t Agent::write (query_t const& query) { // Signal auf die _cv + if (_constituent.leading()) { // We are leading + if (_spear_head.apply(query)) { // We could apply to spear head? + std::vector indices = // otherwise through _state.log (query, term(), id(), _config.size()); // Append to my own log + _confirmed[id()]++; + return } else { throw QUERY_NOT_APPLICABLE; } @@ -186,18 +190,20 @@ void State::run() { for (size_t i = 0; i < _size() ++i) { if (i != id()) { work[i] = _state.collectUnAcked(i); - }} + } + } // (re-)attempt RPCs for (size_t j = 0; j < _setup.size(); ++j) { if (j != id() && work[j].size()) { appendEntriesRPC(j, work[j]); - }} + } + } // catch up read db catchUpReadDB(); - // We were too fast? + // We were too fast?m wait _cvw if (dur = std::chrono::system_clock::now() - dur < _poll_interval) { std::this_thread::sleep_for (_poll_interval - dur); } @@ -210,6 +216,9 @@ bool State::operator(id_t, index_t) (ClusterCommResult* ccr) { guard.broadcast(); } +inline size_t size() const { + return _config.size(); +} void shutdown() { // wake up all blocked rest handlers diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index a5e72bf2e4..75b2e1bada 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -24,16 +24,18 @@ #ifndef __ARANGODB_CONSENSUS_AGENT__ #define __ARANGODB_CONSENSUS_AGENT__ -#include "AgentCallbacks.h" #include "AgencyCommon.h" +#include "AgentCallback.h" #include "Constituent.h" #include "State.h" #include "Store.h" +#include + namespace arangodb { namespace consensus { -class Agent : public arangodb::Thread, { +class Agent : public arangodb::Thread { // We need to asynchroneously append entries public: @@ -121,17 +123,21 @@ public: */ void run (); + /** + * @brief Report appended entries from AgentCallback + */ void reportIn (id_t id, std::vector idx); - bool waitFor (std::vector entries); + /** + * @brief Wait for slaves to confirm appended entries + */ + bool waitFor (std::vector entries, std::chrono::duration timeout=2.0); - operator (id_t id, index_t idx) (ClusterCommResult *); +private: - private: Constituent _constituent; /**< @brief Leader election delegate */ State _state; /**< @brief Log replica */ - config_t _config; - status_t _status; + config_t _config; /**< @brief Command line arguments */ std::atomic _last_commit_index; index_t _last_commit_index_tmp; @@ -141,12 +147,15 @@ public: store _spear_head; store _read_db; - AgentCallbacks _agent_callbacks; + AgentCallback _agent_callback; - arangodb::basics::ConditionVariable _cv; + arangodb::basics::ConditionVariable _cv; // agency callbacks + arangodb::basics::ConditionVariable _cv_rest; // rest handler std::atomic _stopping; + std::vector _confirmed; + }; } diff --git a/arangod/Agency/AgentCallback.cpp b/arangod/Agency/AgentCallback.cpp new file mode 100644 index 0000000000..295c9e880d --- /dev/null +++ b/arangod/Agency/AgentCallback.cpp @@ -0,0 +1,39 @@ +#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()(ClusterCommResult* res) { + + if (res->status == CL_COMM_RECEIVED) { + id_t agent_id; + std::vector idx; + std::shared_ptr< VPackBuilder > builder = res->result->getBodyVelocyPack(); + if (builder->hasKey("agent_id")) { + agent_id = builder->getKey("agent_id").getUInt(); + } + if (builder->hasKey("indices")) { + Slice indices = builder->getKey("indices"); + if (indices.isArray()) { + for (size_t i = 0; i < indices.length(); ++i) { + idx.push_back(indices[i].getUInt()); + } + } + } + if(_agent) { + _agent->reportIn (agent_id, idx); + } + } + return true; +} + diff --git a/arangod/Agency/AgentCallbacks.h b/arangod/Agency/AgentCallback.h similarity index 92% rename from arangod/Agency/AgentCallbacks.h rename to arangod/Agency/AgentCallback.h index d1675c57fb..0ea91b526d 100644 --- a/arangod/Agency/AgentCallbacks.h +++ b/arangod/Agency/AgentCallback.h @@ -24,12 +24,14 @@ #ifndef __ARANGODB_CONSENSUS_AGENT__ #define __ARANGODB_CONSENSUS_AGENT__ -#include "Agent.h" +#include "Cluster/ClusterComm.h" + +class Agent; namespace arangodb { namespace consensus { -class AgentCallbacks : public arangodb::ClusterCommCallback { +class AgentCallback : public arangodb::ClusterCommCallback { public: @@ -45,8 +47,6 @@ private: }; - - }} // namespace #endif diff --git a/arangod/Agency/AgentCallbacks.cpp b/arangod/Agency/AgentCallbacks.cpp deleted file mode 100644 index c328dc4dc3..0000000000 --- a/arangod/Agency/AgentCallbacks.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "AgentCallbacks.h" - -AgentCallbacks::AgentCallbacks() : _agent(0) {} - -AgentCallbacks::AgentCallbacks(Agent* agent) : _agent(agent) {} - -AgentCallbacks::shutdown() { - _agent = 0; -} - -bool AgentCallbacks::operator()(ClusterCommResult* res) { - if (res->status == CL_COMM_RECEIVED) { - id_t agent_id; - std::vector idx; - std::shared_ptr< VPackBuilder > builder = res->result->getVelocyPack(); - if (builder->hasKey("agent_id")) { - agent_id = getUInt("agent_id"); - } - if (builder->hasKey("indices")) { - Slice indices = builder.getKey("indices"); - if (indices.isArray()) { - for (size_t i = 0; i < indices.length(); ++i) { - idx.push_back(indices[i].getUInt()); - } - } - } - if(_agent) { - _agent->reportIn (agent_id, idx); - } - } - return true; -} - diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index 088281572d..6d54f24fc6 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -34,7 +34,7 @@ State::State() { State::~State() {} State::configure (size_t size) { - _log.push_back(log_t (0, 0, 0, "", std::vector(size,true))); + _log.push_back(log_t (0, 0, 0, ""); } //Leader @@ -44,8 +44,7 @@ std::vector State::log (query_t const& query, term_t term, id_t lid, si Builder builder; for (size_t i = 0; i < query->slice().length()) { idx.push_back(_log.end().index+1); - _log.push_back(idx[i], term, lid, query.toString(), std::vector(size)); - _log.end().ack[lid] = true; // Leader confirms myself + _log.push_back(idx[i], term, lid, query.toString()); builder.add("query", qyery->Slice()); builder.add("idx", Value(idx[i])); builder.add("term", Value(term)); @@ -78,7 +77,7 @@ void State::log (query_t const& query, index_t idx, term_t term, id_t lid, size_ void State::confirm (id_t id, index_t index) { MUTEX_LOCKER(mutexLocker, _logLock); - _log[index][id] = true; + _log[index].ack[id] = true; } bool findit (index_t index, term_t term) { diff --git a/arangod/Agency/State.h b/arangod/Agency/State.h index 487fee12e6..267bb3ffbd 100644 --- a/arangod/Agency/State.h +++ b/arangod/Agency/State.h @@ -55,7 +55,7 @@ public: /** * @brief Default constructor */ - State (const size_t size); + State (); /** * @brief Default Destructor diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index a95cd80c98..11f2c13b2d 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -68,6 +68,11 @@ add_executable(${BIN_ARANGOD} ${ProductVersionFiles} Actions/RestActionHandler.cpp Actions/actions.cpp + Agency/Agent.cpp + Agency/AgentCallback.cpp + Agency/ApplicationAgency.cpp + Agency/Constituent.cpp + Agency/State.cpp ApplicationServer/ApplicationFeature.cpp ApplicationServer/ApplicationServer.cpp Aql/Aggregator.cpp diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index 063af0405d..39611444d9 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -78,19 +78,20 @@ inline HttpHandler::status_t RestAgencyHandler::redirect (id_t leader_id) { inline HttpHandler::status_t RestAgencyHandler::handleReadWrite () { bool accepted; - typedef arangodb::velocypack::Options opts_t; - opts_t opts; + arangodb::velocypack::Options options; + if (_request->suffix()[0] == "write") { - write_ret_t ret = _agent->write ( - _request->toVelocyPack(std::make_shared(opts))); + write_ret_t ret = _agent->write (_request->toVelocyPack(&options)); accepted = ret.accepted; _agent->waitFor (ret.indices); // Wait for confirmation } else { - ret = _agent->read( - _request->toVelocyPack(std::make_shared(opts))); + read_ret_t ret = _agent->read(_request->toVelocyPack(&options)); accepted = ret.accepted; + ret.result->close(); + generateResult(ret.result->slice()); } - if (accepted) { // We accepted the request + + if (!accepted) { // We accepted the request ret.result->close(); generateResult(ret.result->slice()); } else { // We redirect the request From 447223a864cc4157bd6ba05096384a4e7db7ebae Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Tue, 1 Mar 2016 09:12:19 +0100 Subject: [PATCH 29/61] property_map --- arangod/Agency/Agent.cpp | 23 +++++++++-------------- arangod/Agency/Agent.h | 2 +- arangod/Agency/Constituent.cpp | 2 +- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 1589f9c15e..478df5d5e7 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -22,6 +22,7 @@ //////////////////////////////////////////////////////////////////////////////// #include "Agent.h" +#include "Basics/ConditionLocker.h" #include @@ -33,7 +34,7 @@ Agent::Agent () : Thread ("Agent"), _stopping(false) {} Agent::Agent (config_t const& config) : _config(config), Thread ("Agent") { _constituent.configure(this); - _state.read(); // read persistent database + _state.load(); // read persistent database } id_t Agent::id() const { return _config.id;} @@ -67,7 +68,7 @@ void Agent::print (arangodb::LoggerStream& logger) const { } void Agent::report(status_t status) { - _status = status; + //_status = status; } id_t Agent::leaderID () const { @@ -80,7 +81,7 @@ arangodb::LoggerStream& operator<< (arangodb::LoggerStream& l, Agent const& a) { } -bool waitFor (index_t index, std::chrono::duration timeout = 2.0) { +bool Agent::waitFor (index_t index, duration_t timeout) { CONDITION_LOCKER(guard, _cv_rest); auto start = std::chrono::system_clock::now(); @@ -90,7 +91,7 @@ bool waitFor (index_t index, std::chrono::duration timeout = 2.0) { _cv.wait(); // shutting down - if (_stopping) { + if (this->isStopping()) { return false; } // timeout? @@ -182,7 +183,7 @@ read_ret_t Agent::read (query_t const& query) const { } void State::run() { - while (!_stopping) { + while (!this->isStopping()) { auto dur = std::chrono::system_clock::now(); std::vector> work(_config.size()); @@ -210,19 +211,13 @@ void State::run() { } } -bool State::operator(id_t, index_t) (ClusterCommResult* ccr) { - CONDITION_LOCKER(guard, _cv); - _state.confirm(id_t, index_t); - guard.broadcast(); -} - -inline size_t size() const { +inline size_t Agent::size() const { return _config.size(); } -void shutdown() { +void Agent::shutdown() { // wake up all blocked rest handlers - _stopping = true; + _agent_callback.shutdown(); CONDITION_LOCKER(guard, _cv); guard.broadcast(); } diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 75b2e1bada..33c0c979dd 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -131,7 +131,7 @@ public: /** * @brief Wait for slaves to confirm appended entries */ - bool waitFor (std::vector entries, std::chrono::duration timeout=2.0); + bool waitFor (std::vector entries, duration_t timeout = duration_t(2.0)); private: diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index 8426dafa85..1e66c883cb 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -202,7 +202,7 @@ void Constituent::callElection() { void Constituent::run() { // Always start off as follower - while (!_stopping) { + while (!isStopping()) { if (_role == FOLLOWER) { _cast = false; // New round set not cast vote std::this_thread::sleep_for(sleepFor()); // Sleep for random time From 316f0cabfa22bd9ef460954f37e4770ad24575cc Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Tue, 1 Mar 2016 12:23:34 +0100 Subject: [PATCH 30/61] found typo in definition --- arangod/Agency/AgencyCommon.h | 21 ++- arangod/Agency/Agent.cpp | 149 ++++++++++-------- arangod/Agency/Agent.h | 33 ++-- arangod/Agency/AgentCallback.cpp | 4 +- arangod/Agency/AgentCallback.h | 12 +- arangod/Agency/State.cpp | 49 +++--- arangod/Agency/State.h | 19 ++- arangod/RestHandler/RestAgencyPrivHandler.cpp | 3 +- 8 files changed, 160 insertions(+), 130 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 1761b58159..810a18a22a 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -39,8 +39,10 @@ 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 + 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 @@ -67,11 +69,11 @@ template struct Config { append_entries_retry_interval(appent_i), end_points(end_p) {} /* void print (arangodb::LoggerStream& l) const { l << "Config: " - << "min_ping(" << min_ping << ")" - << "max_ping(" << max_ping << ")" - << "size(" << end_points.size() << ")" - << end_points; - }*/ + << "min_ping(" << min_ping << ")" + << "max_ping(" << max_ping << ")" + << "size(" << end_points.size() << ")" + << end_points; + }*/ inline size_t size() const {return end_points.size();} }; @@ -110,6 +112,8 @@ struct write_ret_t { std::vector indices; // Indices of log entries (if any) to wait for write_ret_t (bool a, id_t id, index_list_t const& idx = index_list_t()) : accepted(a), redirect(id), indices(idx) {} + write_ret_t (bool a, id_t id, std::vector const& idx) : + accepted(a), redirect(id), indices(idx) {} }; using namespace std::chrono; @@ -127,7 +131,6 @@ struct log_t { index(idx), term(t), leaderId(lid), entry(e), timestamp ( duration_cast(system_clock::now().time_since_epoch())) {} }; - enum AGENCY_EXCEPTION { QUERY_NOT_APPLICABLE @@ -143,8 +146,10 @@ struct collect_ret_t { index_t prev_log_index; term_t prev_log_term; std::vector indices; - collect_ret_t (index_t pli, term_t plt, std::vector idx) : + collect_ret_t () : prev_log_index(0), prev_log_term(0) {} + collect_ret_t (index_t pli, term_t plt, std::vector const& idx) : prev_log_index(pli), prev_log_term(plt), indices(idx) {} + size_t size() const {return indices.size();} }; }} diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 478df5d5e7..ec51a35d76 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -49,6 +49,10 @@ term_t Agent::term () const { return _constituent.term(); } +inline size_t Agent::size() const { + return _config.size(); +} + query_t Agent::requestVote(term_t t, id_t id, index_t lastLogIndex, index_t lastLogTerm) { Builder builder; @@ -80,64 +84,79 @@ arangodb::LoggerStream& operator<< (arangodb::LoggerStream& l, Agent const& a) { return l; } +void Agent::catchUpReadDB() {}; // TODO bool Agent::waitFor (index_t index, duration_t timeout) { - CONDITION_LOCKER(guard, _cv_rest); + CONDITION_LOCKER(guard, _rest_cv); auto start = std::chrono::system_clock::now(); while (true) { - _cv.wait(); + _rest_cv.wait(); // shutting down if (this->isStopping()) { return false; } - // timeout? - if (std::chrono::system_clock::now() - start > timeout) + // timeout? + if (std::chrono::system_clock::now() - start > timeout) { return false; - // more than half have confirmed - if (std::count_if(_confirmed.begin(), _confirmed.end(), - [](index_t i) {return i >= index}) > size()/2) { - return true; } + if (_last_commit_index > index) + return true; } // We should never get here TRI_ASSERT(false); } -append_entries_t Agent::appendEntries ( - term_t term, id_t leaderId, index_t prevLogIndex, term_t prevLogTerm, - index_t leadersLastCommitIndex, query_t const& query) { - - if (term < this->term()) { // Reply false if term < currentTerm (§5.1) - LOG(WARN) << "Term of entry to be appended smaller than my own term (§5.1)"; - return append_entries_t(false,this->term()); +void Agent::reportIn (id_t id, index_t index) { + MUTEX_LOCKER(mutexLocker, _confirmedLock); + if (index > _confirmed[id]) + _confirmed[id] = index; + // last commit index smaller? + // check if we can move forward + if(_last_commit_index < index) { + size_t n = 0; + for (size_t i = 0; i < size(); ++i) { + n += (_confirmed[i]>index); + } + if (n>size()/2) { + _last_commit_index = index; + } } - - if (!_state.findit(prevLogIndex, prevLogTerm)) { // Find entry at pli with plt - LOG(WARN) << "No entry in logs at index " << prevLogIndex - << " and term " prevLogTerm; - return append_entries_t(false,this->term()); - } - - _state.log(query, index_t idx, term, leaderId, _config.size()); // Append all new entries - _read_db.apply(query); // once we become leader we create a new spear head - _last_commit_index = leadersLastCommitIndex; - + _rest_cv.broadcast(); } -append_entries_t Agent::appendEntriesRPC ( - id_t slave_id, collect_ret_t const& entries) { - - std::vector result; +bool 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 true; +} + +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=" + path << "/_api/agency_priv/appendEntries?term=" << term() << "&leaderId=" << id() << "&prevLogIndex=" << entries.prev_log_index << "&prevLogTerm=" - << entries.prev_log_term << "&leaderCommitId=" << commitId; + << entries.prev_log_term << "&leaderCommit=" << _last_commit_index; // Headers std::unique_ptr> headerFields = @@ -145,79 +164,83 @@ append_entries_t Agent::appendEntriesRPC ( // Body Builder builder; - builder.add("term", Value(term())); - builder.add("voteGranted", Value( - _constituent.vote(id, t, lastLogIndex, lastLogTerm))); + for (size_t i = 0; i < entries.size(); ++i) { + builder.add ("index", Value(std::to_string(entries.indices[i]))); + builder.add ("query", Value(_state[entries.indices[i]].entry)); + } builder.close(); - + + // Send arangodb::ClusterComm::instance()->asyncRequest ("1", 1, _config.end_points[slave_id], rest::HttpRequest::HTTP_REQUEST_GET, - path.str(), std::make_shared(body), headerFields, - std::make_shared(_agent_callbacks), + path.str(), std::make_shared(builder.toString()), headerFields, + std::make_shared(_agent_callback), 1.0, true); } //query_ret_t write_ret_t Agent::write (query_t const& query) { // Signal auf die _cv if (_constituent.leading()) { // We are leading - if (_spear_head.apply(query)) { // We could apply to spear head? + if (true/*_spear_head.apply(query)*/) { // We could apply to spear head? std::vector indices = // otherwise through - _state.log (query, term(), id(), _config.size()); // Append to my own log - _confirmed[id()]++; - return + _state.log (query, term(), id()); // Append to my own log + { + MUTEX_LOCKER(mutexLocker, _confirmedLock); + _confirmed[id()]++; + } + return write_ret_t(true,id(),indices); // indices } else { throw QUERY_NOT_APPLICABLE; } } else { // We redirect - return query_ret_t(false,_constituent.leaderID()); + return write_ret_t(false,_constituent.leaderID()); } } read_ret_t Agent::read (query_t const& query) const { if (_constituent.leading()) { // We are leading - return _state.read (query); + return read_ret_t(true,_constituent.leaderID());//(query); //TODO: } else { // We redirect return read_ret_t(false,_constituent.leaderID()); } } -void State::run() { +void Agent::run() { + + CONDITION_LOCKER(guard, _cv); + while (!this->isStopping()) { + + _cv.wait(); auto dur = std::chrono::system_clock::now(); - std::vector> work(_config.size()); - + std::vector work(size()); + // Collect all unacknowledged - for (size_t i = 0; i < _size() ++i) { + for (size_t i = 0; i < size(); ++i) { if (i != id()) { - work[i] = _state.collectUnAcked(i); + work[i] = _state.collectFrom(_confirmed[i]); } } - + // (re-)attempt RPCs - for (size_t j = 0; j < _setup.size(); ++j) { + for (size_t j = 0; j < size(); ++j) { if (j != id() && work[j].size()) { - appendEntriesRPC(j, work[j]); + sendAppendEntriesRPC(j, work[j]); } } - + // catch up read db catchUpReadDB(); - // We were too fast?m wait _cvw - if (dur = std::chrono::system_clock::now() - dur < _poll_interval) { - std::this_thread::sleep_for (_poll_interval - dur); - } } } -inline size_t Agent::size() const { - return _config.size(); -} - -void Agent::shutdown() { - // wake up all blocked rest handlers +void Agent::beginShutdown() { + Thread::beginShutdown(); + // Stop callbacks _agent_callback.shutdown(); + // wake up all blocked rest handlers CONDITION_LOCKER(guard, _cv); guard.broadcast(); } diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 33c0c979dd..57ea7a5c82 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -36,7 +36,6 @@ namespace arangodb { namespace consensus { class Agent : public arangodb::Thread { - // We need to asynchroneously append entries public: @@ -110,28 +109,43 @@ public: */ read_ret_t read (query_t const&) const; + /** + * @brief Received by followers to replicate log entries (§5.3); + * also used as heartbeat (§5.2). + */ + bool 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 appendEntries (term_t, id_t, index_t, term_t, index_t, - query_t const&); - + 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 (); + void run () override final; + void beginShutdown () override; /** * @brief Report appended entries from AgentCallback */ - void reportIn (id_t id, std::vector idx); + void reportIn (id_t id, index_t idx); /** * @brief Wait for slaves to confirm appended entries */ - bool waitFor (std::vector entries, duration_t timeout = duration_t(2.0)); + bool waitFor (index_t last_entry, duration_t timeout = duration_t(2.0)); + + /** + * @brief Convencience size of agency + */ + size_t size() const; + + void catchUpReadDB(); private: @@ -140,7 +154,6 @@ private: config_t _config; /**< @brief Command line arguments */ std::atomic _last_commit_index; - index_t _last_commit_index_tmp; arangodb::Mutex _uncommitedLock; @@ -150,11 +163,13 @@ private: AgentCallback _agent_callback; arangodb::basics::ConditionVariable _cv; // agency callbacks - arangodb::basics::ConditionVariable _cv_rest; // rest handler + arangodb::basics::ConditionVariable _rest_cv; // rest handler + std::atomic _stopping; std::vector _confirmed; + arangodb::Mutex _confirmedLock; /**< @brief Mutex for modifying _confirmed */ }; diff --git a/arangod/Agency/AgentCallback.cpp b/arangod/Agency/AgentCallback.cpp index 295c9e880d..f3052b8ab5 100644 --- a/arangod/Agency/AgentCallback.cpp +++ b/arangod/Agency/AgentCallback.cpp @@ -9,11 +9,11 @@ AgentCallback::AgentCallback() : _agent(0) {} AgentCallback::AgentCallback(Agent* agent) : _agent(agent) {} -void AgentCallback::shutdown() { +void AgentCallbacbk::shutdown() { _agent = 0; } -bool AgentCallback::operator()(ClusterCommResult* res) { +bool AgentCallback::operator()(arangodb::ClusterCommResult* res) { if (res->status == CL_COMM_RECEIVED) { id_t agent_id; diff --git a/arangod/Agency/AgentCallback.h b/arangod/Agency/AgentCallback.h index 0ea91b526d..fd08f4a100 100644 --- a/arangod/Agency/AgentCallback.h +++ b/arangod/Agency/AgentCallback.h @@ -21,24 +21,24 @@ /// @author Kaveh Vahedipour //////////////////////////////////////////////////////////////////////////////// -#ifndef __ARANGODB_CONSENSUS_AGENT__ -#define __ARANGODB_CONSENSUS_AGENT__ +#ifndef __ARANGODB_CONSENSUS_AGENT_CALLBACK__ +#define __ARANGODB_CONSENSUS_AGENT_CALLBACK__ #include "Cluster/ClusterComm.h" -class Agent; - namespace arangodb { namespace consensus { +class Agent; + class AgentCallback : public arangodb::ClusterCommCallback { public: AgentCallback(); - AgentCallback(Agent* agent); + explicit AgentCallback(Agent* agent); - bool operator ()(ClusterCommResult*); + virtual bool operator ()(arangodb::ClusterCommResult*); void shutdown(); diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index 6d54f24fc6..ee55cdadad 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -34,11 +34,11 @@ State::State() { State::~State() {} State::configure (size_t size) { - _log.push_back(log_t (0, 0, 0, ""); + _log.push_back(log_t (0, 0, 0, "")); } //Leader -std::vector State::log (query_t const& query, term_t term, id_t lid, size_t size) { +std::vector State::log (query_t const& query, term_t term, id_t lid) { MUTEX_LOCKER(mutexLocker, _logLock); std::vector idx; Builder builder; @@ -56,13 +56,13 @@ std::vector State::log (query_t const& query, term_t term, id_t lid, si } //Follower -void State::log (query_t const& query, std::vector cont& idx, term_t term, id_t lid, size_t size) { +void State::log (std::string const& query, index_t index, term_t term, id_t lid) { MUTEX_LOCKER(mutexLocker, _logLock); Builder builder; for (size_t i = 0; i < query->slice().length()) { - _log.push_back(idx[i], term, lid, query.toString(), std::vector(size)); + _log.push_back(index, term, lid, query.toString()); builder.add("query", qyery->Slice()); // query - builder.add("idx", Value(idx[i])); // log index + builder.add("idx", Value(index)); // log index builder.add("term", Value(term)); // term builder.add("leaderID", Value(lid)); // leader id builder.close(); @@ -70,17 +70,7 @@ void State::log (query_t const& query, std::vector cont& idx, term_t te save (builder.slice()); } -void State::log (query_t const& query, index_t idx, term_t term, id_t lid, size_t size) { - MUTEX_LOCKER(mutexLocker, _logLock); - _log.push_back(idx, term, lid, query.toString(), std::vector(size)); -} - -void State::confirm (id_t id, index_t index) { - MUTEX_LOCKER(mutexLocker, _logLock); - _log[index].ack[id] = true; -} - -bool findit (index_t index, term_t term) { +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 @@ -99,32 +89,31 @@ bool findit (index_t index, term_t term) { return false; } -collect_ret_t collectUnacked (id_t id) { - // Collect all unacknowledged +log const& State::operator[](index_t index) const { + MUTEX_LOCKER(mutexLocker, _logLock); + return _log[index]; +} + +collect_ret_t State::collectFrom (index_t index) { + // Collect all from index on MUTEX_LOCKER(mutexLocker, _logLock); std::vector work; - bool found_first = false; id_t prev_log_term; index_t prev_log_index; - for (size_t i = 0; i < _log.end(); ++i) { - if (!_log[i].ack[id]) { - work.push_back(_log[i].index); - if (!found_first) { - prev_log_term = _log[i-1].term; - prev_log_index = _log[i-1].index; - found_first = true; - } - } + prev_log_term = _log[index-1].term; + prev_log_index = _log[index-1].index; + for (size_t i = index; i < _log.end(); ++i) { + work.push_back(_log[i].index); } return collect_ret_t(prev_log_index, prev_log_term, work); } -bool save (std::string const& ep) { +bool State::save (std::string const& ep) { // Persist to arango db // AQL votedFor, lastCommit }; -load_ret_t load (std::string const& ep) { +load_ret_t State::load (std::string const& ep) { // Read all from arango db return load_ret_t (currentTerm, votedFor) }; diff --git a/arangod/Agency/State.h b/arangod/Agency/State.h index 267bb3ffbd..58cebc1085 100644 --- a/arangod/Agency/State.h +++ b/arangod/Agency/State.h @@ -75,13 +75,13 @@ public: /** * @brief Log entries (leader) */ - std::vector log (query_t const& query, term_t term, id_t lid, size_t size); + std::vector log (query_t const& query, term_t term, id_t lid); /** - * @brief + * @brief Log entry follower */ - void log (query_t const& query, index_t, term_t term, id_t lid, size_t size); - + void log (std::string const& query, index_t, term_t term, id_t lid); + /** * @brief Save currentTerm, votedFor, log entries */ @@ -98,14 +98,13 @@ public: bool findit (index_t index, term_t term) const; /** - * @brief Confirm entry index for agent id + * @brief Collect all from index on */ - void confirm (id_t id, index_t idx); + collect_ret_t collectFrom (index_t index); - /** - * @brief Collect all unacknowledged for agent id - */ - collect_ret_t collectUnacked (id_t id); + log_t const& operator[](index_t t) { + + } private: diff --git a/arangod/RestHandler/RestAgencyPrivHandler.cpp b/arangod/RestHandler/RestAgencyPrivHandler.cpp index 401bf27c22..ae2cff23af 100644 --- a/arangod/RestHandler/RestAgencyPrivHandler.cpp +++ b/arangod/RestHandler/RestAgencyPrivHandler.cpp @@ -89,9 +89,8 @@ HttpHandler::status_t RestAgencyPrivHandler::execute() { generateError(HttpResponse::NOT_FOUND,404); // nothing return HttpHandler::status_t(HANDLER_DONE); } - } - + result.close(); VPackSlice s = result.slice(); generateResult(s); From fa66ba2ffc0d8ab3ba27707848ef726c6bd86898 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Tue, 1 Mar 2016 16:45:34 +0100 Subject: [PATCH 31/61] compiles again --- arangod/Agency/AgencyCommon.h | 9 ++- arangod/Agency/Agent.cpp | 38 ++++++++----- arangod/Agency/Agent.h | 22 +++++++- arangod/Agency/AgentCallback.cpp | 14 ++--- arangod/Agency/AgentCallback.h | 2 +- arangod/Agency/Constituent.cpp | 10 ++-- arangod/Agency/State.cpp | 51 +++++++++-------- arangod/Agency/State.h | 41 +++++++------- arangod/CMakeLists.txt | 2 +- arangod/RestHandler/RestAgencyHandler.cpp | 12 ++-- arangod/RestHandler/RestAgencyHandler.h | 2 +- arangod/RestHandler/RestAgencyPrivHandler.cpp | 56 +++++++++++++++---- arangod/RestHandler/RestAgencyPrivHandler.h | 21 ++++++- 13 files changed, 185 insertions(+), 95 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 810a18a22a..4945570fec 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -126,8 +126,7 @@ struct log_t { id_t leaderId; std::string entry; milliseconds timestamp; - log_t (index_t idx, term_t t, id_t lid, std::string const& e, - std::vector const& r) : + log_t (index_t idx, term_t t, id_t lid, std::string const& e) : index(idx), term(t), leaderId(lid), entry(e), timestamp ( duration_cast(system_clock::now().time_since_epoch())) {} }; @@ -152,6 +151,12 @@ struct collect_ret_t { 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) {} +}; + }} inline arangodb::LoggerStream& operator<< ( diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index ec51a35d76..ca1584a754 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -27,6 +27,7 @@ #include using namespace arangodb::velocypack; + namespace arangodb { namespace consensus { @@ -34,7 +35,6 @@ Agent::Agent () : Thread ("Agent"), _stopping(false) {} Agent::Agent (config_t const& config) : _config(config), Thread ("Agent") { _constituent.configure(this); - _state.load(); // read persistent database } id_t Agent::id() const { return _config.id;} @@ -53,14 +53,10 @@ inline size_t Agent::size() const { return _config.size(); } -query_t Agent::requestVote(term_t t, id_t id, index_t lastLogIndex, +priv_rpc_ret_t Agent::requestVote(term_t t, id_t id, index_t lastLogIndex, index_t lastLogTerm) { - Builder builder; - builder.add("term", Value(term())); - builder.add("voteGranted", Value( - _constituent.vote(id, t, lastLogIndex, lastLogTerm))); - builder.close(); - return std::make_shared(builder); + return priv_rpc_ret_t( + _constituent.vote(id, t, lastLogIndex, lastLogTerm),this->term()); } Config const& Agent::config () const { @@ -87,10 +83,10 @@ arangodb::LoggerStream& operator<< (arangodb::LoggerStream& l, Agent const& a) { void Agent::catchUpReadDB() {}; // TODO bool Agent::waitFor (index_t index, duration_t timeout) { - + CONDITION_LOCKER(guard, _rest_cv); auto start = std::chrono::system_clock::now(); - + while (true) { _rest_cv.wait(); @@ -103,8 +99,9 @@ bool Agent::waitFor (index_t index, duration_t timeout) { if (std::chrono::system_clock::now() - start > timeout) { return false; } - if (_last_commit_index > index) + if (_last_commit_index > index) { return true; + } } // We should never get here TRI_ASSERT(false); @@ -128,7 +125,7 @@ void Agent::reportIn (id_t id, index_t index) { _rest_cv.broadcast(); } -bool Agent::recvAppendEntriesRPC (term_t term, id_t leaderId, index_t prevIndex, +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 @@ -146,7 +143,7 @@ bool Agent::recvAppendEntriesRPC (term_t term, id_t leaderId, index_t prevIndex, queries->slice()[i+1].getUInt(), term, leaderId); } - return true; + return priv_rpc_ret_t(true, this->term()); } append_entries_t Agent::sendAppendEntriesRPC ( @@ -175,7 +172,7 @@ append_entries_t Agent::sendAppendEntriesRPC ( ("1", 1, _config.end_points[slave_id], rest::HttpRequest::HTTP_REQUEST_GET, path.str(), std::make_shared(builder.toString()), headerFields, - std::make_shared(_agent_callback), + std::make_shared(this), 1.0, true); } @@ -245,5 +242,18 @@ void Agent::beginShutdown() { guard.broadcast(); } +inline bool Agent::lead () { + rebuildDBs(); +} + +inline bool Agent::rebuildDBs() { + MUTEX_LOCKER(mutexLocker, _dbLock); + return true; +} + +inline log_t const& Agent::lastLog() const { + return _state.lastLog(); +} + }} diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 57ea7a5c82..1649c78ffd 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -67,7 +67,7 @@ public: /** * @brief Vote request */ - query_t requestVote(term_t , id_t, index_t, index_t); + priv_rpc_ret_t requestVote(term_t , id_t, index_t, index_t); /** * @brief Provide configuration @@ -99,6 +99,8 @@ public: */ id_t leaderID () const; + bool lead (); + /** * @brief Attempt write */ @@ -113,7 +115,7 @@ public: * @brief Received by followers to replicate log entries (§5.3); * also used as heartbeat (§5.2). */ - bool recvAppendEntriesRPC (term_t term, id_t leaderId, index_t prevIndex, + priv_rpc_ret_t recvAppendEntriesRPC (term_t term, id_t leaderId, index_t prevIndex, term_t prevTerm, index_t lastCommitIndex, query_t const& queries); /** @@ -145,8 +147,21 @@ public: */ 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 */ @@ -170,7 +185,8 @@ private: std::vector _confirmed; arangodb::Mutex _confirmedLock; /**< @brief Mutex for modifying _confirmed */ - + arangodb::Mutex _dbLock; + }; } diff --git a/arangod/Agency/AgentCallback.cpp b/arangod/Agency/AgentCallback.cpp index f3052b8ab5..adb6c25bd3 100644 --- a/arangod/Agency/AgentCallback.cpp +++ b/arangod/Agency/AgentCallback.cpp @@ -9,7 +9,7 @@ AgentCallback::AgentCallback() : _agent(0) {} AgentCallback::AgentCallback(Agent* agent) : _agent(agent) {} -void AgentCallbacbk::shutdown() { +void AgentCallback::shutdown() { _agent = 0; } @@ -18,20 +18,20 @@ bool AgentCallback::operator()(arangodb::ClusterCommResult* res) { if (res->status == CL_COMM_RECEIVED) { id_t agent_id; std::vector idx; - std::shared_ptr< VPackBuilder > builder = res->result->getBodyVelocyPack(); + std::shared_ptr builder = res->result->getBodyVelocyPack(); if (builder->hasKey("agent_id")) { agent_id = builder->getKey("agent_id").getUInt(); } if (builder->hasKey("indices")) { - Slice indices = builder->getKey("indices"); - if (indices.isArray()) { - for (size_t i = 0; i < indices.length(); ++i) { - idx.push_back(indices[i].getUInt()); + 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); + _agent->reportIn (agent_id, idx.back()); } } return true; diff --git a/arangod/Agency/AgentCallback.h b/arangod/Agency/AgentCallback.h index fd08f4a100..1147d8f989 100644 --- a/arangod/Agency/AgentCallback.h +++ b/arangod/Agency/AgentCallback.h @@ -38,7 +38,7 @@ public: AgentCallback(); explicit AgentCallback(Agent* agent); - virtual bool operator ()(arangodb::ClusterCommResult*); + virtual bool operator()(arangodb::ClusterCommResult*); void shutdown(); diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index 1e66c883cb..b5cb1b73f2 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -111,7 +111,7 @@ size_t Constituent::notifyAll () { bool Constituent::vote(term_t term, id_t leaderId, index_t prevLogIndex, term_t prevLogTerm) { - if (id == _id) { // Won't vote for myself should never happen. + if (leaderId == _id) { // Won't vote for myself should never happen. return false; // TODO: Assertion? } else { if (term > _term) { // Candidate with higher term: ALWAYS turn follower if not already @@ -128,7 +128,7 @@ bool Constituent::vote(term_t term, id_t leaderId, index_t prevLogIndex, term_t break; } _cast = true; // Note that I voted this time around. - _leader_id = id; // The guy I voted for I assume leader. + _leader_id = leaderId; // The guy I voted for I assume leader. follow (term); return true; } else { // Myself running or leading @@ -158,8 +158,8 @@ void Constituent::callElection() { std::stringstream path; path << "/_api/agency/requestVote?term=" << _term << "&candidateId=" << _id - << "&lastLogIndex=" << _agent.lastLogIndex() << "&lastLogTerm=" - << _agent.LastLogTerm(); + << "&lastLogIndex=" << _agent->lastLog().index << "&lastLogTerm=" + << _agent->lastLog().term; for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { // Ask everyone for their vote if (i != _id) { @@ -171,7 +171,7 @@ void Constituent::callElection() { } } - std::this_thread::sleep_for(.9*_agent->config().min_ping); // Wait timeout + std::this_thread::sleep_for(duration_t(.9*_agent->config().min_ping)); // Wait timeout for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { // Collect votes if (i != _id) { diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index ee55cdadad..54ff1cbd8e 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -27,47 +27,45 @@ #include using namespace arangodb::consensus; +using namespace arangodb::velocypack; State::State() { + load(); + if (!_log.size()) + _log.push_back(log_t(index_t(0), term_t(0), id_t(0), std::string())); } State::~State() {} -State::configure (size_t size) { - _log.push_back(log_t (0, 0, 0, "")); -} - //Leader std::vector State::log (query_t const& query, term_t term, id_t lid) { MUTEX_LOCKER(mutexLocker, _logLock); std::vector idx; Builder builder; - for (size_t i = 0; i < query->slice().length()) { - idx.push_back(_log.end().index+1); - _log.push_back(idx[i], term, lid, query.toString()); - builder.add("query", qyery->Slice()); + for (size_t i = 0; i < query->slice().length(); ++i) { + idx.push_back(_log.back().index+1); + _log.push_back(log_t(idx[i], term, lid, query->slice()[i].toString())); + builder.add("query", query->slice()[i]); builder.add("idx", Value(idx[i])); builder.add("term", Value(term)); builder.add("leaderID", Value(lid)); builder.close(); } - save (builder.slice()); + save (builder); // persist return idx; } //Follower void State::log (std::string const& query, index_t index, term_t term, id_t lid) { MUTEX_LOCKER(mutexLocker, _logLock); + _log.push_back(log_t(index, term, lid, query)); Builder builder; - for (size_t i = 0; i < query->slice().length()) { - _log.push_back(index, term, lid, query.toString()); - builder.add("query", qyery->Slice()); // query - builder.add("idx", Value(index)); // log index - builder.add("term", Value(term)); // term - builder.add("leaderID", Value(lid)); // leader id - builder.close(); - } - save (builder.slice()); + builder.add("query", Value(query)); // query + builder.add("idx", Value(index)); // log index + builder.add("term", Value(term)); // term + builder.add("leaderID", Value(lid)); // leader id + builder.close(); + save (builder); } bool State::findit (index_t index, term_t term) { @@ -89,11 +87,16 @@ bool State::findit (index_t index, term_t term) { return false; } -log const& State::operator[](index_t index) const { +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); @@ -102,20 +105,22 @@ collect_ret_t State::collectFrom (index_t index) { index_t prev_log_index; prev_log_term = _log[index-1].term; prev_log_index = _log[index-1].index; - for (size_t i = index; i < _log.end(); ++i) { + 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::save (std::string const& ep) { +bool State::save (arangodb::velocypack::Builder const&) { // Persist to arango db // AQL votedFor, lastCommit + return true; }; -load_ret_t State::load (std::string const& ep) { +bool State::load () { // Read all from arango db - return load_ret_t (currentTerm, votedFor) + //return load_ret_t (currentTerm, votedFor) + return true; }; diff --git a/arangod/Agency/State.h b/arangod/Agency/State.h index 58cebc1085..7475946f7e 100644 --- a/arangod/Agency/State.h +++ b/arangod/Agency/State.h @@ -62,11 +62,6 @@ public: */ virtual ~State(); - /** - * @brief Configure this state machine - */ - void configure (size_t size); - /** * @brief Append log entry */ @@ -82,33 +77,39 @@ public: */ void log (std::string const& query, index_t, term_t term, id_t lid); - /** - * @brief Save currentTerm, votedFor, log entries - */ - bool save (std::string const& ep = "tcp://localhost:8529"); - - /** - * @brief Load persisted data from above or start with empty log - */ - bool load (std::string const& ep = "tcp://localhost:8529"); - /** * @brief Find entry at index with term */ - bool findit (index_t index, term_t term) const; + bool findit (index_t index, term_t term); /** * @brief Collect all from index on */ collect_ret_t collectFrom (index_t index); - log_t const& operator[](index_t t) { - - } + /** + * @brief log entry at index i + */ + log_t const& operator[](index_t) const; + + /** + * @brief last log entry + */ + log_t const& lastLog () const; private: - arangodb::Mutex _logLock; /**< @brief Mutex for modifying _log */ + /** + * @brief Save currentTerm, votedFor, log entries + */ + bool save (arangodb::velocypack::Builder const&); + + /** + * @brief Load persisted data from above or start with empty log + */ + bool load (); + + mutable arangodb::Mutex _logLock; /**< @brief Mutex for modifying _log */ std::vector _log; /**< @brief State entries */ }; diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index 11f2c13b2d..b2e8bdd32f 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -69,10 +69,10 @@ add_executable(${BIN_ARANGOD} Actions/RestActionHandler.cpp Actions/actions.cpp Agency/Agent.cpp - Agency/AgentCallback.cpp Agency/ApplicationAgency.cpp Agency/Constituent.cpp Agency/State.cpp + Agency/AgentCallback.cpp ApplicationServer/ApplicationFeature.cpp ApplicationServer/ApplicationServer.cpp Aql/Aggregator.cpp diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index 39611444d9..21bce51569 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -64,7 +64,7 @@ inline HttpHandler::status_t RestAgencyHandler::reportTooManySuffices () { return HttpHandler::status_t(HANDLER_DONE); } -inline HttpHandler::status_t RestAgencyHandler::unknownMethod () { +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); @@ -83,21 +83,23 @@ inline HttpHandler::status_t RestAgencyHandler::handleReadWrite () { if (_request->suffix()[0] == "write") { write_ret_t ret = _agent->write (_request->toVelocyPack(&options)); accepted = ret.accepted; - _agent->waitFor (ret.indices); // Wait for confirmation + _agent->waitFor (ret.indices.back()); // Wait for confirmation (last entry is enough) } else { read_ret_t ret = _agent->read(_request->toVelocyPack(&options)); accepted = ret.accepted; ret.result->close(); generateResult(ret.result->slice()); } - + + /* if (!accepted) { // We accepted the request - ret.result->close(); - generateResult(ret.result->slice()); + //ret.result->close(); + //generateResult(ret.result->slice()); } else { // We redirect the request _response->setHeader("Location", _agent->config().endpoints[ret.redirect]); generateError(HttpResponse::TEMPORARY_REDIRECT,307); } + */ return HttpHandler::status_t(HANDLER_DONE); } diff --git a/arangod/RestHandler/RestAgencyHandler.h b/arangod/RestHandler/RestAgencyHandler.h index 623c232edb..62e4282ace 100644 --- a/arangod/RestHandler/RestAgencyHandler.h +++ b/arangod/RestHandler/RestAgencyHandler.h @@ -47,7 +47,7 @@ class RestAgencyHandler : public arangodb::RestBaseHandler { status_t reportErrorEmptyRequest() ; status_t reportTooManySuffices() ; - status_t unknownMethod() ; + status_t reportUnknownMethod() ; status_t redirect(id_t leader_id) ; status_t handleReadWrite() ; diff --git a/arangod/RestHandler/RestAgencyPrivHandler.cpp b/arangod/RestHandler/RestAgencyPrivHandler.cpp index ae2cff23af..3704082440 100644 --- a/arangod/RestHandler/RestAgencyPrivHandler.cpp +++ b/arangod/RestHandler/RestAgencyPrivHandler.cpp @@ -31,6 +31,8 @@ #include #include +#include + #include "Basics/Logger.h" using namespace arangodb; @@ -64,9 +66,8 @@ inline HttpHandler::status_t RestAgencyPrivHandler::reportTooManySuffices () { return HttpHandler::status_t(HANDLER_DONE); } -inline HttpHandler::status_t RestAgencyPrivHandler::unknownMethod () { - LOG(WARN) << "Too many suffixes. Agency public interface takes one path."; - generateError(HttpResponse::NOT_FOUND,404); +inline HttpHandler::status_t RestAgencyPrivHandler::reportBadQuery () { + generateError(HttpResponse::BAD,400); return HttpHandler::status_t(HANDLER_DONE); } @@ -75,22 +76,48 @@ HttpHandler::status_t RestAgencyPrivHandler::execute() { VPackBuilder result; result.add(VPackValue(VPackValueType::Object)); arangodb::velocypack::Options opts; - if (_request->suffix().size() == 0) { // empty request - reportErrorEmptyRequest(); + return reportErrorEmptyRequest(); } else if (_request->suffix().size() > 1) { // request too long - reportTooManySuffices(); + return reportTooManySuffices(); } else { - if (_request->suffix()[0] == "appendEntries") { // replication - _agent->appendEntries (_request->toVelocyPack()); - } else if (_request->suffix()[0] == "requestVote") { // election - _agent->requestVote (_request->toVelocyPack()); + term_t term, prevLogTerm; + id_t id; // leaderId for appendEntries, cadidateId for requestVote + index_t prevLogIndex, leaderCommit; + if (_request->suffix()[0] == "appendEntries") { // appendEntries + 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); + result.add("term", VPackValue(ret.term)); + result.add("voteGranted", VPackValue(ret.success)); + } } else { - generateError(HttpResponse::NOT_FOUND,404); // nothing + generateError(HttpResponse::NOT_FOUND,404); // nothing else here return HttpHandler::status_t(HANDLER_DONE); } } - result.close(); VPackSlice s = result.slice(); generateResult(s); @@ -101,3 +128,8 @@ HttpHandler::status_t RestAgencyPrivHandler::execute() { } + +/* + term_t term, id_t leaderId, index_t prevIndex, + term_t prevTerm, index_t lastCommitIndex, query_t const& queries + */ diff --git a/arangod/RestHandler/RestAgencyPrivHandler.h b/arangod/RestHandler/RestAgencyPrivHandler.h index 17231d91e0..70e86d03c3 100644 --- a/arangod/RestHandler/RestAgencyPrivHandler.h +++ b/arangod/RestHandler/RestAgencyPrivHandler.h @@ -46,9 +46,28 @@ class RestAgencyPrivHandler : public arangodb::RestBaseHandler { private: + template 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 unknownMethod() ; + status_t reportBadQuery(); consensus::Agent* _agent; From 5f8a42fc8e206c23ae6e79e259ba84592210d0d2 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Thu, 3 Mar 2016 03:59:39 +0100 Subject: [PATCH 32/61] builds with new store --- arangod/Agency/Agent.cpp | 13 +- arangod/Agency/Agent.h | 16 +- arangod/Agency/Constituent.cpp | 41 +- arangod/Agency/Constituent.h | 8 + arangod/Agency/Store.h | 2764 ++--------------- arangod/RestHandler/RestAgencyPrivHandler.cpp | 15 +- 6 files changed, 316 insertions(+), 2541 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index ca1584a754..2780a83d28 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -75,11 +75,6 @@ id_t Agent::leaderID () const { return _constituent.leaderID(); } -arangodb::LoggerStream& operator<< (arangodb::LoggerStream& l, Agent const& a) { - a.print(l); - return l; -} - void Agent::catchUpReadDB() {}; // TODO bool Agent::waitFor (index_t index, duration_t timeout) { @@ -148,7 +143,7 @@ priv_rpc_ret_t Agent::recvAppendEntriesRPC (term_t term, id_t leaderId, index_t 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=" @@ -242,16 +237,16 @@ void Agent::beginShutdown() { guard.broadcast(); } -inline bool Agent::lead () { +bool Agent::lead () { rebuildDBs(); } -inline bool Agent::rebuildDBs() { +bool Agent::rebuildDBs() { MUTEX_LOCKER(mutexLocker, _dbLock); return true; } -inline log_t const& Agent::lastLog() const { +log_t const& Agent::lastLog() const { return _state.lastLog(); } diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 1649c78ffd..a584b18c7b 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -30,8 +30,6 @@ #include "State.h" #include "Store.h" -#include - namespace arangodb { namespace consensus { @@ -172,9 +170,9 @@ private: arangodb::Mutex _uncommitedLock; - store _spear_head; - store _read_db; - + Store _spear_head; + Store _read_db; + AgentCallback _agent_callback; arangodb::basics::ConditionVariable _cv; // agency callbacks @@ -189,12 +187,6 @@ private: }; -} - -LoggerStream& operator<< (LoggerStream&, arangodb::consensus::Agent const&); - -} - - +}} #endif diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index b5cb1b73f2..f55d041788 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -32,6 +32,7 @@ using namespace arangodb::consensus; using namespace arangodb::rest; +using namespace arangodb::velocypack; void Constituent::configure(Agent* agent) { _agent = agent; @@ -89,28 +90,48 @@ 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 const& Constituent::end_points() const { + return _agent->config().end_points; +} + size_t Constituent::notifyAll () { // Last process notifies everyone - std::string body; std::unique_ptr> headerFields = std::make_unique >(); std::vector results(_agent->config().end_points.size()); std::stringstream path; - path << "/_api/agency/notify?agency_size=" << _id << "&term=" << _term; - for (size_t i = 0; i < _agent->config().end_points.size(); ++i) { + path << "/_api/agency/notifyAll?term=" << _term << "&agencyId=" << _id; + + // Body contains ids and endpoints + Builder builder; + for (auto const& i : end_points()) + builder.add("endpoint", Value(i)); + builder.close(); + + // Send request to all but myself + for (size_t i = 0; i < size(); ++i) { if (i != _id) { - results[i] = arangodb::ClusterComm::instance()->asyncRequest("1", 1, - _agent->config().end_points[i], rest::HttpRequest::HTTP_REQUEST_GET, - path.str(), std::make_shared(body), headerFields, nullptr, - .75*_agent->config().min_ping, true); - LOG(WARN) << _agent->config().end_points[i]; + results[i] = arangodb::ClusterComm::instance()->asyncRequest( + "1", 1, _agent->config().end_points[i], + rest::HttpRequest::HTTP_REQUEST_GET, path.str(), + std::make_shared(builder.toString()), headerFields, nullptr, + 0.0, true); } } } -bool Constituent::vote(term_t term, id_t leaderId, index_t prevLogIndex, term_t prevLogTerm) { +bool Constituent::vote( + term_t term, id_t leaderId, index_t prevLogIndex, term_t prevLogTerm) { if (leaderId == _id) { // Won't vote for myself should never happen. return false; // TODO: Assertion? } else { @@ -180,7 +201,7 @@ void Constituent::callElection() { if (res.status == CL_COMM_SENT) { // Request successfully sent res = arangodb::ClusterComm::instance()->wait("1", 1, results[i].operationID, "1"); - std::shared_ptr< arangodb::velocypack::Builder > body = res.result->getBodyVelocyPack(); + std::shared_ptr body = res.result->getBodyVelocyPack(); if (body->isEmpty()) { continue; } else { diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h index 03b3b8f4fa..dd2a484540 100644 --- a/arangod/Agency/Constituent.h +++ b/arangod/Agency/Constituent.h @@ -96,8 +96,16 @@ public: */ void follow(term_t); + /** + * @brief Agency size + */ + size_t size() const; + private: + std::vector const& end_points() const; + std::string const& end_point(id_t) const; + /** * @brief Run for leadership */ diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index 4969bbb7a8..457b2b9741 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -24,2538 +24,294 @@ #ifndef __ARANGODB_CONSENSUS_STORE__ #define __ARANGODB_CONSENSUS_STORE__ +#include +#include +#include +#include #include +#include +#include #include -#include -#include -#include -#include -#include -#include +#include +namespace arangodb { +namespace consensus { -/** - * @brief Node - */ -template -class store_node_ { - public: - store_node_(); - store_node_(const T&); - - store_node_ *parent; - store_node_ *first_child, *last_child; - store_node_ *prev_sibling, *next_sibling; - T data; -}; +template struct TypeTraits { + static bool supported () {return false;} +}; +template<> struct TypeTraits { + static bool supported () {return false;} +}; +template<> struct TypeTraits { + static bool supported () {return false;} +}; +template<> struct TypeTraits { + static bool supported () {return false;} +}; +template<> struct TypeTraits { + static bool supported () {return false;} +}; template -store_node_::store_node_() - : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0) { +using Type = typename std::decay::type>::type; + +struct Any { + + bool is_null() const { return !_base_ptr; } + bool not_null() const { return _base_ptr; } + + template Any(S&& value) + : _base_ptr(new Derived>(std::forward(value))) {} + + template bool is() const { + typedef Type T; + auto derived = dynamic_cast*> (_base_ptr); + return derived; + } + + template Type& as() const { + typedef Type T; + auto derived = dynamic_cast*> (_base_ptr); + if (!derived) + throw std::bad_cast(); + return derived->value; + } + + template operator S() { + return as>(); + } + + Any() : _base_ptr(nullptr) {} + + Any(Any& that) : _base_ptr(that.clone()) {} + + Any(Any&& that) : _base_ptr(that._base_ptr) { + that._base_ptr = nullptr; + } + + Any(const Any& that) : _base_ptr(that.clone()) {} + + Any(const Any&& that) : _base_ptr(that.clone()) {} + + Any& operator=(const Any& a) { + if (_base_ptr == a._base_ptr) + return *this; + auto old__base_ptr = _base_ptr; + _base_ptr = a.clone(); + if (old__base_ptr) + delete old__base_ptr; + return *this; + } + + Any& operator=(Any&& a) { + if (_base_ptr == a._base_ptr) + return *this; + std::swap(_base_ptr, a._base_ptr); + return *this; + } + + ~Any() { + if (_base_ptr) + delete _base_ptr; + } + + friend std::ostream& operator<<(std::ostream& os, const Any& a) { + try { + os << a.as(); + } catch (std::bad_cast const&) { + try { + os << a.as(); + } catch (std::bad_cast const&) { + try { + os << "\"" << a.as() << "\""; + } catch (std::bad_cast const& e) { + throw e; + } + } + } + return os; + } + +private: + + struct Base { + virtual ~Base() {} + virtual Base* clone() const = 0; + }; + + template struct Derived : Base { + template Derived(S&& value) : value(std::forward(value)) { } + T value; + Base* clone() const { return new Derived(value); } + }; + + Base* clone() const { + if (_base_ptr) + return _base_ptr->clone(); + else + return nullptr; + } + + Base* _base_ptr; + +}; + +static inline std::vector +split (std::string str, const std::string& dlm) { + std::vector sv; + std::regex slashes("/+"); + str = std::regex_replace (str, slashes, "/"); + size_t start = (str.find('/') == 0) ? 1:0, end = 0; + while (end != std::string::npos) { + end = str.find (dlm, start); + sv.push_back(str.substr(start, (end == std::string::npos) ? std::string::npos : end - start)); + start = ((end > (std::string::npos - dlm.size())) ? std::string::npos : end + dlm.size()); } + return sv; +} -template -store_node_::store_node_(const T& val) - : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0), - data(val) { } +enum NodeType {NODE, LEAF}; -/** - * @brief Store - */ -template >> -class store { - protected: - typedef store_node_ store_node; - public: - - typedef T value_type; +inline std::ostream& operator<< (std::ostream& os, std::vector const& sv) { + for (auto const& i : sv) + os << i << " "; + os << std::endl; + return os; +} - class iterator_base; - class pre_order_iterator; - class post_order_iterator; - class sibling_iterator; - class leaf_iterator; +enum NODE_EXPECTION {PATH_NOT_FOUND}; - store(); // empty constructor - store(const T&); // constructor setting given element as head - store(const iterator_base&); - store(const store&); // copy constructor - store(store&&); // move constructor - ~store(); - store& operator=(const store&); // copy assignment - store& operator=(store&&); // move assignment +class Node { +public: - class iterator_base { - public: - typedef T value_type; - typedef T* pointer; - typedef T& reference; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - typedef std::bidirectional_iterator_tag iterator_category; + typedef std::vector PathType; + typedef std::map> Children; - iterator_base(); - iterator_base(store_node *); + Node (std::string const& name) : _name(name), _parent(nullptr), _value("") {} - T& operator*() const; - T* operator->() const; + ~Node () {} - /// When called, the next increment/decrement skips children of this node. - void skip_children(); - void skip_children(bool skip); - /// Number of children of the node pointed to by the iterator. - unsigned int number_of_children() const; + std::string const& name() const {return _name;} - sibling_iterator begin() const; - sibling_iterator end() const; + template + Node& operator= (T const& t) { // Assign value (become leaf) + _children.clear(); + _value = t; + return *this; + } - store_node *node; - protected: - bool skip_current_children_; - }; + Node& operator= (Node const& node) { // Assign node + *this = node; + return *this; + } - // Depth-first iterator, first accessing the node, then its children. - class pre_order_iterator : public iterator_base { - public: - pre_order_iterator(); - pre_order_iterator(store_node *); - pre_order_iterator(const iterator_base&); - pre_order_iterator(const sibling_iterator&); + inline NodeType type() const {return _children.size() ? NODE : LEAF;} - bool operator==(const pre_order_iterator&) const; - bool operator!=(const pre_order_iterator&) const; - pre_order_iterator& operator++(); - pre_order_iterator& operator--(); - pre_order_iterator operator++(int); - pre_order_iterator operator--(int); - pre_order_iterator& operator+=(unsigned int); - pre_order_iterator& operator-=(unsigned int); + Node& operator [](std::string name) { + return *_children[name]; + } - pre_order_iterator& next_skip_children(); - }; + bool append (std::string const name, std::shared_ptr const node = nullptr) { + if (node != nullptr) { + _children[name] = node; + } else { + _children[name] = std::make_shared(name); + } + _children[name]->_parent = this; + return true; + } - // Depth-first iterator, first accessing the children, then the node itself. - class post_order_iterator : public iterator_base { - public: - post_order_iterator(); - post_order_iterator(store_node *); - post_order_iterator(const iterator_base&); - post_order_iterator(const sibling_iterator&); + Node& operator ()(std::vector& pv) { + switch (pv.size()) { + case 0: // path empty + return *this; + break; + default: // at least one depth left + auto found = _children.find(pv[0]); + if (found == _children.end()) { + _children[pv[0]] = std::make_shared(pv[0]); + found = _children.find(pv[0]); + found->second->_parent = this; + } + pv.erase(pv.begin()); + return (*found->second)(pv); + break; + } + } + + Node const& operator ()(std::vector& pv) const { + switch (pv.size()) { + case 0: // path empty + return *this; + break; + default: // at least one depth left + auto found = _children.find(pv[0]); + if (found == _children.end()) { + throw PATH_NOT_FOUND; + } + pv.erase(pv.begin()); + return (*found->second)(pv); + break; + } + } + + Node const& operator ()(std::string const& path) const { + PathType pv = split(path,"/"); + return this->operator()(pv); + } - bool operator==(const post_order_iterator&) const; - bool operator!=(const post_order_iterator&) const; - post_order_iterator& operator++(); - post_order_iterator& operator--(); - post_order_iterator operator++(int); - post_order_iterator operator--(int); - post_order_iterator& operator+=(unsigned int); - post_order_iterator& operator-=(unsigned int); + Node& operator ()(std::string const& path) { + PathType pv = split(path,"/"); + return this->operator()(pv); + } - // Set iterator to the first child as deep as possible down the store. - void descend_all(); - }; + Node const& read (std::string const& path) const { + PathType pv = split(path,"/"); + return this->operator()(pv); + } - // Breadth-first iterator, using a queue - class breadth_first_queued_iterator : public iterator_base { - public: - breadth_first_queued_iterator(); - breadth_first_queued_iterator(store_node *); - breadth_first_queued_iterator(const iterator_base&); + Node& write (std::string const& path) { + PathType pv = split(path,"/"); + return this->operator()(pv); + } - bool operator==(const breadth_first_queued_iterator&) const; - bool operator!=(const breadth_first_queued_iterator&) const; - breadth_first_queued_iterator& operator++(); - breadth_first_queued_iterator operator++(int); - breadth_first_queued_iterator& operator+=(unsigned int); + friend std::ostream& operator<<(std::ostream& os, const Node& n) { + Node* 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._value << std::endl; + } + return os; + } - private: - std::queue traversal_queue; - }; - - // The default iterator types throughout the store class. - typedef pre_order_iterator iterator; - typedef breadth_first_queued_iterator breadth_first_iterator; - - // Iterator which traverses only the nodes at a given depth from the root. - class fixed_depth_iterator : public iterator_base { - public: - fixed_depth_iterator(); - fixed_depth_iterator(store_node *); - fixed_depth_iterator(const iterator_base&); - fixed_depth_iterator(const sibling_iterator&); - fixed_depth_iterator(const fixed_depth_iterator&); - - bool operator==(const fixed_depth_iterator&) const; - bool operator!=(const fixed_depth_iterator&) const; - fixed_depth_iterator& operator++(); - fixed_depth_iterator& operator--(); - fixed_depth_iterator operator++(int); - fixed_depth_iterator operator--(int); - fixed_depth_iterator& operator+=(unsigned int); - fixed_depth_iterator& operator-=(unsigned int); - - store_node *top_node; - }; - - // Iterator which traverses only the nodes which are siblings of each other. - class sibling_iterator : public iterator_base { - public: - sibling_iterator(); - sibling_iterator(store_node *); - sibling_iterator(const sibling_iterator&); - sibling_iterator(const iterator_base&); - - bool operator==(const sibling_iterator&) const; - bool operator!=(const sibling_iterator&) const; - sibling_iterator& operator++(); - sibling_iterator& operator--(); - sibling_iterator operator++(int); - sibling_iterator operator--(int); - sibling_iterator& operator+=(unsigned int); - sibling_iterator& operator-=(unsigned int); - - store_node *range_first() const; - store_node *range_last() const; - store_node *parent_; - private: - void set_parent_(); - }; - - // Iterator which traverses only the leaves. - class leaf_iterator : public iterator_base { - public: - leaf_iterator(); - leaf_iterator(store_node *, store_node *top=0); - leaf_iterator(const sibling_iterator&); - leaf_iterator(const iterator_base&); - - bool operator==(const leaf_iterator&) const; - bool operator!=(const leaf_iterator&) const; - leaf_iterator& operator++(); - leaf_iterator& operator--(); - leaf_iterator operator++(int); - leaf_iterator operator--(int); - leaf_iterator& operator+=(unsigned int); - leaf_iterator& operator-=(unsigned int); - private: - store_node *top_node; - }; - - inline pre_order_iterator begin() const; - inline pre_order_iterator end() const; - post_order_iterator begin_post() const; - post_order_iterator end_post() const; - fixed_depth_iterator begin_fixed(const iterator_base&, unsigned int) const; - fixed_depth_iterator end_fixed(const iterator_base&, unsigned int) const; - breadth_first_queued_iterator begin_breadth_first() const; - breadth_first_queued_iterator end_breadth_first() const; - sibling_iterator begin(const iterator_base&) const; - sibling_iterator end(const iterator_base&) const; - leaf_iterator begin_leaf() const; - leaf_iterator end_leaf() const; - leaf_iterator begin_leaf(const iterator_base& top) const; - leaf_iterator end_leaf(const iterator_base& top) const; - - template static iter parent(iter); - template static iter previous_sibling(iter); - template static iter next_sibling(iter); - template iter next_at_same_depth(iter) const; - - void clear(); - template iter erase(iter); - void erase_children(const iterator_base&); - - template iter append_child(iter position); - template iter prepend_child(iter position); - template iter append_child(iter position, const T& x); - template iter prepend_child(iter position, const T& x); - template iter append_child(iter position, iter other_position); - template iter prepend_child(iter position, iter other_position); - template iter append_children(iter position, sibling_iterator from, sibling_iterator to); - template iter prepend_children(iter position, sibling_iterator from, sibling_iterator to); - - pre_order_iterator set_head(const T& x); - template iter insert(iter position, const T& x); - sibling_iterator insert(sibling_iterator position, const T& x); - template iter insert_substore(iter position, const iterator_base& substore); - template iter insert_after(iter position, const T& x); - template iter insert_substore_after(iter position, const iterator_base& substore); - - template iter replace(iter position, const T& x); - template iter replace(iter position, const iterator_base& from); - sibling_iterator replace(sibling_iterator orig_begin, sibling_iterator orig_end, - sibling_iterator new_begin, sibling_iterator new_end); - - template iter flatten(iter position); - template iter reparent(iter position, sibling_iterator begin, sibling_iterator end); - template iter reparent(iter position, iter from); - - template iter wrap(iter position, const T& x); - - template iter move_after(iter target, iter source); - template iter move_before(iter target, iter source); - sibling_iterator move_before(sibling_iterator target, sibling_iterator source); - template iter move_ontop(iter target, iter source); - - store move_out(iterator); - template iter move_in(iter, store&); - template iter move_in_below(iter, store&); - template iter move_in_as_nth_child(iter, size_t, store&); - - void merge(sibling_iterator, sibling_iterator, sibling_iterator, sibling_iterator, - bool duplicate_leaves=false); - void sort(sibling_iterator from, sibling_iterator to, bool deep=false); - template - void sort(sibling_iterator from, sibling_iterator to, StrictWeakOrdering comp, bool deep=false); - template - bool equal(const iter& one, const iter& two, const iter& three) const; - template - bool equal(const iter& one, const iter& two, const iter& three, BinaryPredicate) const; - template - bool equal_substore(const iter& one, const iter& two) const; - template - bool equal_substore(const iter& one, const iter& two, BinaryPredicate) const; - store substore(sibling_iterator from, sibling_iterator to) const; - void substore(store&, sibling_iterator from, sibling_iterator to) const; - void swap(sibling_iterator it); - void swap(iterator, iterator); - - size_t size() const; - size_t size(const iterator_base&) const; - bool empty() const; - static int depth(const iterator_base&); - static int depth(const iterator_base&, const iterator_base&); - int max_depth() const; - int max_depth(const iterator_base&) const; - static unsigned int number_of_children(const iterator_base&); - unsigned int number_of_siblings(const iterator_base&) const; - bool is_in_substore(const iterator_base& position, const iterator_base& begin, - const iterator_base& end) const; - bool is_valid(const iterator_base&) const; - iterator lowest_common_ancestor(const iterator_base&, const iterator_base &) const; - - unsigned int index(sibling_iterator it) const; - static sibling_iterator child(const iterator_base& position, unsigned int); - sibling_iterator sibling(const iterator_base& position, unsigned int); - - void debug_verify_consistency() const; - - class iterator_base_less { - public: - bool operator()(const typename store::iterator_base& one, - const typename store::iterator_base& two) const - { - return one.node < two.node; - } - }; - store_node *head, *feet; - private: - allocator alloc_; - void head_initialise_(); - void copy_(const store& other); - - template - class compare_nodes { - public: - compare_nodes(StrictWeakOrdering comp) : comp_(comp) {}; - - bool operator()(const store_node *a, const store_node *b) - { - return comp_(a->data, b->data); - } - private: - StrictWeakOrdering comp_; - }; +protected: + Node* _parent; +private: + NodeType _type; + std::string _name; + Any _value; + Children _children; }; -template -store::store() { - head_initialise_(); - } - -template -store::store(const T& x) { - head_initialise_(); - set_head(x); - } - -template -store::store(store&& x) { - head_initialise_(); - head->next_sibling=x.head->next_sibling; - feet->prev_sibling=x.head->prev_sibling; - x.head->next_sibling->prev_sibling=head; - x.feet->prev_sibling->next_sibling=feet; - x.head->next_sibling=x.feet; - x.feet->prev_sibling=x.head; - } - -template -store::store(const iterator_base& other) { - head_initialise_(); - set_head((*other)); - replace(begin(), other); - } - -template -store::~store() { - clear(); - alloc_.destroy(head); - alloc_.destroy(feet); - alloc_.deallocate(head,1); - alloc_.deallocate(feet,1); - } - -template -void store::head_initialise_() - { - head = alloc_.allocate(1,0); - feet = alloc_.allocate(1,0); - alloc_.construct(head, store_node_()); - alloc_.construct(feet, store_node_()); - - head->parent=0; - head->first_child=0; - head->last_child=0; - head->prev_sibling=0; - head->next_sibling=feet; - - feet->parent=0; - feet->first_child=0; - feet->last_child=0; - feet->prev_sibling=head; - feet->next_sibling=0; - } - -template -store& store::operator=(const store& other) { - if(this != &other) - copy_(other); - return *this; - } - -template -store& store::operator=(store&& x) { - if(this != &x) { - head->next_sibling=x.head->next_sibling; - feet->prev_sibling=x.head->prev_sibling; - x.head->next_sibling->prev_sibling=head; - x.feet->prev_sibling->next_sibling=feet; - x.head->next_sibling=x.feet; - x.feet->prev_sibling=x.head; - } - return *this; - } - -template -store::store(const store& other) { - head_initialise_(); - copy_(other); - } - -template -void store::copy_(const store& other) { - clear(); - pre_order_iterator it=other.begin(), to=begin(); - while(it!=other.end()) { - to=insert(to, (*it)); - it.skip_children(); - ++it; - } - to=begin(); - it=other.begin(); - while(it!=other.end()) { - to=replace(to, it); - to.skip_children(); - it.skip_children(); - ++to; - ++it; - } - } - -template -void store::clear() { - if(head) - while(head->next_sibling!=feet) - erase(pre_order_iterator(head->next_sibling)); - } - -template -void store::erase_children(const iterator_base& it) { - if(it.node==0) return; - - store_node *cur=it.node->first_child; - store_node *prev=0; - - while(cur!=0) { - prev=cur; - cur=cur->next_sibling; - erase_children(pre_order_iterator(prev)); - alloc_.destroy(prev); - alloc_.deallocate(prev,1); - } - it.node->first_child=0; - it.node->last_child=0; - } - -template -template -iter store::erase(iter it) { - store_node *cur=it.node; - assert(cur!=head); - iter ret=it; - ret.skip_children(); - ++ret; - erase_children(it); - if(cur->prev_sibling==0) { - cur->parent->first_child=cur->next_sibling; - } - else { - cur->prev_sibling->next_sibling=cur->next_sibling; - } - if(cur->next_sibling==0) { - cur->parent->last_child=cur->prev_sibling; - } - else { - cur->next_sibling->prev_sibling=cur->prev_sibling; - } - - alloc_.destroy(cur); - alloc_.deallocate(cur,1); - return ret; - } - -template -typename store::pre_order_iterator store::begin() const { - return pre_order_iterator(head->next_sibling); - } - -template -typename store::pre_order_iterator store::end() const { - return pre_order_iterator(feet); - } - -template -typename store::breadth_first_queued_iterator store::begin_breadth_first() const { - return breadth_first_queued_iterator(head->next_sibling); - } - -template -typename store::breadth_first_queued_iterator store::end_breadth_first() const { - return breadth_first_queued_iterator(); - } - -template -typename store::post_order_iterator store::begin_post() const { - store_node *tmp=head->next_sibling; - if(tmp!=feet) { - while(tmp->first_child) - tmp=tmp->first_child; - } - return post_order_iterator(tmp); - } - -template -typename store::post_order_iterator store::end_post() const { - return post_order_iterator(feet); - } - -template -typename store::fixed_depth_iterator store::begin_fixed(const iterator_base& pos, unsigned int dp) const { - typename store::fixed_depth_iterator ret; - ret.top_node=pos.node; - - store_node *tmp=pos.node; - unsigned int curdepth=0; - while(curdepthfirst_child==0) { - if(tmp->next_sibling==0) { - - do { - if(tmp==ret.top_node) - throw std::range_error("store: begin_fixed out of range"); - tmp=tmp->parent; - if(tmp==0) - throw std::range_error("store: begin_fixed out of range"); - --curdepth; - } while(tmp->next_sibling==0); - } - tmp=tmp->next_sibling; - } - tmp=tmp->first_child; - ++curdepth; - } - - ret.node=tmp; - return ret; - } - -template -typename store::fixed_depth_iterator store::end_fixed(const iterator_base& pos, unsigned int dp) const { - assert(1==0); - store_node *tmp=pos.node; - unsigned int curdepth=1; - while(curdepthfirst_child==0) { - tmp=tmp->next_sibling; - if(tmp==0) - throw std::range_error("store: end_fixed out of range"); - } - tmp=tmp->first_child; - ++curdepth; - } - return tmp; - } - -template -typename store::sibling_iterator store::begin(const iterator_base& pos) const { - assert(pos.node!=0); - if(pos.node->first_child==0) { - return end(pos); - } - return pos.node->first_child; - } - -template -typename store::sibling_iterator store::end(const iterator_base& pos) const { - sibling_iterator ret(0); - ret.parent_=pos.node; - return ret; - } - -template -typename store::leaf_iterator store::begin_leaf() const - { - store_node *tmp=head->next_sibling; - if(tmp!=feet) { - while(tmp->first_child) - tmp=tmp->first_child; - } - return leaf_iterator(tmp); - } - -template -typename store::leaf_iterator store::end_leaf() const - { - return leaf_iterator(feet); - } - -template -typename store::leaf_iterator store::begin_leaf(const iterator_base& top) const - { - store_node *tmp=top.node; - while(tmp->first_child) - tmp=tmp->first_child; - return leaf_iterator(tmp, top.node); - } - -template -typename store::leaf_iterator store::end_leaf(const iterator_base& top) const - { - return leaf_iterator(top.node, top.node); - } - -template -template -iter store::parent(iter position) { - assert(position.node!=0); - return iter(position.node->parent); - } - -template -template -iter store::previous_sibling(iter position) { - assert(position.node!=0); - iter ret(position); - ret.node=position.node->prev_sibling; - return ret; - } - -template -template -iter store::next_sibling(iter position) { - assert(position.node!=0); - iter ret(position); - ret.node=position.node->next_sibling; - return ret; - } - -template -template -iter store::next_at_same_depth(iter position) const { - // We make use of a temporary fixed_depth iterator to implement this. - - typename store::fixed_depth_iterator tmp(position.node); - - ++tmp; - return iter(tmp); - - } - -template -template -iter store::append_child(iter position) - { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - store_node *tmp=alloc_.allocate(1,0); - alloc_.construct(tmp, store_node_()); - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node; - if(position.node->last_child!=0) { - position.node->last_child->next_sibling=tmp; - } - else { - position.node->first_child=tmp; - } - tmp->prev_sibling=position.node->last_child; - position.node->last_child=tmp; - tmp->next_sibling=0; - return tmp; - } - -template -template -iter store::prepend_child(iter position) - { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - store_node *tmp=alloc_.allocate(1,0); - alloc_.construct(tmp, store_node_()); - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node; - if(position.node->first_child!=0) { - position.node->first_child->prev_sibling=tmp; - } - else { - position.node->last_child=tmp; - } - tmp->next_sibling=position.node->first_child; - position.node->prev_child=tmp; - tmp->prev_sibling=0; - return tmp; - } - -template -template -iter store::append_child(iter position, const T& x) { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - store_node* tmp = alloc_.allocate(1,0); - alloc_.construct(tmp, x); - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node; - if(position.node->last_child!=0) { - position.node->last_child->next_sibling=tmp; - } - else { - position.node->first_child=tmp; - } - tmp->prev_sibling=position.node->last_child; - position.node->last_child=tmp; - tmp->next_sibling=0; - return tmp; - } - -template -template -iter store::prepend_child(iter position, const T& x) { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - store_node* tmp = alloc_.allocate(1,0); - alloc_.construct(tmp, x); - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node; - if(position.node->first_child!=0) { - position.node->first_child->prev_sibling=tmp; - } - else { - position.node->last_child=tmp; - } - tmp->next_sibling=position.node->first_child; - position.node->first_child=tmp; - tmp->prev_sibling=0; - return tmp; - } - -template -template -iter store::append_child(iter position, iter other) { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - sibling_iterator aargh=append_child(position, value_type()); - return replace(aargh, other); - } - -template -template -iter store::prepend_child(iter position, iter other) { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - sibling_iterator aargh=prepend_child(position, value_type()); - return replace(aargh, other); - } - -template -template -iter store::append_children(iter position, sibling_iterator from, sibling_iterator to) { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - iter ret=from; - - while(from!=to) { - insert_substore(position.end(), from); - ++from; - } - return ret; - } - -template -template -iter store::prepend_children(iter position, sibling_iterator from, sibling_iterator to) { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - iter ret=from; - - while(from!=to) { - insert_substore(position.begin(), from); - ++from; - } - return ret; - } - -template -typename store::pre_order_iterator store::set_head(const T& x) { - assert(head->next_sibling==feet); - return insert(iterator(feet), x); - } - -template -template -iter store::insert(iter position, const T& x) { - if(position.node==0) { - position.node=feet; - - } - store_node* tmp = alloc_.allocate(1,0); - alloc_.construct(tmp, x); - - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node->parent; - tmp->next_sibling=position.node; - tmp->prev_sibling=position.node->prev_sibling; - position.node->prev_sibling=tmp; - - if(tmp->prev_sibling==0) { - if(tmp->parent) - tmp->parent->first_child=tmp; - } - else - tmp->prev_sibling->next_sibling=tmp; - return tmp; - } - -template -typename store::sibling_iterator store::insert(sibling_iterator position, const T& x) { - store_node* tmp = alloc_.allocate(1,0); - alloc_.construct(tmp, x); - - tmp->first_child=0; - tmp->last_child=0; - - tmp->next_sibling=position.node; - if(position.node==0) { - tmp->parent=position.parent_; - tmp->prev_sibling=position.range_last(); - tmp->parent->last_child=tmp; - } - else { - tmp->parent=position.node->parent; - tmp->prev_sibling=position.node->prev_sibling; - position.node->prev_sibling=tmp; - } - - if(tmp->prev_sibling==0) { - if(tmp->parent) - tmp->parent->first_child=tmp; - } - else - tmp->prev_sibling->next_sibling=tmp; - return tmp; - } - -template -template -iter store::insert_after(iter position, const T& x) { - store_node* tmp = alloc_.allocate(1,0); - alloc_.construct(tmp, x); - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node->parent; - tmp->prev_sibling=position.node; - tmp->next_sibling=position.node->next_sibling; - position.node->next_sibling=tmp; - - if(tmp->next_sibling==0) { - if(tmp->parent) - tmp->parent->last_child=tmp; - } - else { - tmp->next_sibling->prev_sibling=tmp; - } - return tmp; - } - -template -template -iter store::insert_substore(iter position, const iterator_base& substore) { - iter it=insert(position, value_type()); - return replace(it, substore); - } - -template -template -iter store::insert_substore_after(iter position, const iterator_base& substore) { - iter it=insert_after(position, value_type()); - return replace(it, substore); - } - - -template -template -iter store::replace(iter position, const T& x) { - position.node->data=x; - return position; - } - -template -template -iter store::replace(iter position, const iterator_base& from) { - assert(position.node!=head); - store_node *current_from=from.node; - store_node *start_from=from.node; - store_node *current_to =position.node; - - erase_children(position); - store_node* tmp = alloc_.allocate(1,0); - alloc_.construct(tmp, (*from)); - tmp->first_child=0; - tmp->last_child=0; - if(current_to->prev_sibling==0) { - if(current_to->parent!=0) - current_to->parent->first_child=tmp; - } - else { - current_to->prev_sibling->next_sibling=tmp; - } - tmp->prev_sibling=current_to->prev_sibling; - if(current_to->next_sibling==0) { - if(current_to->parent!=0) - current_to->parent->last_child=tmp; - } - else { - current_to->next_sibling->prev_sibling=tmp; - } - tmp->next_sibling=current_to->next_sibling; - tmp->parent=current_to->parent; - alloc_.destroy(current_to); - alloc_.deallocate(current_to,1); - current_to=tmp; - - store_node *last=from.node->next_sibling; - - pre_order_iterator toit=tmp; - do { - assert(current_from!=0); - if(current_from->first_child != 0) { - current_from=current_from->first_child; - toit=append_child(toit, current_from->data); - } - else { - while(current_from->next_sibling==0 && current_from!=start_from) { - current_from=current_from->parent; - toit=parent(toit); - assert(current_from!=0); - } - current_from=current_from->next_sibling; - if(current_from!=last) { - toit=append_child(parent(toit), current_from->data); - } - } - } while(current_from!=last); - - return current_to; - } - -template -typename store::sibling_iterator store::replace( - sibling_iterator orig_begin, - sibling_iterator orig_end, - sibling_iterator new_begin, - sibling_iterator new_end) { - store_node *orig_first=orig_begin.node; - store_node *new_first=new_begin.node; - store_node *orig_last=orig_first; - while((++orig_begin)!=orig_end) - orig_last=orig_last->next_sibling; - store_node *new_last=new_first; - while((++new_begin)!=new_end) - new_last=new_last->next_sibling; - - bool first=true; - pre_order_iterator ret; - while(1==1) { - pre_order_iterator tt=insert_substore(pre_order_iterator(orig_first), pre_order_iterator(new_first)); - if(first) { - ret=tt; - first=false; - } - if(new_first==new_last) - break; - new_first=new_first->next_sibling; - } - - bool last=false; - store_node *next=orig_first; - while(1==1) { - if(next==orig_last) - last=true; - next=next->next_sibling; - erase((pre_order_iterator)orig_first); - if(last) - break; - orig_first=next; - } - return ret; - } - -template -template -iter store::flatten(iter position) { - if(position.node->first_child==0) - return position; - - store_node *tmp=position.node->first_child; - while(tmp) { - tmp->parent=position.node->parent; - tmp=tmp->next_sibling; - } - if(position.node->next_sibling) { - position.node->last_child->next_sibling=position.node->next_sibling; - position.node->next_sibling->prev_sibling=position.node->last_child; - } - else { - position.node->parent->last_child=position.node->last_child; - } - position.node->next_sibling=position.node->first_child; - position.node->next_sibling->prev_sibling=position.node; - position.node->first_child=0; - position.node->last_child=0; - - return position; - } - - -template -template -iter store::reparent(iter position, sibling_iterator begin, sibling_iterator end) { - store_node *first=begin.node; - store_node *last=first; - - assert(first!=position.node); - - if(begin==end) return begin; - while((++begin)!=end) { - last=last->next_sibling; - } - if(first->prev_sibling==0) { - first->parent->first_child=last->next_sibling; - } - else { - first->prev_sibling->next_sibling=last->next_sibling; - } - if(last->next_sibling==0) { - last->parent->last_child=first->prev_sibling; - } - else { - last->next_sibling->prev_sibling=first->prev_sibling; - } - if(position.node->first_child==0) { - position.node->first_child=first; - position.node->last_child=last; - first->prev_sibling=0; - } - else { - position.node->last_child->next_sibling=first; - first->prev_sibling=position.node->last_child; - position.node->last_child=last; - } - last->next_sibling=0; - - store_node *pos=first; - for(;;) { - pos->parent=position.node; - if(pos==last) break; - pos=pos->next_sibling; - } - - return first; - } - -template -template iter store::reparent(iter position, iter from) { - if(from.node->first_child==0) return position; - return reparent(position, from.node->first_child, end(from)); - } - -template -template iter store::wrap(iter position, const T& x) { - assert(position.node!=0); - sibling_iterator fr=position, to=position; - ++to; - iter ret = insert(position, x); - reparent(ret, fr, to); - return ret; - } - -template -template iter store::move_after(iter target, iter source) - { - store_node *dst=target.node; - store_node *src=source.node; - assert(dst); - assert(src); - - if(dst==src) return source; - if(dst->next_sibling) - if(dst->next_sibling==src) - return source; - - if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; - else src->parent->first_child=src->next_sibling; - if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; - else src->parent->last_child=src->prev_sibling; - - if(dst->next_sibling!=0) dst->next_sibling->prev_sibling=src; - else dst->parent->last_child=src; - src->next_sibling=dst->next_sibling; - dst->next_sibling=src; - src->prev_sibling=dst; - src->parent=dst->parent; - return src; - } - -template -template iter store::move_before(iter target, iter source) - { - store_node *dst=target.node; - store_node *src=source.node; - assert(dst); - assert(src); - - if(dst==src) return source; - if(dst->prev_sibling) - if(dst->prev_sibling==src) - return source; - - if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; - else src->parent->first_child=src->next_sibling; - if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; - else src->parent->last_child=src->prev_sibling; - - if(dst->prev_sibling!=0) dst->prev_sibling->next_sibling=src; - else dst->parent->first_child=src; - src->prev_sibling=dst->prev_sibling; - dst->prev_sibling=src; - src->next_sibling=dst; - src->parent=dst->parent; - return src; - } - -template -typename store::sibling_iterator store::move_before(sibling_iterator target, - sibling_iterator source) { - store_node *dst=target.node; - store_node *src=source.node; - store_node *dst_prev_sibling; - if(dst==0) { - dst_prev_sibling=target.parent_->last_child; - assert(dst_prev_sibling); - } - else dst_prev_sibling=dst->prev_sibling; - assert(src); - - if(dst==src) return source; - if(dst_prev_sibling) - if(dst_prev_sibling==src) - return source; - - if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; - else src->parent->first_child=src->next_sibling; - if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; - else src->parent->last_child=src->prev_sibling; - - if(dst_prev_sibling!=0) dst_prev_sibling->next_sibling=src; - else target.parent_->first_child=src; - src->prev_sibling=dst_prev_sibling; - if(dst) { - dst->prev_sibling=src; - src->parent=dst->parent; - } - src->next_sibling=dst; - return src; - } - -template -template iter store::move_ontop(iter target, iter source) { - store_node *dst=target.node; - store_node *src=source.node; - assert(dst); - assert(src); - - if(dst==src) return source; - - store_node *b_prev_sibling=dst->prev_sibling; - store_node *b_next_sibling=dst->next_sibling; - store_node *b_parent=dst->parent; - - erase(target); - - if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; - else src->parent->first_child=src->next_sibling; - if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; - else src->parent->last_child=src->prev_sibling; - - if(b_prev_sibling!=0) b_prev_sibling->next_sibling=src; - else b_parent->first_child=src; - if(b_next_sibling!=0) b_next_sibling->prev_sibling=src; - else b_parent->last_child=src; - src->prev_sibling=b_prev_sibling; - src->next_sibling=b_next_sibling; - src->parent=b_parent; - return src; - } - - -template -store store::move_out(iterator source) { - store ret; - - ret.head->next_sibling = source.node; - ret.feet->prev_sibling = source.node; - source.node->parent=0; - - if(source.node->prev_sibling!=0) - source.node->prev_sibling->next_sibling = source.node->next_sibling; - - if(source.node->next_sibling!=0) - source.node->next_sibling->prev_sibling = source.node->prev_sibling; - - source.node->prev_sibling = ret.head; - source.node->next_sibling = ret.feet; - - return ret; - } - -template -template iter store::move_in(iter loc, store& other) { - if(other.head->next_sibling==other.feet) return loc; - - store_node *other_first_head = other.head->next_sibling; - store_node *other_last_head = other.feet->prev_sibling; - - sibling_iterator prev(loc); - --prev; - - prev.node->next_sibling = other_first_head; - loc.node->prev_sibling = other_last_head; - other_first_head->prev_sibling = prev.node; - other_last_head->next_sibling = loc.node; - - store_node *walk=other_first_head; - while(true) { - walk->parent=loc.node->parent; - if(walk==other_last_head) - break; - walk=walk->next_sibling; - } - - // Close other store. - other.head->next_sibling=other.feet; - other.feet->prev_sibling=other.head; - - return other_first_head; - } - -template -template iter store::move_in_as_nth_child(iter loc, size_t n, store& other) { - if(other.head->next_sibling==other.feet) return loc; // other store is empty - - store_node *other_first_head = other.head->next_sibling; - store_node *other_last_head = other.feet->prev_sibling; - - if(n==0) { - if(loc.node->first_child==0) { - loc.node->first_child=other_first_head; - loc.node->last_child=other_last_head; - other_last_head->next_sibling=0; - other_first_head->prev_sibling=0; - } - else { - loc.node->first_child->prev_sibling=other_last_head; - other_last_head->next_sibling=loc.node->first_child; - loc.node->first_child=other_first_head; - other_first_head->prev_sibling=0; - } - } - else { - --n; - store_node *walk = loc.node->first_child; - while(true) { - if(walk==0) - throw std::range_error("store: move_in_as_nth_child position " - +std::to_string(n+1) - +" out of range; only " - +std::to_string(number_of_children(loc)) - +" child nodes present"); - if(n==0) - break; - --n; - walk = walk->next_sibling; - } - if(walk->next_sibling==0) - loc.node->last_child=other_last_head; - else - walk->next_sibling->prev_sibling=other_last_head; - other_last_head->next_sibling=walk->next_sibling; - walk->next_sibling=other_first_head; - other_first_head->prev_sibling=walk; - } - - // Adjust parent pointers. - store_node *walk=other_first_head; - while(true) { - walk->parent=loc.node; - if(walk==other_last_head) - break; - walk=walk->next_sibling; - } - - // Close other store. - other.head->next_sibling=other.feet; - other.feet->prev_sibling=other.head; - - return other_first_head; - } - - -template -void store::merge(sibling_iterator to1, sibling_iterator to2, - sibling_iterator from1, sibling_iterator from2, - bool duplicate_leaves) { - sibling_iterator fnd; - while(from1!=from2) { - if((fnd=std::find(to1, to2, (*from1))) != to2) { // element found - if(from1.begin()==from1.end()) { // full depth reached - if(duplicate_leaves) - append_child(parent(to1), (*from1)); - } - else { // descend further - merge(fnd.begin(), fnd.end(), from1.begin(), from1.end(), duplicate_leaves); - } - } - else { // element missing - insert_substore(to2, from1); - } - ++from1; - } - } - - -template -void store::sort(sibling_iterator from, sibling_iterator to, bool deep) { - std::less comp; - sort(from, to, comp, deep); - } - -template -template -void store::sort(sibling_iterator from, sibling_iterator to, - StrictWeakOrdering comp, bool deep) { - if(from==to) return; - - std::multiset > nodes(comp); - sibling_iterator it=from, it2=to; - while(it != to) { - nodes.insert(it.node); - ++it; - } - // reassemble - --it2; - - // prev and next are the nodes before and after the sorted range - store_node *prev=from.node->prev_sibling; - store_node *next=it2.node->next_sibling; - typename std::multiset >::iterator nit=nodes.begin(), eit=nodes.end(); - if(prev==0) { - if((*nit)->parent!=0) // to catch "sorting the head" situations, when there is no parent - (*nit)->parent->first_child=(*nit); - } - else prev->next_sibling=(*nit); - - --eit; - while(nit!=eit) { - (*nit)->prev_sibling=prev; - if(prev) - prev->next_sibling=(*nit); - prev=(*nit); - ++nit; - } - if(prev) - prev->next_sibling=(*eit); - - (*eit)->next_sibling=next; - (*eit)->prev_sibling=prev; // missed in the loop above - if(next==0) { - if((*eit)->parent!=0) - (*eit)->parent->last_child=(*eit); - } - else next->prev_sibling=(*eit); - - if(deep) { - sibling_iterator bcs(*nodes.begin()); - sibling_iterator ecs(*eit); - ++ecs; - while(bcs!=ecs) { - sort(begin(bcs), end(bcs), comp, deep); - ++bcs; - } - } - } - -template -template -bool store::equal(const iter& one_, const iter& two, const iter& three_) const { - std::equal_to comp; - return equal(one_, two, three_, comp); - } - -template -template -bool store::equal_substore(const iter& one_, const iter& two_) const { - std::equal_to comp; - return equal_substore(one_, two_, comp); - } - -template -template -bool store::equal(const iter& one_, const iter& two, const iter& three_, BinaryPredicate fun) const { - pre_order_iterator one(one_), three(three_); - - while(one!=two && is_valid(three)) { - if(!fun(*one,*three)) - return false; - if(one.number_of_children()!=three.number_of_children()) - return false; - ++one; - ++three; - } - return true; - } - -template -template -bool store::equal_substore(const iter& one_, const iter& two_, BinaryPredicate fun) const { - pre_order_iterator one(one_), two(two_); - - if(!fun(*one,*two)) return false; - if(number_of_children(one)!=number_of_children(two)) return false; - return equal(begin(one),end(one),begin(two),fun); - } - -template -store store::substore(sibling_iterator from, sibling_iterator to) const { - assert(from!=to); - - store tmp; - tmp.set_head(value_type()); - tmp.replace(tmp.begin(), tmp.end(), from, to); - return tmp; - } - -template -void store::substore(store& tmp, sibling_iterator from, sibling_iterator to) const { - assert(from!=to); // if from==to, the range is empty, hence no store to return. - - tmp.set_head(value_type()); - tmp.replace(tmp.begin(), tmp.end(), from, to); - } - -template -size_t store::size() const { - size_t i=0; - pre_order_iterator it=begin(), eit=end(); - while(it!=eit) { - ++i; - ++it; - } - return i; - } - -template -size_t store::size(const iterator_base& top) const { - size_t i=0; - pre_order_iterator it=top, eit=top; - eit.skip_children(); - ++eit; - while(it!=eit) { - ++i; - ++it; - } - return i; - } - -template -bool store::empty() const { - pre_order_iterator it=begin(), eit=end(); - return (it==eit); - } - -template -int store::depth(const iterator_base& it) { - store_node* pos=it.node; - assert(pos!=0); - int ret=0; - while(pos->parent!=0) { - pos=pos->parent; - ++ret; - } - return ret; - } - -template -int store::depth(const iterator_base& it, const iterator_base& root) { - store_node* pos=it.node; - assert(pos!=0); - int ret=0; - while(pos->parent!=0 && pos!=root.node) { - pos=pos->parent; - ++ret; - } - return ret; - } - -template -int store::max_depth() const { - int maxd=-1; - for(store_node *it = head->next_sibling; it!=feet; it=it->next_sibling) - maxd=std::max(maxd, max_depth(it)); - - return maxd; - } - - -template -int store::max_depth(const iterator_base& pos) const { - store_node *tmp=pos.node; - - if(tmp==0 || tmp==head || tmp==feet) return -1; - - int curdepth=0, maxdepth=0; - while(true) { // try to walk the bottom of the store - while(tmp->first_child==0) { - if(tmp==pos.node) return maxdepth; - if(tmp->next_sibling==0) { - // try to walk up and then right again - do { - tmp=tmp->parent; - if(tmp==0) return maxdepth; - --curdepth; - } while(tmp->next_sibling==0); - } - if(tmp==pos.node) return maxdepth; - tmp=tmp->next_sibling; - } - tmp=tmp->first_child; - ++curdepth; - maxdepth=std::max(curdepth, maxdepth); - } - } - -template -unsigned int store::number_of_children(const iterator_base& it) { - store_node *pos=it.node->first_child; - if(pos==0) return 0; - - unsigned int ret=1; -// while(pos!=it.node->last_child) { -// ++ret; -// pos=pos->next_sibling; -// } - while((pos=pos->next_sibling)) - ++ret; - return ret; - } - -template -unsigned int store::number_of_siblings(const iterator_base& it) const { - store_node *pos=it.node; - unsigned int ret=0; - // count forward - while(pos->next_sibling && - pos->next_sibling!=head && - pos->next_sibling!=feet) { - ++ret; - pos=pos->next_sibling; - } - // count backward - pos=it.node; - while(pos->prev_sibling && - pos->prev_sibling!=head && - pos->prev_sibling!=feet) { - ++ret; - pos=pos->prev_sibling; - } - - return ret; - } - -template -void store::swap(sibling_iterator it) { - store_node *nxt=it.node->next_sibling; - if(nxt) { - if(it.node->prev_sibling) - it.node->prev_sibling->next_sibling=nxt; - else - it.node->parent->first_child=nxt; - nxt->prev_sibling=it.node->prev_sibling; - store_node *nxtnxt=nxt->next_sibling; - if(nxtnxt) - nxtnxt->prev_sibling=it.node; - else - it.node->parent->last_child=it.node; - nxt->next_sibling=it.node; - it.node->prev_sibling=nxt; - it.node->next_sibling=nxtnxt; - } - } - -template -void store::swap(iterator one, iterator two) { - // if one and two are adjacent siblings, use the sibling swap - if(one.node->next_sibling==two.node) swap(one); - else if(two.node->next_sibling==one.node) swap(two); - else { - store_node *nxt1=one.node->next_sibling; - store_node *nxt2=two.node->next_sibling; - store_node *pre1=one.node->prev_sibling; - store_node *pre2=two.node->prev_sibling; - store_node *par1=one.node->parent; - store_node *par2=two.node->parent; - - // reconnect - one.node->parent=par2; - one.node->next_sibling=nxt2; - if(nxt2) nxt2->prev_sibling=one.node; - else par2->last_child=one.node; - one.node->prev_sibling=pre2; - if(pre2) pre2->next_sibling=one.node; - else par2->first_child=one.node; - - two.node->parent=par1; - two.node->next_sibling=nxt1; - if(nxt1) nxt1->prev_sibling=two.node; - else par1->last_child=two.node; - two.node->prev_sibling=pre1; - if(pre1) pre1->next_sibling=two.node; - else par1->first_child=two.node; - } - } - - -template -bool store::is_in_substore(const iterator_base& it, const iterator_base& begin, - const iterator_base& end) const { - // FIXME: this should be optimised. - pre_order_iterator tmp=begin; - while(tmp!=end) { - if(tmp==it) return true; - ++tmp; - } - return false; - } - -template -bool store::is_valid(const iterator_base& it) const { - if(it.node==0 || it.node==feet || it.node==head) return false; - else return true; - } - -template -typename store::iterator store::lowest_common_ancestor( - const iterator_base& one, const iterator_base& two) const { - std::set parents; - - // Walk up from 'one' storing all parents. - iterator walk=one; - do { - walk=parent(walk); - parents.insert(walk); - } while( is_valid(parent(walk)) ); - - // Walk up from 'two' until we encounter a node in parents. - walk=two; - do { - walk=parent(walk); - if(parents.find(walk) != parents.end()) break; - } while( is_valid(parent(walk)) ); - - return walk; - } - -template -unsigned int store::index(sibling_iterator it) const { - unsigned int ind=0; - if(it.node->parent==0) { - while(it.node->prev_sibling!=head) { - it.node=it.node->prev_sibling; - ++ind; - } - } - else { - while(it.node->prev_sibling!=0) { - it.node=it.node->prev_sibling; - ++ind; - } - } - return ind; - } - -template -typename store::sibling_iterator store::sibling(const iterator_base& it, unsigned int num) - { - store_node *tmp; - if(it.node->parent==0) { - tmp=head->next_sibling; - while(num) { - tmp = tmp->next_sibling; - --num; - } - } - else { - tmp=it.node->parent->first_child; - while(num) { - assert(tmp!=0); - tmp = tmp->next_sibling; - --num; - } - } - return tmp; - } - -template -void store::debug_verify_consistency() const { - iterator it=begin(); - while(it!=end()) { - if(it.node->parent!=0) { - if(it.node->prev_sibling==0) - assert(it.node->parent->first_child==it.node); - else - assert(it.node->prev_sibling->next_sibling==it.node); - if(it.node->next_sibling==0) - assert(it.node->parent->last_child==it.node); - else - assert(it.node->next_sibling->prev_sibling==it.node); - } - ++it; - } - } - -template -typename store::sibling_iterator store::child(const iterator_base& it, unsigned int num) { - store_node *tmp=it.node->first_child; - while(num--) { - assert(tmp!=0); - tmp=tmp->next_sibling; - } - return tmp; - } - - - - -// Iterator base - -template -store::iterator_base::iterator_base() - : node(0), skip_current_children_(false) { - } - -template -store::iterator_base::iterator_base(store_node *tn) - : node(tn), skip_current_children_(false) { - } - -template -T& store::iterator_base::operator*() const { - return node->data; - } - -template -T* store::iterator_base::operator->() const { - return &(node->data); - } - -template -bool store::post_order_iterator::operator!=(const post_order_iterator& other) const { - if(other.node!=this->node) return true; - else return false; - } - -template -bool store::post_order_iterator::operator==(const post_order_iterator& other) const { - if(other.node==this->node) return true; - else return false; - } - -template -bool store::pre_order_iterator::operator!=(const pre_order_iterator& other) const { - if(other.node!=this->node) return true; - else return false; - } - -template -bool store::pre_order_iterator::operator==(const pre_order_iterator& other) const { - if(other.node==this->node) return true; - else return false; - } - -template -bool store::sibling_iterator::operator!=(const sibling_iterator& other) const { - if(other.node!=this->node) return true; - else return false; - } - -template -bool store::sibling_iterator::operator==(const sibling_iterator& other) const { - if(other.node==this->node) return true; - else return false; - } - -template -bool store::leaf_iterator::operator!=(const leaf_iterator& other) const - { - if(other.node!=this->node) return true; - else return false; - } - -template -bool store::leaf_iterator::operator==(const leaf_iterator& other) const - { - if(other.node==this->node && other.top_node==this->top_node) return true; - else return false; - } - -template -typename store::sibling_iterator store::iterator_base::begin() const { - if(node->first_child==0) - return end(); - - sibling_iterator ret(node->first_child); - ret.parent_=this->node; - return ret; - } - -template -typename store::sibling_iterator store::iterator_base::end() const { - sibling_iterator ret(0); - ret.parent_=node; - return ret; - } - -template -void store::iterator_base::skip_children() { - skip_current_children_=true; - } - -template -void store::iterator_base::skip_children(bool skip) - { - skip_current_children_=skip; - } - -template -unsigned int store::iterator_base::number_of_children() const { - store_node *pos=node->first_child; - if(pos==0) return 0; - - unsigned int ret=1; - while(pos!=node->last_child) { - ++ret; - pos=pos->next_sibling; - } - return ret; - } - - - -// Pre-order iterator - -template -store::pre_order_iterator::pre_order_iterator() - : iterator_base(0) { - } - -template -store::pre_order_iterator::pre_order_iterator(store_node *tn) - : iterator_base(tn) { - } - -template -store::pre_order_iterator::pre_order_iterator(const iterator_base &other) - : iterator_base(other.node) { - } - -template -store::pre_order_iterator::pre_order_iterator(const sibling_iterator& other) - : iterator_base(other.node) { - if(this->node==0) { - if(other.range_last()!=0) - this->node=other.range_last(); - else - this->node=other.parent_; - this->skip_children(); - ++(*this); - } - } - -template -typename store::pre_order_iterator& store::pre_order_iterator::operator++() { - assert(this->node!=0); - if(!this->skip_current_children_ && this->node->first_child != 0) { - this->node=this->node->first_child; - } - else { - this->skip_current_children_=false; - while(this->node->next_sibling==0) { - this->node=this->node->parent; - if(this->node==0) - return *this; - } - this->node=this->node->next_sibling; - } - return *this; - } - -template -typename store::pre_order_iterator& store::pre_order_iterator::operator--() { - assert(this->node!=0); - if(this->node->prev_sibling) { - this->node=this->node->prev_sibling; - while(this->node->last_child) - this->node=this->node->last_child; - } - else { - this->node=this->node->parent; - if(this->node==0) - return *this; - } - return *this; -} - -template -typename store::pre_order_iterator store::pre_order_iterator::operator++(int) { - pre_order_iterator copy = *this; - ++(*this); - return copy; - } - -template -typename store::pre_order_iterator& store::pre_order_iterator::next_skip_children() - { - (*this).skip_children(); - (*this)++; - return *this; - } - -template -typename store::pre_order_iterator store::pre_order_iterator::operator--(int) -{ - pre_order_iterator copy = *this; - --(*this); - return copy; -} - -template -typename store::pre_order_iterator& store::pre_order_iterator::operator+=(unsigned int num) { - while(num>0) { - ++(*this); - --num; - } - return (*this); - } - -template -typename store::pre_order_iterator& store::pre_order_iterator::operator-=(unsigned int num) { - while(num>0) { - --(*this); - --num; - } - return (*this); - } - - - -// Post-order iterator - -template -store::post_order_iterator::post_order_iterator() - : iterator_base(0) { - } - -template -store::post_order_iterator::post_order_iterator(store_node *tn) - : iterator_base(tn) { - } - -template -store::post_order_iterator::post_order_iterator(const iterator_base &other) - : iterator_base(other.node) { - } - -template -store::post_order_iterator::post_order_iterator(const sibling_iterator& other) - : iterator_base(other.node) { - if(this->node==0) { - if(other.range_last()!=0) - this->node=other.range_last(); - else - this->node=other.parent_; - this->skip_children(); - ++(*this); - } - } - -template -typename store::post_order_iterator& store::post_order_iterator::operator++() { - assert(this->node!=0); - if(this->node->next_sibling==0) { - this->node=this->node->parent; - this->skip_current_children_=false; - } - else { - this->node=this->node->next_sibling; - if(this->skip_current_children_) { - this->skip_current_children_=false; - } - else { - while(this->node->first_child) - this->node=this->node->first_child; - } - } - return *this; - } - -template -typename store::post_order_iterator& store::post_order_iterator::operator--() { - assert(this->node!=0); - if(this->skip_current_children_ || this->node->last_child==0) { - this->skip_current_children_=false; - while(this->node->prev_sibling==0) - this->node=this->node->parent; - this->node=this->node->prev_sibling; - } - else { - this->node=this->node->last_child; - } - return *this; - } - -template -typename store::post_order_iterator store::post_order_iterator::operator++(int) { - post_order_iterator copy = *this; - ++(*this); - return copy; - } - -template -typename store::post_order_iterator store::post_order_iterator::operator--(int) { - post_order_iterator copy = *this; - --(*this); - return copy; - } - - -template -typename store::post_order_iterator& store::post_order_iterator::operator+=(unsigned int num) { - while(num>0) { - ++(*this); - --num; - } - return (*this); - } - -template -typename store::post_order_iterator& store::post_order_iterator::operator-=(unsigned int num) { - while(num>0) { - --(*this); - --num; - } - return (*this); - } - -template -void store::post_order_iterator::descend_all() { - assert(this->node!=0); - while(this->node->first_child) - this->node=this->node->first_child; - } - - -// Breadth-first iterator - -template -store::breadth_first_queued_iterator::breadth_first_queued_iterator() - : iterator_base() { - } - -template -store::breadth_first_queued_iterator::breadth_first_queued_iterator(store_node *tn) - : iterator_base(tn) { - traversal_queue.push(tn); - } - -template -store::breadth_first_queued_iterator::breadth_first_queued_iterator(const iterator_base& other) - : iterator_base(other.node) { - traversal_queue.push(other.node); - } - -template -bool store::breadth_first_queued_iterator::operator!=(const breadth_first_queued_iterator& other) const { - if(other.node!=this->node) return true; - else return false; - } - -template -bool store::breadth_first_queued_iterator::operator==(const breadth_first_queued_iterator& other) const { - if(other.node==this->node) return true; - else return false; - } - -template -typename store::breadth_first_queued_iterator& store::breadth_first_queued_iterator::operator++() { - assert(this->node!=0); - - // Add child nodes and pop current node - sibling_iterator sib=this->begin(); - while(sib!=this->end()) { - traversal_queue.push(sib.node); - ++sib; - } - traversal_queue.pop(); - if(traversal_queue.size()>0) - this->node=traversal_queue.front(); - else - this->node=0; - return (*this); - } - -template -typename store::breadth_first_queued_iterator store::breadth_first_queued_iterator::operator++(int) { - breadth_first_queued_iterator copy = *this; - ++(*this); - return copy; - } - -template -typename store::breadth_first_queued_iterator& store::breadth_first_queued_iterator::operator+=(unsigned int num) { - while(num>0) { - ++(*this); - --num; - } - return (*this); - } - - - -// Fixed depth iterator - -template -store::fixed_depth_iterator::fixed_depth_iterator() - : iterator_base() { - } - -template -store::fixed_depth_iterator::fixed_depth_iterator(store_node *tn) - : iterator_base(tn), top_node(0) { - } - -template -store::fixed_depth_iterator::fixed_depth_iterator(const iterator_base& other) - : iterator_base(other.node), top_node(0) { - } - -template -store::fixed_depth_iterator::fixed_depth_iterator(const sibling_iterator& other) - : iterator_base(other.node), top_node(0) { - } - -template -store::fixed_depth_iterator::fixed_depth_iterator(const fixed_depth_iterator& other) - : iterator_base(other.node), top_node(other.top_node) { - } - -template -bool store::fixed_depth_iterator::operator==(const fixed_depth_iterator& other) const { - if(other.node==this->node && other.top_node==top_node) return true; - else return false; - } - -template -bool store::fixed_depth_iterator::operator!=(const fixed_depth_iterator& other) const { - if(other.node!=this->node || other.top_node!=top_node) return true; - else return false; - } - -template -typename store::fixed_depth_iterator& store::fixed_depth_iterator::operator++() { - assert(this->node!=0); - - if(this->node->next_sibling) { - this->node=this->node->next_sibling; - } - else { - int relative_depth=0; - upper: - do { - if(this->node==this->top_node) { - this->node=0; // FIXME: return a proper fixed_depth end iterator once implemented - return *this; - } - this->node=this->node->parent; - if(this->node==0) return *this; - --relative_depth; - } while(this->node->next_sibling==0); - lower: - this->node=this->node->next_sibling; - while(this->node->first_child==0) { - if(this->node->next_sibling==0) - goto upper; - this->node=this->node->next_sibling; - if(this->node==0) return *this; - } - while(relative_depth<0 && this->node->first_child!=0) { - this->node=this->node->first_child; - ++relative_depth; - } - if(relative_depth<0) { - if(this->node->next_sibling==0) goto upper; - else goto lower; - } - } - return *this; - } - -template -typename store::fixed_depth_iterator& store::fixed_depth_iterator::operator--() { - assert(this->node!=0); - - if(this->node->prev_sibling) { - this->node=this->node->prev_sibling; - } - else { - int relative_depth=0; - upper: - do { - if(this->node==this->top_node) { - this->node=0; - return *this; - } - this->node=this->node->parent; - if(this->node==0) return *this; - --relative_depth; - } while(this->node->prev_sibling==0); - lower: - this->node=this->node->prev_sibling; - while(this->node->last_child==0) { - if(this->node->prev_sibling==0) - goto upper; - this->node=this->node->prev_sibling; - if(this->node==0) return *this; - } - while(relative_depth<0 && this->node->last_child!=0) { - this->node=this->node->last_child; - ++relative_depth; - } - if(relative_depth<0) { - if(this->node->prev_sibling==0) goto upper; - else goto lower; - } - } - return *this; - } - -template -typename store::fixed_depth_iterator store::fixed_depth_iterator::operator++(int) { - fixed_depth_iterator copy = *this; - ++(*this); - return copy; - } - -template -typename store::fixed_depth_iterator store::fixed_depth_iterator::operator--(int) - { - fixed_depth_iterator copy = *this; - --(*this); - return copy; - } - -template -typename store::fixed_depth_iterator& store::fixed_depth_iterator::operator-=(unsigned int num) { - while(num>0) { - --(*this); - --(num); - } - return (*this); - } - -template -typename store::fixed_depth_iterator& store::fixed_depth_iterator::operator+=(unsigned int num) { - while(num>0) { - ++(*this); - --(num); - } - return *this; - } - - -// Sibling iterator - -template -store::sibling_iterator::sibling_iterator() - : iterator_base() { - set_parent_(); - } - -template -store::sibling_iterator::sibling_iterator(store_node *tn) - : iterator_base(tn) { - set_parent_(); - } - -template -store::sibling_iterator::sibling_iterator(const iterator_base& other) - : iterator_base(other.node) { - set_parent_(); - } - -template -store::sibling_iterator::sibling_iterator(const sibling_iterator& other) - : iterator_base(other), parent_(other.parent_) { - } - -template -void store::sibling_iterator::set_parent_() { - parent_=0; - if(this->node==0) return; - if(this->node->parent!=0) - parent_=this->node->parent; - } - -template -typename store::sibling_iterator& store::sibling_iterator::operator++() { - if(this->node) - this->node=this->node->next_sibling; - return *this; - } - -template -typename store::sibling_iterator& store::sibling_iterator::operator--() { - if(this->node) this->node=this->node->prev_sibling; - else { - assert(parent_); - this->node=parent_->last_child; - } - return *this; -} - -template -typename store::sibling_iterator store::sibling_iterator::operator++(int) { - sibling_iterator copy = *this; - ++(*this); - return copy; - } - -template -typename store::sibling_iterator store::sibling_iterator::operator--(int) { - sibling_iterator copy = *this; - --(*this); - return copy; - } - -template -typename store::sibling_iterator& store::sibling_iterator::operator+=(unsigned int num) { - while(num>0) { - ++(*this); - --num; - } - return (*this); - } - -template -typename store::sibling_iterator& store::sibling_iterator::operator-=(unsigned int num) { - while(num>0) { - --(*this); - --num; - } - return (*this); - } - -template -typename store::store_node *store::sibling_iterator::range_first() const { - store_node *tmp=parent_->first_child; - return tmp; - } - -template -typename store::store_node *store::sibling_iterator::range_last() const { - return parent_->last_child; - } - -// Leaf iterator - -template -store::leaf_iterator::leaf_iterator() - : iterator_base(0), top_node(0) - { - } - -template -store::leaf_iterator::leaf_iterator(store_node *tn, store_node *top) - : iterator_base(tn), top_node(top) - { - } - -template -store::leaf_iterator::leaf_iterator(const iterator_base &other) - : iterator_base(other.node), top_node(0) - { - } - -template -store::leaf_iterator::leaf_iterator(const sibling_iterator& other) - : iterator_base(other.node), top_node(0) - { - if(this->node==0) { - if(other.range_last()!=0) - this->node=other.range_last(); - else - this->node=other.parent_; - ++(*this); - } - } - -template -typename store::leaf_iterator& store::leaf_iterator::operator++() - { - assert(this->node!=0); - if(this->node->first_child!=0) { // current node is no longer leaf (children got added) - while(this->node->first_child) - this->node=this->node->first_child; - } - else { - while(this->node->next_sibling==0) { - if (this->node->parent==0) return *this; - this->node=this->node->parent; - if (top_node != 0 && this->node==top_node) return *this; - } - this->node=this->node->next_sibling; - while(this->node->first_child) - this->node=this->node->first_child; - } - return *this; - } - -template -typename store::leaf_iterator& store::leaf_iterator::operator--() - { - assert(this->node!=0); - while (this->node->prev_sibling==0) { - if (this->node->parent==0) return *this; - this->node=this->node->parent; - if (top_node !=0 && this->node==top_node) return *this; - } - this->node=this->node->prev_sibling; - while(this->node->last_child) - this->node=this->node->last_child; - return *this; - } - -template -typename store::leaf_iterator store::leaf_iterator::operator++(int) - { - leaf_iterator copy = *this; - ++(*this); - return copy; - } - -template -typename store::leaf_iterator store::leaf_iterator::operator--(int) - { - leaf_iterator copy = *this; - --(*this); - return copy; - } - - -template -typename store::leaf_iterator& store::leaf_iterator::operator+=(unsigned int num) - { - while(num>0) { - ++(*this); - --num; - } - return (*this); - } - -template -typename store::leaf_iterator& store::leaf_iterator::operator-=(unsigned int num) - { - while(num>0) { - --(*this); - --num; - } - return (*this); - } +//using namespace arangodb::velocypack; +class Store : public Node { // Root node + +public: + Store () : Node("root") { + } + ~Store () {} +}; + +}} #endif - diff --git a/arangod/RestHandler/RestAgencyPrivHandler.cpp b/arangod/RestHandler/RestAgencyPrivHandler.cpp index 3704082440..a8be019e03 100644 --- a/arangod/RestHandler/RestAgencyPrivHandler.cpp +++ b/arangod/RestHandler/RestAgencyPrivHandler.cpp @@ -49,7 +49,7 @@ extern ArangoServer* ArangoInstance; RestAgencyPrivHandler::RestAgencyPrivHandler(HttpRequest* request, Agent* agent) - : RestBaseHandler(request), _agent(agent) { + : RestBaseHandler(request), _agent(agent) { } bool RestAgencyPrivHandler::isDirect() const { return false; } @@ -103,7 +103,7 @@ HttpHandler::status_t RestAgencyPrivHandler::execute() { } else { return reportBadQuery(); // bad query } - } else if (_request->suffix()[0] == "requestVote") { // requestVote + } else if (_request->suffix()[0] == "requestVote") { // requestVote if (readValue("term", term) && readValue("candidateId", id) && readValue("prevLogIndex", prevLogIndex) && @@ -113,6 +113,13 @@ HttpHandler::status_t RestAgencyPrivHandler::execute() { result.add("term", VPackValue(ret.term)); result.add("voteGranted", VPackValue(ret.success)); } + } else if (_request->suffix()[0] == "notifyAll") { // notify + if (readValue("term", term) && readValue("agencyId", id)) { + priv_rpc_ret_t ret = _agent->requestVote (term, id, prevLogIndex, + prevLogTerm); + result.add("term", VPackValue(ret.term)); + result.add("voteGranted", VPackValue(ret.success)); + } } else { generateError(HttpResponse::NOT_FOUND,404); // nothing else here return HttpHandler::status_t(HANDLER_DONE); @@ -129,7 +136,3 @@ HttpHandler::status_t RestAgencyPrivHandler::execute() { -/* - term_t term, id_t leaderId, index_t prevIndex, - term_t prevTerm, index_t lastCommitIndex, query_t const& queries - */ From b020fc0a23857f1e5fc934eb7fc4153399855477 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Thu, 3 Mar 2016 08:16:48 +0100 Subject: [PATCH 33/61] builds with new store --- arangod/Agency/Agent.cpp | 11 +++++------ arangod/Agency/Constituent.cpp | 6 +++--- arangod/Agency/Store.h | 10 ++++++---- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 2780a83d28..d609e5a808 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -104,20 +104,19 @@ bool Agent::waitFor (index_t index, duration_t timeout) { void Agent::reportIn (id_t id, index_t index) { MUTEX_LOCKER(mutexLocker, _confirmedLock); - if (index > _confirmed[id]) + if (index > _confirmed[id]) // progress this follower? _confirmed[id] = index; - // last commit index smaller? - // check if we can move forward - if(_last_commit_index < 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) { + if (n>size()/2) { // enough confirms? _last_commit_index = index; } } - _rest_cv.broadcast(); + _rest_cv.broadcast(); // wake up REST handlers } priv_rpc_ret_t Agent::recvAppendEntriesRPC (term_t term, id_t leaderId, index_t prevIndex, diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index f55d041788..e38b2243a5 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -37,8 +37,8 @@ using namespace arangodb::velocypack; void Constituent::configure(Agent* agent) { _agent = agent; _votes.resize(_agent->config().end_points.size()); - if (_agent->config().id == (_votes.size()-1)) // Last will notify eveyone - notifyAll(); + if (_agent->config().id == (_votes.size()-1)) // Last will (notify everyone) + notifyAll(); } Constituent::Constituent() : Thread("Constituent"), _term(0), _id(0), @@ -130,7 +130,7 @@ size_t Constituent::notifyAll () { } -bool Constituent::vote( +bool Constituent::vote ( term_t term, id_t leaderId, index_t prevLogIndex, term_t prevLogTerm) { if (leaderId == _id) { // Won't vote for myself should never happen. return false; // TODO: Assertion? diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index 457b2b9741..cc3eb47a55 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -180,6 +180,9 @@ inline std::ostream& operator<< (std::ostream& os, std::vector cons return os; } + +using namespace arangodb::velocypack; + enum NODE_EXPECTION {PATH_NOT_FOUND}; class Node { @@ -303,13 +306,12 @@ private: }; -//using namespace arangodb::velocypack; class Store : public Node { // Root node public: - Store () : Node("root") { - } - ~Store () {} + Store () : Node("root") {} + ~Store () {} + }; }} From 9f71872116d64adfd03c283d9339fbfebb28645c Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Thu, 3 Mar 2016 18:50:19 +0100 Subject: [PATCH 34/61] notifyall --- arangod/Agency/AgencyCommon.h | 12 +- arangod/Agency/Agent.cpp | 21 +++- arangod/Agency/Agent.h | 2 +- arangod/Agency/AgentCallback.cpp | 2 + arangod/Agency/ApplicationAgency.cpp | 7 +- arangod/Agency/ApplicationAgency.h | 1 + arangod/Agency/Constituent.cpp | 104 +++++++++++++----- arangod/Agency/Constituent.h | 4 +- arangod/RestHandler/RestAgencyPrivHandler.cpp | 11 +- 9 files changed, 116 insertions(+), 48 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 297e671665..d1966d35e7 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -62,18 +62,12 @@ template struct Config { T election_timeout; T append_entries_retry_interval; std::vector end_points; + bool notify; Config () : min_ping(.15), max_ping(.3) {}; Config (uint32_t i, T min_p, T max_p, T appent_i, - std::vector const& end_p) : + std::vector 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) {} -/* void print (arangodb::LoggerStream& l) const { - l << "Config: " - << "min_ping(" << min_ping << ")" - << "max_ping(" << max_ping << ")" - << "size(" << end_points.size() << ")" - << end_points; - }*/ + append_entries_retry_interval(appent_i), end_points(end_p) , notify(n){} inline size_t size() const {return end_points.size();} }; diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 1de843116a..794ba661bb 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -24,6 +24,9 @@ #include "Agent.h" #include "Basics/ConditionLocker.h" +#include +#include + #include using namespace arangodb::velocypack; @@ -56,9 +59,23 @@ inline size_t Agent::size() const { } priv_rpc_ret_t Agent::requestVote(term_t t, id_t id, index_t lastLogIndex, - index_t lastLogTerm) { + 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++; + } + } + } + return priv_rpc_ret_t( - _constituent.vote(id, t, lastLogIndex, lastLogTerm),this->term()); + _constituent.vote(id, t, lastLogIndex, lastLogTerm), this->term()); } Config const& Agent::config () const { diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index a584b18c7b..e75069f288 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -65,7 +65,7 @@ public: /** * @brief Vote request */ - priv_rpc_ret_t requestVote(term_t , id_t, index_t, index_t); + priv_rpc_ret_t requestVote(term_t , id_t, index_t, index_t, query_t const&); /** * @brief Provide configuration diff --git a/arangod/Agency/AgentCallback.cpp b/arangod/Agency/AgentCallback.cpp index adb6c25bd3..ae78744baf 100644 --- a/arangod/Agency/AgentCallback.cpp +++ b/arangod/Agency/AgentCallback.cpp @@ -21,6 +21,8 @@ bool AgentCallback::operator()(arangodb::ClusterCommResult* res) { std::shared_ptr 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"); diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index 52029c9145..57a1e5080b 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -36,7 +36,7 @@ using namespace arangodb::rest; ApplicationAgency::ApplicationAgency() : ApplicationFeature("agency"), _size(1), _min_election_timeout(.5), - _max_election_timeout(1.), _election_call_rate_mul(.75), + _max_election_timeout(5.), _election_call_rate_mul(2.5), _append_entries_retry_interval(1.0), _agent_id(std::numeric_limits::max()) { @@ -64,7 +64,8 @@ void ApplicationAgency::setupOptions( "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"); + "in seconds") + ("agency.notify", &_notify, "Notify others [beta :)]"); } @@ -103,7 +104,7 @@ bool ApplicationAgency::prepare() { _agent = std::unique_ptr( new agent_t(config_t( _agent_id, _min_election_timeout, _max_election_timeout, - _append_entries_retry_interval, _agency_endpoints))); + _append_entries_retry_interval, _agency_endpoints, _notify))); return true; diff --git a/arangod/Agency/ApplicationAgency.h b/arangod/Agency/ApplicationAgency.h index c00e1680f0..c895f98825 100644 --- a/arangod/Agency/ApplicationAgency.h +++ b/arangod/Agency/ApplicationAgency.h @@ -110,6 +110,7 @@ class ApplicationAgency : virtual public arangodb::rest::ApplicationFeature { 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 _agency_endpoints; /**< @brief agency adresses */ std::unique_ptr _agent; diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index 91d706e0e3..7970e9b392 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -23,10 +23,14 @@ #include "Cluster/ClusterComm.h" #include "Basics/Logger.h" +#include "Basics/ConditionLocker.h" #include "Constituent.h" #include "Agent.h" +#include +#include + #include #include @@ -39,7 +43,7 @@ void Constituent::configure(Agent* agent) { _votes.resize(size()); _id = _agent->config().id; LOG(WARN) << " +++ my id is " << _id << "agency size is " << size(); - if (_id == (size()-1)) // Last will (notify everyone) + if (_agent->config().notify) // Last will (notify everyone) notifyAll(); } @@ -55,6 +59,11 @@ duration_t Constituent::sleepFor (double min_t, double 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; } @@ -64,17 +73,23 @@ role_t Constituent::role () const { } 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; } @@ -135,29 +150,25 @@ size_t Constituent::notifyAll () { 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) { // Candidate with higher term: ALWAYS turn follower if not already - switch(_role) { - case(LEADER): // I was leading. What happened? - LOG(WARN) << "Cadidate with higher term. Becoming follower."; - _agent->report(RESIGNED_LEADERSHIP_FOR_HIGHER_TERM); - break; - case(CANDIDATE): // I was candidate. What happened? - LOG(WARN) << "Cadidate with higher term. Becoming follower. Something bad has happened?"; - _agent->report(RETRACTED_CANDIDACY_FOR_HIGHER_TERM); - break; - default: - break; - } + 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. - follow (term); + if (_role>FOLLOWER) + follow (term); + _cv.signal(); return true; } else { // Myself running or leading return false; @@ -177,7 +188,9 @@ const constituency_t& Constituent::gossip () { void Constituent::callElection() { _votes[_id] = true; // vote for myself - _term++; // raise my term + _cast = true; + if(_role == CANDIDATE) + _term++; // raise my term std::string body; std::vector results(_agent->config().end_points.size()); @@ -199,7 +212,11 @@ void Constituent::callElection() { } } - std::this_thread::sleep_for(sleepFor(0., .9*_agent->config().min_ping)); // Wait timeout + if (_role == CANDIDATE) { + std::this_thread::sleep_for(sleepFor(.5*_agent->config().min_ping, 1.*_agent->config().min_ping)); // Wait timeout + } else { + std::this_thread::sleep_for(sleepFor(.7*_agent->config().min_ping, .75*_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) != "") { @@ -212,19 +229,44 @@ void Constituent::callElection() { if (body->isEmpty()) { continue; } else { - if (!body->slice().hasKey("vote")) { // Answer has no vote. What happened? - _votes[i] = false; - continue; - } else { - _votes[i] = (body->slice().get("vote").isEqualString("TRUE")); // Record vote + 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 << " " < _term) { // follow? + follow(it.value.getUInt()); + break; + } + } + } else if (key == "voteGranted") { + if (it.value.isBool()) { + _votes[i] = it.value.getBool(); + } + } + } } + LOG(WARN) << body->toJson(); } - LOG(WARN) << body->toString(); } 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() { @@ -235,12 +277,18 @@ void Constituent::run() { // Always start off as follower while (!this->isStopping()) { - if (_role == FOLLOWER) { - _cast = false; // New round set not cast vote - std::this_thread::sleep_for( // Sleep for random time - sleepFor(_agent->config().min_ping, _agent->config().max_ping)); - if (!_cast) + 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 } diff --git a/arangod/Agency/Constituent.h b/arangod/Agency/Constituent.h index db59ab0a56..768cb544db 100644 --- a/arangod/Agency/Constituent.h +++ b/arangod/Agency/Constituent.h @@ -139,9 +139,10 @@ private: * @brief Sleep for how long */ duration_t sleepFor(double, double); + double sleepFord(double, double); // mission critical - std::atomic _term; /**< @brief term number */ + term_t _term; /**< @brief term number */ std::atomic _cast; /**< @brief cast a vote this term */ std::atomic _state; /**< @brief State (follower, candidate, leader)*/ @@ -154,6 +155,7 @@ private: std::vector _votes; /**< @brief My list of votes cast in my favour*/ Agent* _agent; /**< @brief My boss */ + arangodb::basics::ConditionVariable _cv; // agency callbacks }; diff --git a/arangod/RestHandler/RestAgencyPrivHandler.cpp b/arangod/RestHandler/RestAgencyPrivHandler.cpp index 0192530768..4326f09482 100644 --- a/arangod/RestHandler/RestAgencyPrivHandler.cpp +++ b/arangod/RestHandler/RestAgencyPrivHandler.cpp @@ -86,7 +86,6 @@ HttpHandler::status_t RestAgencyPrivHandler::execute() { } else if (_request->suffix().size() > 1) { // request too long return reportTooManySuffices(); } else { - LOG(WARN) << _request->suffix()[0]; term_t term, prevLogTerm; id_t id; // leaderId for appendEntries, cadidateId for requestVote index_t prevLogIndex, leaderCommit; @@ -116,8 +115,8 @@ HttpHandler::status_t RestAgencyPrivHandler::execute() { readValue("candidateId", id) && readValue("prevLogIndex", prevLogIndex) && readValue("prevLogTerm", prevLogTerm)) { - priv_rpc_ret_t ret = _agent->requestVote (term, id, prevLogIndex, - 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)); } @@ -125,9 +124,12 @@ HttpHandler::status_t RestAgencyPrivHandler::execute() { 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); + 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 @@ -135,6 +137,7 @@ HttpHandler::status_t RestAgencyPrivHandler::execute() { } } result.close(); + LOG(WARN) << result.toJson(); VPackSlice s = result.slice(); generateResult(s); } catch (...) { From ee11ff13f6c1e72cef8d98d93b0fdd6bdea02439 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Fri, 4 Mar 2016 18:13:17 +0100 Subject: [PATCH 35/61] log & persist --- arangod/Agency/AgencyCommon.h | 64 ++++++-- arangod/Agency/Agent.cpp | 26 ++-- arangod/Agency/Agent.h | 2 +- arangod/Agency/ApplicationAgency.cpp | 4 +- arangod/Agency/ApplicationAgency.h | 1 - arangod/Agency/Constituent.cpp | 32 ++-- arangod/Agency/State.cpp | 35 ++--- arangod/Agency/State.h | 2 +- arangod/Agency/Store.h | 140 ++---------------- arangod/RestHandler/RestAgencyHandler.cpp | 68 ++++++--- arangod/RestHandler/RestAgencyHandler.h | 5 +- arangod/RestHandler/RestAgencyPrivHandler.cpp | 1 - 12 files changed, 164 insertions(+), 216 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index d1966d35e7..645f23b364 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -27,11 +27,17 @@ #include #include +#include +#include + #include #include #include +#include #include +#include + namespace arangodb { namespace consensus { @@ -52,26 +58,49 @@ enum role_t { // Role FOLLOWER, CANDIDATE, LEADER }; +enum AGENT_FAILURE { + PERSISTENCE, + TIMEOUT, + UNAVAILABLE, + PRECONDITION +}; + /** * @brief Agent configuration */ -template struct Config { +template +inline std::ostream& operator<< (std::ostream& l, std::vector const& v) { + for (auto const& i : v) + l << i << " "; + return l; +} +struct AgentConfiguration { id_t id; - T min_ping; - T max_ping; - T election_timeout; - T append_entries_retry_interval; + float min_ping; + float max_ping; + float election_timeout; + float append_entries_retry_interval; std::vector end_points; bool notify; - Config () : min_ping(.15), max_ping(.3) {}; - Config (uint32_t i, T min_p, T max_p, T appent_i, + AgentConfiguration () : min_ping(.15), max_ping(.3) {}; + AgentConfiguration (uint32_t i, float min_p, float max_p, float appent_i, std::vector 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){} inline size_t size() const {return end_points.size();} + inline std::string const 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 LoggerStream& operator<< (LoggerStream& l, AgentConfiguration const& c) { + l << c.toString(); + return l; + } }; - -using config_t = Config; // Configuration type +typedef AgentConfiguration config_t; struct constituent_t { // Constituent type id_t id; @@ -111,6 +140,7 @@ struct write_ret_t { }; using namespace std::chrono; +using buffer_t = std::shared_ptr>; /** * @brief State entry */ @@ -118,11 +148,18 @@ struct log_t { index_t index; term_t term; id_t leaderId; - std::string entry; + //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, 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(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 AGENCY_EXCEPTION { @@ -153,10 +190,5 @@ struct priv_rpc_ret_t { }} -inline arangodb::LoggerStream& operator<< ( - arangodb::LoggerStream& l, arangodb::consensus::config_t const& c) { - return l; -} - #endif // __ARANGODB_CONSENSUS_AGENT__ diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 794ba661bb..3440998adf 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -38,6 +38,7 @@ Agent::Agent () : Thread ("Agent"), _stopping(false) {} Agent::Agent (config_t const& config) : Thread ("Agent"), _config(config) { _constituent.configure(this); + _confirmed.resize(size(),0); } id_t Agent::id() const { return _config.id;} @@ -69,16 +70,17 @@ priv_rpc_ret_t Agent::requestVote(term_t t, id_t id, index_t lastLogIndex, std::string const value(i.value.copyString()); if (key == "endpoint") _config.end_points[j] = value; - j++; + ++j; } } + LOG(WARN) << _config; } return priv_rpc_ret_t( _constituent.vote(id, t, lastLogIndex, lastLogTerm), this->term()); } -Config const& Agent::config () const { +config_t const& Agent::config () const { return _config; } @@ -97,7 +99,10 @@ id_t Agent::leaderID () const { 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(); @@ -149,12 +154,12 @@ priv_rpc_ret_t Agent::recvAppendEntriesRPC (term_t term, id_t leaderId, index_t 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); - } + //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()); } @@ -176,7 +181,7 @@ append_entries_t Agent::sendAppendEntriesRPC ( Builder builder; for (size_t i = 0; i < entries.size(); ++i) { builder.add ("index", Value(std::to_string(entries.indices[i]))); - builder.add ("query", Value(_state[entries.indices[i]].entry)); + builder.add ("query", Builder(*_state[entries.indices[i]].entry).slice()); } builder.close(); @@ -192,7 +197,6 @@ append_entries_t Agent::sendAppendEntriesRPC ( } -//query_ret_t write_ret_t Agent::write (query_t const& query) { // Signal auf die _cv if (_constituent.leading()) { // We are leading if (true/*_spear_head.apply(query)*/) { // We could apply to spear head? @@ -202,7 +206,7 @@ write_ret_t Agent::write (query_t const& query) { // Signal auf die _cv MUTEX_LOCKER(mutexLocker, _confirmedLock); _confirmed[id()]++; } - return write_ret_t(true,id(),indices); // indices + return write_ret_t(true,id(),indices); // indices } else { throw QUERY_NOT_APPLICABLE; } diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index e75069f288..2d31a66d76 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -70,7 +70,7 @@ public: /** * @brief Provide configuration */ - Config const& config () const; + config_t const& config () const; /** * @brief Start thread diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index 57a1e5080b..e1aa132d2c 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -36,7 +36,7 @@ using namespace arangodb::rest; ApplicationAgency::ApplicationAgency() : ApplicationFeature("agency"), _size(1), _min_election_timeout(.5), - _max_election_timeout(5.), _election_call_rate_mul(2.5), + _max_election_timeout(2.0), _election_call_rate_mul(2.5), _append_entries_retry_interval(1.0), _agent_id(std::numeric_limits::max()) { @@ -102,7 +102,7 @@ bool ApplicationAgency::prepare() { _agency_endpoints.begin() + _agent_id); _agent = std::unique_ptr( - new agent_t(config_t( + new agent_t(arangodb::consensus::config_t( _agent_id, _min_election_timeout, _max_election_timeout, _append_entries_retry_interval, _agency_endpoints, _notify))); diff --git a/arangod/Agency/ApplicationAgency.h b/arangod/Agency/ApplicationAgency.h index c895f98825..1f158b110d 100644 --- a/arangod/Agency/ApplicationAgency.h +++ b/arangod/Agency/ApplicationAgency.h @@ -39,7 +39,6 @@ class Task; /// @brief application server with agency //////////////////////////////////////////////////////////////////////////////// using agent_t = consensus::Agent; -using config_t = consensus::Config; class ApplicationAgency : virtual public arangodb::rest::ApplicationFeature { diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index 7970e9b392..a25d294956 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -40,11 +40,16 @@ using namespace arangodb::velocypack; void Constituent::configure(Agent* agent) { _agent = agent; - _votes.resize(size()); - _id = _agent->config().id; - LOG(WARN) << " +++ my id is " << _id << "agency size is " << size(); - if (_agent->config().notify) // Last will (notify everyone) - notifyAll(); + 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), @@ -72,7 +77,7 @@ role_t Constituent::role () const { return _role; } -void Constituent::follow(term_t term) { +void Constituent::follow (term_t term) { if (_role > FOLLOWER) LOG(WARN) << "Converted to follower in term " << _term ; _term = term; @@ -80,20 +85,21 @@ void Constituent::follow(term_t term) { _role = FOLLOWER; } -void Constituent::lead() { +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() { +void Constituent::candidate () { if (_role != CANDIDATE) LOG(WARN) << "Converted to candidate in term " << _term ; _role = CANDIDATE; } bool Constituent::leading () const { + LOG(WARN) << _role; return _role == LEADER; } @@ -150,7 +156,7 @@ size_t Constituent::notifyAll () { 0.0, true); } } - + return size()-1; } @@ -212,11 +218,7 @@ void Constituent::callElection() { } } - if (_role == CANDIDATE) { - std::this_thread::sleep_for(sleepFor(.5*_agent->config().min_ping, 1.*_agent->config().min_ping)); // Wait timeout - } else { - std::this_thread::sleep_for(sleepFor(.7*_agent->config().min_ping, .75*_agent->config().min_ping)); // Wait timeout - } + 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) != "") { @@ -276,7 +278,7 @@ void Constituent::beginShutdown() { void Constituent::run() { // Always start off as follower - while (!this->isStopping()) { + while (!this->isStopping() && size() > 1) { if (_role == FOLLOWER) { bool cast; { diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index 54ff1cbd8e..94a3d0fd59 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -23,6 +23,9 @@ #include "State.h" +#include +#include + #include #include @@ -32,7 +35,8 @@ using namespace arangodb::velocypack; State::State() { load(); if (!_log.size()) - _log.push_back(log_t(index_t(0), term_t(0), id_t(0), std::string())); + _log.push_back(log_t(index_t(0), term_t(0), id_t(0), + std::make_shared>())); } State::~State() {} @@ -41,31 +45,24 @@ State::~State() {} std::vector State::log (query_t const& query, term_t term, id_t lid) { MUTEX_LOCKER(mutexLocker, _logLock); std::vector idx; - Builder builder; - for (size_t i = 0; i < query->slice().length(); ++i) { + size_t j = 0; + for (auto const& i : VPackArrayIterator(query->slice())) { + std::shared_ptr> buf = std::make_shared>(); + buf->append ((char const*)i.begin(), i.byteSize()); idx.push_back(_log.back().index+1); - _log.push_back(log_t(idx[i], term, lid, query->slice()[i].toString())); - builder.add("query", query->slice()[i]); - builder.add("idx", Value(idx[i])); - builder.add("term", Value(term)); - builder.add("leaderID", Value(lid)); - builder.close(); + _log.push_back(log_t(idx[j++], term, lid, buf)); } - save (builder); // persist + // save (builder); return idx; } //Follower -void State::log (std::string const& query, index_t index, term_t term, id_t lid) { +void State::log (query_t const& query, index_t index, term_t term, id_t lid) { MUTEX_LOCKER(mutexLocker, _logLock); - _log.push_back(log_t(index, term, lid, query)); - Builder builder; - builder.add("query", Value(query)); // query - builder.add("idx", Value(index)); // log index - builder.add("term", Value(term)); // term - builder.add("leaderID", Value(lid)); // leader id - builder.close(); - save (builder); + std::shared_ptr> buf = std::make_shared>(); + 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) { diff --git a/arangod/Agency/State.h b/arangod/Agency/State.h index 7475946f7e..99caaa79e7 100644 --- a/arangod/Agency/State.h +++ b/arangod/Agency/State.h @@ -75,7 +75,7 @@ public: /** * @brief Log entry follower */ - void log (std::string const& query, index_t, term_t term, id_t lid); + void log (query_t const& query, index_t, term_t term, id_t lid); /** * @brief Find entry at index with term diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index d058c955c1..a09f5b819f 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -32,131 +32,14 @@ #include #include #include -#include +#include + +#include +#include namespace arangodb { namespace consensus { -template struct TypeTraits { - static bool supported () {return false;} -}; -template<> struct TypeTraits { - static bool supported () {return false;} -}; -template<> struct TypeTraits { - static bool supported () {return false;} -}; -template<> struct TypeTraits { - static bool supported () {return false;} -}; -template<> struct TypeTraits { - static bool supported () {return false;} -}; - -template -using Type = typename std::decay::type>::type; - -struct Any { - - bool is_null() const { return !_base_ptr; } - bool not_null() const { return _base_ptr; } - - template Any(S&& value) - : _base_ptr(new Derived>(std::forward(value))) {} - - template bool is() const { - typedef Type T; - auto derived = dynamic_cast*> (_base_ptr); - return derived; - } - - template Type& as() const { - typedef Type T; - auto derived = dynamic_cast*> (_base_ptr); - if (!derived) - throw std::bad_cast(); - return derived->value; - } - - template operator S() { - return as>(); - } - - Any() : _base_ptr(nullptr) {} - - Any(Any& that) : _base_ptr(that.clone()) {} - - Any(Any&& that) : _base_ptr(that._base_ptr) { - that._base_ptr = nullptr; - } - - Any(const Any& that) : _base_ptr(that.clone()) {} - - Any(const Any&& that) : _base_ptr(that.clone()) {} - - Any& operator=(const Any& a) { - if (_base_ptr == a._base_ptr) - return *this; - auto old__base_ptr = _base_ptr; - _base_ptr = a.clone(); - if (old__base_ptr) - delete old__base_ptr; - return *this; - } - - Any& operator=(Any&& a) { - if (_base_ptr == a._base_ptr) - return *this; - std::swap(_base_ptr, a._base_ptr); - return *this; - } - - ~Any() { - if (_base_ptr) - delete _base_ptr; - } - - friend std::ostream& operator<<(std::ostream& os, const Any& a) { - try { - os << a.as(); - } catch (std::bad_cast const&) { - try { - os << a.as(); - } catch (std::bad_cast const&) { - try { - os << "\"" << a.as() << "\""; - } catch (std::bad_cast const& e) { - throw e; - } - } - } - return os; - } - -private: - - struct Base { - virtual ~Base() {} - virtual Base* clone() const = 0; - }; - - template struct Derived : Base { - template Derived(S&& value) : value(std::forward(value)) { } - T value; - Base* clone() const { return new Derived(value); } - }; - - Base* clone() const { - if (_base_ptr) - return _base_ptr->clone(); - else - return nullptr; - } - - Base* _base_ptr; - -}; - static inline std::vector split (std::string str, const std::string& dlm) { std::vector sv; @@ -188,15 +71,14 @@ public: typedef std::vector PathType; typedef std::map> Children; - - Node (std::string const& name) : _parent(nullptr), _name(name), _value("") {} - + + Node (std::string const& name) : _parent(nullptr), _name(name), _value(Buffer()) {} + ~Node () {} - + std::string const& name() const {return _name;} - template - Node& operator= (T const& t) { // Assign value (become leaf) + template Node& operator= (T const& t) { // Assign value (become leaf) _children.clear(); _value = t; return *this; @@ -289,7 +171,7 @@ public: for (auto const& i : n._children) os << *(i.second); } else { - os << n._value << std::endl; + os << n._value.toString() << std::endl; } return os; } @@ -299,7 +181,7 @@ protected: private: NodeType _type; std::string _name; - Any _value; + Buffer _value; Children _children; }; diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index e5ecba9d6f..ab5b9313cd 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -76,29 +76,53 @@ inline HttpHandler::status_t RestAgencyHandler::redirect (id_t leader_id) { return HttpHandler::status_t(HANDLER_DONE); } -inline HttpHandler::status_t RestAgencyHandler::handleReadWrite () { - bool accepted; +inline HttpHandler::status_t RestAgencyHandler::handleWrite () { arangodb::velocypack::Options options; - - if (_request->suffix()[0] == "write") { + if (_request->requestType() == HttpRequest::HTTP_REQUEST_POST) { write_ret_t ret = _agent->write (_request->toVelocyPack(&options)); - accepted = ret.accepted; - _agent->waitFor (ret.indices.back()); // Wait for confirmation (last entry is enough) + if (ret.accepted) { + Builder body; + body.add(VPackValue(VPackValueType::Object)); + _agent->waitFor (ret.indices.back()); // Wait for confirmation (last entry is enough) + body.close(); + generateResult(body.slice()); + } else { + generateError(HttpResponse::TEMPORARY_REDIRECT,307); + } } else { - read_ret_t ret = _agent->read(_request->toVelocyPack(&options)); - accepted = ret.accepted; - ret.result->close(); - generateResult(ret.result->slice()); + generateError(HttpResponse::METHOD_NOT_ALLOWED,405); } + return HttpHandler::status_t(HANDLER_DONE); +} - if (!accepted) { // We accepted the request - //ret.result->close(); - //generateResult(ret.result->slice()); - } else { // We redirect the request - //_response->setHeader("Location", _agent->config().endpoints[ret.redirect]); - generateError(HttpResponse::TEMPORARY_REDIRECT,307); +inline HttpHandler::status_t RestAgencyHandler::handleRead () { + arangodb::velocypack::Options options; + if (_request->requestType() != HttpRequest::HTTP_REQUEST_POST) { + read_ret_t ret = _agent->read (_request->toVelocyPack(&options)); + 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 +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); } @@ -109,9 +133,15 @@ HttpHandler::status_t RestAgencyHandler::execute() { } else if (_request->suffix().size() > 1) { // path size >= 2 return reportTooManySuffices(); } else { - if (_request->suffix()[0] == "write" || - _request->suffix()[0] == "read") { // write to / read from agency - return handleReadWrite(); + 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(); } diff --git a/arangod/RestHandler/RestAgencyHandler.h b/arangod/RestHandler/RestAgencyHandler.h index 62e4282ace..28a9c5b8b1 100644 --- a/arangod/RestHandler/RestAgencyHandler.h +++ b/arangod/RestHandler/RestAgencyHandler.h @@ -49,7 +49,10 @@ class RestAgencyHandler : public arangodb::RestBaseHandler { status_t reportTooManySuffices() ; status_t reportUnknownMethod() ; status_t redirect(id_t leader_id) ; - status_t handleReadWrite() ; + status_t handleRead() ; + status_t handleWrite() ; + status_t handleTest(); + status_t reportMethodNotAllowed(); consensus::Agent* _agent; diff --git a/arangod/RestHandler/RestAgencyPrivHandler.cpp b/arangod/RestHandler/RestAgencyPrivHandler.cpp index 4326f09482..2c2c8239a4 100644 --- a/arangod/RestHandler/RestAgencyPrivHandler.cpp +++ b/arangod/RestHandler/RestAgencyPrivHandler.cpp @@ -137,7 +137,6 @@ HttpHandler::status_t RestAgencyPrivHandler::execute() { } } result.close(); - LOG(WARN) << result.toJson(); VPackSlice s = result.slice(); generateResult(s); } catch (...) { From 8ba74610f9cd5ebcad4dc96a9a2b4db99c2ef87a Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 7 Mar 2016 11:15:53 +0100 Subject: [PATCH 36/61] State::save, Store::apply, Store::read --- arangod/Agency/AgencyCommon.h | 28 +++++++++++----- arangod/Agency/Agent.cpp | 25 ++++++++------ arangod/Agency/State.cpp | 41 ++++++++++++++++------- arangod/Agency/State.h | 22 +++++++----- arangod/Agency/Store.h | 21 ++++++++++++ arangod/RestHandler/RestAgencyHandler.cpp | 7 +++- 6 files changed, 103 insertions(+), 41 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 645f23b364..2b7e096a08 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -81,23 +81,34 @@ struct AgentConfiguration { float election_timeout; float append_entries_retry_interval; std::vector 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 const& end_p, bool n = false) : + std::vector 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){} - inline size_t size() const {return end_points.size();} - inline std::string const toString() const { + 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; } - friend LoggerStream& operator<< (LoggerStream& l, AgentConfiguration const& c) { - l << c.toString(); - return l; + inline std::string const toString() const { + std::stringstream s; + s << *this; + return s.str(); } }; typedef AgentConfiguration config_t; @@ -133,6 +144,7 @@ struct write_ret_t { bool accepted; // Query processed id_t redirect; // Otherwise redirect to std::vector 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, index_list_t const& idx = index_list_t()) : accepted(a), redirect(id), indices(idx) {} write_ret_t (bool a, id_t id, std::vector const& idx) : @@ -162,7 +174,7 @@ struct log_t { } }; -enum AGENCY_EXCEPTION { +enum agencyException { QUERY_NOT_APPLICABLE }; diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 3440998adf..8e2d6273d9 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -37,6 +37,8 @@ namespace consensus { Agent::Agent () : Thread ("Agent"), _stopping(false) {} Agent::Agent (config_t const& config) : Thread ("Agent"), _config(config) { + if (!_state.load()) + LOG(FATAL) << "Failed to load persistent state on statup."; _constituent.configure(this); _confirmed.resize(size(),0); } @@ -198,25 +200,26 @@ append_entries_t Agent::sendAppendEntriesRPC ( } write_ret_t Agent::write (query_t const& query) { // Signal auf die _cv - if (_constituent.leading()) { // We are leading - if (true/*_spear_head.apply(query)*/) { // We could apply to spear head? - std::vector indices = // otherwise through - _state.log (query, term(), id()); // Append to my own log - { - MUTEX_LOCKER(mutexLocker, _confirmedLock); - _confirmed[id()]++; - } - return write_ret_t(true,id(),indices); // indices - } else { + if (_constituent.leading()) { // Leading + std::vector applied = _spear_head.apply(query); // Apply to spearhead + if (applied.size() == 0) { throw QUERY_NOT_APPLICABLE; } - } else { // We redirect + std::vector indices = + _state.log (query, applied, term(), id()); // Append to log w/ indicies + { + MUTEX_LOCKER(mutexLocker, _confirmedLock); + _confirmed[id()]++; // Confirm myself + } + return write_ret_t(true,id(),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 + _read_db.read (query); return read_ret_t(true,_constituent.leaderID());//(query); //TODO: } else { // We redirect return read_ret_t(false,_constituent.leaderID()); diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index 94a3d0fd59..7014e280a7 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -27,13 +27,14 @@ #include #include +#include #include using namespace arangodb::consensus; using namespace arangodb::velocypack; +using namespace arangodb::rest; -State::State() { - load(); +State::State(std::string const& end_point) : _end_point(end_point) { if (!_log.size()) _log.push_back(log_t(index_t(0), term_t(0), id_t(0), std::make_shared>())); @@ -41,18 +42,33 @@ State::State() { State::~State() {} +bool State::save (arangodb::velocypack::Slice const& slice, double timeout) { + static const std::string path = "/_api/cursor"; + std::map headerFields; + std::unique_ptr res = + arangodb::ClusterComm::instance()->syncRequest ( + "1", 1, _end_point, HttpRequest::HTTP_REQUEST_POST, path, slice.toJson(), + headerFields, 0.0); + return (res->status == CL_COMM_SENT); // TODO: More verbose result +} + //Leader -std::vector State::log (query_t const& query, term_t term, id_t lid) { - MUTEX_LOCKER(mutexLocker, _logLock); +std::vector State::log ( + query_t const& query, std::vector const& appl, term_t term, id_t lid) { std::vector idx; + std::vector good = appl; size_t j = 0; + MUTEX_LOCKER(mutexLocker, _logLock); // log entries must stay in order for (auto const& i : VPackArrayIterator(query->slice())) { - std::shared_ptr> buf = std::make_shared>(); - buf->append ((char const*)i.begin(), i.byteSize()); - idx.push_back(_log.back().index+1); - _log.push_back(log_t(idx[j++], term, lid, buf)); + if (good[j]) { + std::shared_ptr> buf = std::make_shared>(); + buf->append ((char const*)i.begin(), i.byteSize()); + idx.push_back(_log.back().index+1); + _log.push_back(log_t(idx[j], term, lid, buf)); // log to RAM + save(i); // log to disk + ++j; + } } - // save (builder); return idx; } @@ -108,10 +124,9 @@ collect_ret_t State::collectFrom (index_t index) { return collect_ret_t(prev_log_index, prev_log_term, work); } -bool State::save (arangodb::velocypack::Builder const&) { - // Persist to arango db - // AQL votedFor, lastCommit - return true; +bool State::setEndPoint (std::string const& end_point) { + _end_point = end_point; + return true; // TODO: check endpoint }; bool State::load () { diff --git a/arangod/Agency/State.h b/arangod/Agency/State.h index 99caaa79e7..f883e98747 100644 --- a/arangod/Agency/State.h +++ b/arangod/Agency/State.h @@ -55,7 +55,7 @@ public: /** * @brief Default constructor */ - State (); + State (std::string const& end_point = "tcp://localhost:8529"); /** * @brief Default Destructor @@ -70,7 +70,7 @@ public: /** * @brief Log entries (leader) */ - std::vector log (query_t const& query, term_t term, id_t lid); + std::vector log (query_t const& query, std::vector const& indices, term_t term, id_t lid); /** * @brief Log entry follower @@ -96,21 +96,27 @@ public: * @brief last log entry */ log_t const& lastLog () const; - -private: - + /** - * @brief Save currentTerm, votedFor, log entries + * @brief Set endpoint */ - bool save (arangodb::velocypack::Builder const&); + bool setEndPoint (std::string const&); /** * @brief Load persisted data from above or start with empty log */ bool load (); - mutable arangodb::Mutex _logLock; /**< @brief Mutex for modifying _log */ +private: + + /** + * @brief Save currentTerm, votedFor, log entries + */ + bool save (arangodb::velocypack::Slice const&, double timeout = 0.0); + + mutable arangodb::Mutex _logLock; /**< @brief Mutex for modifying _log */ std::vector _log; /**< @brief State entries */ + std::string _end_point; /**< @brief persistence end point */ }; diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index a09f5b819f..a50921713a 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -24,6 +24,8 @@ #ifndef __ARANGODB_CONSENSUS_STORE__ #define __ARANGODB_CONSENSUS_STORE__ +#include "AgencyCommon.h" + #include #include #include @@ -176,6 +178,25 @@ public: return os; } + bool apply (arangodb::velocypack::Slice const& slice) { + // TODO apply slice to database + return true; + } + + std::vector apply (query_t const& query) { + std::vector applied; + for (auto const& i : VPackArrayIterator(query->slice())) { + applied.push_back(apply(i)); + } + return applied; + } + + query_t read (query_t const& query) const { + // TODO: Run through JSON and asseble result + query_t ret = std::make_shared(); + return ret; + } + protected: Node* _parent; private: diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index ab5b9313cd..97c2a1933f 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -79,7 +79,12 @@ inline HttpHandler::status_t RestAgencyHandler::redirect (id_t leader_id) { inline HttpHandler::status_t RestAgencyHandler::handleWrite () { arangodb::velocypack::Options options; if (_request->requestType() == HttpRequest::HTTP_REQUEST_POST) { - write_ret_t ret = _agent->write (_request->toVelocyPack(&options)); + write_ret_t ret; + try { + ret = _agent->write (_request->toVelocyPack(&options)); + } catch (agencyException const& e) { + generateError(HttpResponse::PRECONDITION_FAILED,412); + } if (ret.accepted) { Builder body; body.add(VPackValue(VPackValueType::Object)); From 702a510f9e3fafb5384ea858ee49e6e111dae89f Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 7 Mar 2016 17:12:07 +0100 Subject: [PATCH 37/61] State::save, Store::apply, Store::read --- arangod/Agency/AgencyCommon.h | 9 +- arangod/Agency/Agent.cpp | 25 ++-- arangod/Agency/State.cpp | 135 +++++++++++++++++++--- arangod/Agency/State.h | 10 +- arangod/Agency/Store.h | 16 ++- arangod/RestHandler/RestAgencyHandler.cpp | 12 +- 6 files changed, 161 insertions(+), 46 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 2b7e096a08..dc928b7c32 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -143,12 +143,13 @@ typedef std::initializer_list index_list_t; struct write_ret_t { bool accepted; // Query processed id_t redirect; // Otherwise redirect to + std::vector applied; std::vector 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, index_list_t const& idx = index_list_t()) : - accepted(a), redirect(id), indices(idx) {} - write_ret_t (bool a, id_t id, std::vector const& idx) : - accepted(a), redirect(id), indices(idx) {} + write_ret_t (bool a, id_t id) : accepted(a), redirect(id) {} + write_ret_t (bool a, id_t id, std::vector const& app, + std::vector const& idx) : + accepted(a), redirect(id), applied(app), indices(idx) {} }; using namespace std::chrono; diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 8e2d6273d9..d44347cddc 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -28,6 +28,7 @@ #include #include +#include using namespace arangodb::velocypack; @@ -37,6 +38,7 @@ 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()]); if (!_state.load()) LOG(FATAL) << "Failed to load persistent state on statup."; _constituent.configure(this); @@ -199,20 +201,23 @@ append_entries_t Agent::sendAppendEntriesRPC ( } -write_ret_t Agent::write (query_t const& query) { // Signal auf die _cv +write_ret_t Agent::write (query_t const& query) { if (_constituent.leading()) { // Leading + MUTEX_LOCKER(mutexLocker, _confirmedLock); std::vector applied = _spear_head.apply(query); // Apply to spearhead - if (applied.size() == 0) { - throw QUERY_NOT_APPLICABLE; - } + std::vector indices = - _state.log (query, applied, term(), id()); // Append to log w/ indicies - { - MUTEX_LOCKER(mutexLocker, _confirmedLock); - _confirmed[id()]++; // Confirm myself + _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 + } } - return write_ret_t(true,id(),indices); // Indices to wait for to rest - } else { // Leading else redirect + + _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()); } } diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index 7014e280a7..9a774a031c 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -34,7 +34,7 @@ using namespace arangodb::consensus; using namespace arangodb::velocypack; using namespace arangodb::rest; -State::State(std::string const& end_point) : _end_point(end_point) { +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>())); @@ -42,20 +42,50 @@ State::State(std::string const& end_point) : _end_point(end_point) { State::~State() {} -bool State::save (arangodb::velocypack::Slice const& slice, double timeout) { - static const std::string path = "/_api/cursor"; - std::map headerFields; - std::unique_ptr res = - arangodb::ClusterComm::instance()->syncRequest ( - "1", 1, _end_point, HttpRequest::HTTP_REQUEST_POST, path, slice.toJson(), - headerFields, 0.0); - return (res->status == CL_COMM_SENT); // TODO: More verbose result +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 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 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 State::log ( query_t const& query, std::vector const& appl, term_t term, id_t lid) { - std::vector idx; + std::vector idx(appl.size()); std::vector good = appl; size_t j = 0; MUTEX_LOCKER(mutexLocker, _logLock); // log entries must stay in order @@ -63,9 +93,9 @@ std::vector State::log ( if (good[j]) { std::shared_ptr> buf = std::make_shared>(); buf->append ((char const*)i.begin(), i.byteSize()); - idx.push_back(_log.back().index+1); + idx[j] = _log.back().index+1; _log.push_back(log_t(idx[j], term, lid, buf)); // log to RAM - save(i); // log to disk + save(i, idx[j], term); // log to disk ++j; } } @@ -126,14 +156,83 @@ collect_ret_t State::collectFrom (index_t index) { bool State::setEndPoint (std::string const& end_point) { _end_point = end_point; - return true; // TODO: check endpoint -}; - -bool State::load () { - // Read all from arango db - //return load_ret_t (currentTerm, votedFor) + _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 headerFields; + std::unique_ptr 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 headerFields; + Builder body; + body.add(VPackValue(VPackValueType::Object)); + body.add("name", Value(name)); + body.close(); + std::unique_ptr 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) { + std::this_thread::sleep_for(std::chrono::duration(2.0)); + if (checkDBs()) { + + std::stringstream path; + path << "/_api/document?collection=" << name; + std::map headerFields; + std::unique_ptr 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 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; + } +} + diff --git a/arangod/Agency/State.h b/arangod/Agency/State.h index f883e98747..8f4990861e 100644 --- a/arangod/Agency/State.h +++ b/arangod/Agency/State.h @@ -112,11 +112,19 @@ private: /** * @brief Save currentTerm, votedFor, log entries */ - bool save (arangodb::velocypack::Slice const&, double timeout = 0.0); + 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; /**< @brief State entries */ std::string _end_point; /**< @brief persistence end point */ + bool _dbs_checked; }; diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index a50921713a..ce72453a0e 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -178,13 +178,9 @@ public: return os; } - bool apply (arangodb::velocypack::Slice const& slice) { - // TODO apply slice to database - return true; - } - - std::vector apply (query_t const& query) { + std::vector apply (query_t const& query) { std::vector applied; + MUTEX_LOCKER(mutexLocker, _storeLock); for (auto const& i : VPackArrayIterator(query->slice())) { applied.push_back(apply(i)); } @@ -192,6 +188,7 @@ public: } query_t read (query_t const& query) const { + MUTEX_LOCKER(mutexLocker, _storeLock); // TODO: Run through JSON and asseble result query_t ret = std::make_shared(); return ret; @@ -200,10 +197,17 @@ public: protected: Node* _parent; private: + bool apply (arangodb::velocypack::Slice const& slice) { + // TODO apply slice to database + return true; + } + NodeType _type; std::string _name; Buffer _value; Children _children; + mutable arangodb::Mutex _storeLock; + }; diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index 97c2a1933f..f157a01f93 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -77,18 +77,16 @@ inline HttpHandler::status_t RestAgencyHandler::redirect (id_t leader_id) { } inline HttpHandler::status_t RestAgencyHandler::handleWrite () { - arangodb::velocypack::Options options; + arangodb::velocypack::Options options; // TODO: User not wait. if (_request->requestType() == HttpRequest::HTTP_REQUEST_POST) { - write_ret_t ret; - try { - ret = _agent->write (_request->toVelocyPack(&options)); - } catch (agencyException const& e) { - generateError(HttpResponse::PRECONDITION_FAILED,412); - } + write_ret_t ret = _agent->write (_request->toVelocyPack(&options)); 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(ret.applied[i]), Value(ret.indices[i])); + } body.close(); generateResult(body.slice()); } else { From 1cb51b022a70557856d10b7736d1b01e09067b7d Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 7 Mar 2016 17:38:34 +0100 Subject: [PATCH 38/61] merging from devel --- arangod/Agency/Agent.cpp | 4 ++++ arangod/Agency/Agent.h | 2 ++ arangod/Agency/Store.h | 18 +++--------------- arangod/RestServer/ArangoServer.cpp | 3 +++ 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index d44347cddc..f94f928f32 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -201,6 +201,10 @@ append_entries_t Agent::sendAppendEntriesRPC ( } +bool Agent::load () { + return _state.load(); +} + write_ret_t Agent::write (query_t const& query) { if (_constituent.leading()) { // Leading MUTEX_LOCKER(mutexLocker, _confirmedLock); diff --git a/arangod/Agency/Agent.h b/arangod/Agency/Agent.h index 2d31a66d76..b60897c3a9 100644 --- a/arangod/Agency/Agent.h +++ b/arangod/Agency/Agent.h @@ -99,6 +99,8 @@ public: bool lead (); + bool load (); + /** * @brief Attempt write */ diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index ce72453a0e..01d72889bb 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -42,18 +42,6 @@ namespace arangodb { namespace consensus { -static inline std::vector -split (std::string str, const std::string& dlm) { - std::vector sv; - size_t start = (str.find('/') == 0) ? 1:0, end = 0; - while (end != std::string::npos) { - end = str.find (dlm, start); - sv.push_back(str.substr(start, (end == std::string::npos) ? std::string::npos : end - start)); - start = ((end > (std::string::npos - dlm.size())) ? std::string::npos : end + dlm.size()); - } - return sv; -} - enum NodeType {NODE, LEAF}; inline std::ostream& operator<< (std::ostream& os, std::vector const& sv) { @@ -74,11 +62,11 @@ public: typedef std::vector PathType; typedef std::map> Children; - Node (std::string const& name) : _parent(nullptr), _name(name), _value(Buffer()) {} + Node (std::string const& name); - ~Node () {} + ~Node (); - std::string const& name() const {return _name;} + std::string const& name() const; template Node& operator= (T const& t) { // Assign value (become leaf) _children.clear(); diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 47d35c9c1d..33ca494636 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -1888,6 +1888,9 @@ int ArangoServer::runServer(TRI_vocbase_t* vocbase) { waitForHeartbeat(); HttpHandlerFactory::setMaintenance(false); + if(_applicationAgency->agent()!=nullptr) + _applicationAgency->agent().load(); + // just wait until we are signalled _applicationServer->wait(); From 3a239fe1d5301dcda916a192dbd7aba0a0bd3d7a Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 9 Mar 2016 09:58:43 +0100 Subject: [PATCH 39/61] Store intestinals into cpp file. Write to db ok. --- arangod/Agency/Agent.cpp | 12 +- arangod/Agency/Constituent.cpp | 1 - arangod/Agency/State.cpp | 4 +- arangod/Agency/Store.cpp | 185 ++++++++++++++++++++++ arangod/Agency/Store.h | 129 ++++----------- arangod/CMakeLists.txt | 1 + arangod/RestHandler/RestAgencyHandler.cpp | 2 +- arangod/RestServer/ArangoServer.cpp | 4 +- 8 files changed, 230 insertions(+), 108 deletions(-) create mode 100644 arangod/Agency/Store.cpp diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index f94f928f32..8d77504909 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -39,8 +39,6 @@ Agent::Agent () : Thread ("Agent"), _stopping(false) {} Agent::Agent (config_t const& config) : Thread ("Agent"), _config(config) { _state.setEndPoint(_config.end_points[this->id()]); - if (!_state.load()) - LOG(FATAL) << "Failed to load persistent state on statup."; _constituent.configure(this); _confirmed.resize(size(),0); } @@ -202,14 +200,18 @@ append_entries_t Agent::sendAppendEntriesRPC ( } bool Agent::load () { - return _state.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 applied = _spear_head.apply(query); // Apply to spearhead - std::vector indices = _state.log (query, applied, term(), id()); // Append to log w/ indicies @@ -271,7 +273,7 @@ void Agent::beginShutdown() { // Stop callbacks //_agent_callback.shutdown(); // wake up all blocked rest handlers - //CONDITION_LOCKER(guard, _cv); + CONDITION_LOCKER(guard, _cv); //guard.broadcast(); } diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index a25d294956..36151a98cc 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -99,7 +99,6 @@ void Constituent::candidate () { } bool Constituent::leading () const { - LOG(WARN) << _role; return _role == LEADER; } diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index 9a774a031c..f65ac564f6 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -200,12 +200,12 @@ bool State::createCollection (std::string const& name) { } bool State::load () { - //loadCollection("log"); + loadCollection("log"); return true; } bool State::loadCollection (std::string const& name) { - std::this_thread::sleep_for(std::chrono::duration(2.0)); + if (checkDBs()) { std::stringstream path; diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp new file mode 100644 index 0000000000..4aec2b5eff --- /dev/null +++ b/arangod/Agency/Store.cpp @@ -0,0 +1,185 @@ +//////////////////////////////////////////////////////////////////////////////// +/// 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 +#include +#include + + +using namespace arangodb::consensus; + + +static inline std::vector split ( + std::string str, const std::string& dlm) { + std::vector sv; + size_t start = (str.find('/') == 0) ? 1:0, end = 0; + while (end != std::string::npos) { + end = str.find (dlm, start); + sv.push_back(str.substr(start, (end == std::string::npos) ? + std::string::npos : end - start)); + start = ((end > (std::string::npos - dlm.size())) ? + std::string::npos : end + dlm.size()); + } + return sv; +} + +Node::Node (std::string const& name) : _parent(nullptr), _name(name) {} + +Node::~Node() {} + +std::string const& Node::name() const {return _name;} + +Node& Node::operator= (Slice const& slice) { // Assign value (become leaf) + _children.clear(); + _value.reset(); + _value.append((char const*)slice.begin(), slice.byteSize()); + return *this; +} + +Node& Node::operator= (Node const& node) { // Assign node + *this = node; + return *this; +} + +inline NodeType Node::type() const {return _children.size() ? NODE : LEAF;} + +Node& Node::operator [](std::string name) { + return *_children[name]; +} + +bool Node::append (std::string const name, std::shared_ptr const node) { + if (node != nullptr) { + _children[name] = node; + } else { + _children[name] = std::make_shared(name); + } + _children[name]->_parent = this; + return true; +} +#include + +Node& Node::operator ()(std::vector& pv) { + if (pv.size()) { + auto found = _children.find(pv[0]); + if (found == _children.end()) { + _children[pv[0]] = std::make_shared(pv[0]); + found = _children.find(pv[0]); + found->second->_parent = this; + } + pv.erase(pv.begin()); + return (*found->second)(pv); + } else { + return *this; + } +} + +Node const& Node::operator ()(std::vector& pv) const { + if (pv.size()) { + auto found = _children.find(pv[0]); + if (found == _children.end()) { + throw PATH_NOT_FOUND; + } + pv.erase(pv.begin()); + return (*found->second)(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); +} + +Node const& Node::read (std::string const& path) const { + PathType pv = split(path,"/"); + return this->operator()(pv); +} + +Node& Node::write (std::string const& path) { + PathType pv = split(path,"/"); + return this->operator()(pv); +} + +std::vector Node::apply (query_t const& query) { + std::vector applied; + std::vector path; + MUTEX_LOCKER(storeLocker, _storeLock); + for (auto const& i : VPackArrayIterator(query->slice())) { + switch (i.length()) { + case 1: + applied.push_back(apply(i[0])); break; // no precond + case 2: + if (check(i[0])) { + applied.push_back(apply(i[1])); // 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; + } + } + return applied; +} + +bool Node::apply (arangodb::velocypack::Slice const& slice) { + if (slice.type() == ValueType::Object) { + for (auto const& i : VPackObjectIterator(slice)) { + std::string key = i.key.toString(); + auto found = _children.find(key); + if (found == _children.end()) { + _children[key] = std::make_shared(key); + found = _children.find(key); + found->second->_parent = this; + } + found->second->apply(i.value); + } + } else { + *this = slice; + } + return true; +} + +bool Node::check (arangodb::velocypack::Slice const& slice) { + return true; +} + +query_t Node::read (query_t const& query) const { + MUTEX_LOCKER(storeLocker, _storeLock); + // TODO: Run through JSON and asseble result + query_t ret = std::make_shared(); + return ret; +} + + diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index 01d72889bb..dc390e6aac 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -36,6 +36,8 @@ #include #include +#include +#include #include #include @@ -47,7 +49,6 @@ enum NodeType {NODE, LEAF}; inline std::ostream& operator<< (std::ostream& os, std::vector const& sv) { for (auto const& i : sv) os << i << " "; - os << std::endl; return os; } @@ -68,86 +69,34 @@ public: std::string const& name() const; - template Node& operator= (T const& t) { // Assign value (become leaf) - _children.clear(); - _value = t; - return *this; - } + Node& operator= (arangodb::velocypack::Slice const& t); - Node& operator= (Node const& node) { // Assign node - *this = node; - return *this; - } - - inline NodeType type() const {return _children.size() ? NODE : LEAF;} - - Node& operator [](std::string name) { - return *_children[name]; - } - - bool append (std::string const name, std::shared_ptr const node = nullptr) { - if (node != nullptr) { - _children[name] = node; - } else { - _children[name] = std::make_shared(name); - } - _children[name]->_parent = this; - return true; - } - - Node& operator ()(std::vector& pv) { - switch (pv.size()) { - case 0: // path empty - return *this; - break; - default: // at least one depth left - auto found = _children.find(pv[0]); - if (found == _children.end()) { - _children[pv[0]] = std::make_shared(pv[0]); - found = _children.find(pv[0]); - found->second->_parent = this; - } - pv.erase(pv.begin()); - return (*found->second)(pv); - break; - } - } + Node& operator= (Node const& node); - Node const& operator ()(std::vector& pv) const { - switch (pv.size()) { - case 0: // path empty - return *this; - break; - default: // at least one depth left - auto found = _children.find(pv[0]); - if (found == _children.end()) { - throw PATH_NOT_FOUND; - } - pv.erase(pv.begin()); - return (*found->second)(pv); - break; - } - } + NodeType type() const; + + Node& operator [](std::string name); + + bool append (std::string const name, + std::shared_ptr const node = nullptr); + + Node& operator ()(std::vector& pv); - Node const& operator ()(std::string const& path) const { - PathType pv = split(path,"/"); - return this->operator()(pv); - } + Node const& operator ()(std::vector& pv) const; + + Node const& operator ()(std::string const& path) const; - Node& operator ()(std::string const& path) { - PathType pv = split(path,"/"); - return this->operator()(pv); - } + Node& operator ()(std::string const& path); - Node const& read (std::string const& path) const { - PathType pv = split(path,"/"); - return this->operator()(pv); - } + Node const& read (std::string const& path) const; - Node& write (std::string const& path) { - PathType pv = split(path,"/"); - return this->operator()(pv); - } + Node& write (std::string const& path); + + friend std::ostream& operator<<(std::ostream& os, const Node& n); + + std::vector apply (query_t const& query); + + query_t read (query_t const& query) const; friend std::ostream& operator<<(std::ostream& os, const Node& n) { Node* par = n._parent; @@ -166,33 +115,19 @@ public: return os; } - std::vector apply (query_t const& query) { - std::vector applied; - MUTEX_LOCKER(mutexLocker, _storeLock); - for (auto const& i : VPackArrayIterator(query->slice())) { - applied.push_back(apply(i)); - } - return applied; - } - - query_t read (query_t const& query) const { - MUTEX_LOCKER(mutexLocker, _storeLock); - // TODO: Run through JSON and asseble result - query_t ret = std::make_shared(); - return ret; - } - protected: Node* _parent; + bool apply (arangodb::velocypack::Slice const&); + private: - bool apply (arangodb::velocypack::Slice const& slice) { - // TODO apply slice to database - return true; - } - + + bool check (arangodb::velocypack::Slice const&); + + typedef Buffer value_t; + NodeType _type; std::string _name; - Buffer _value; + value_t _value; Children _children; mutable arangodb::Mutex _storeLock; diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index cbd7553cc6..5221552200 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -72,6 +72,7 @@ add_executable(${BIN_ARANGOD} Agency/ApplicationAgency.cpp Agency/Constituent.cpp Agency/State.cpp + Agency/Store.cpp Agency/AgentCallback.cpp ApplicationServer/ApplicationFeature.cpp ApplicationServer/ApplicationServer.cpp diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index f157a01f93..6978a81cbb 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -75,7 +75,7 @@ inline HttpHandler::status_t RestAgencyHandler::redirect (id_t leader_id) { generateError(HttpResponse::NOT_FOUND,404); return HttpHandler::status_t(HANDLER_DONE); } - +#include inline HttpHandler::status_t RestAgencyHandler::handleWrite () { arangodb::velocypack::Options options; // TODO: User not wait. if (_request->requestType() == HttpRequest::HTTP_REQUEST_POST) { diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 9dcd8b8238..8d89c28f06 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -1882,14 +1882,14 @@ 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(); + _applicationAgency->agent()->load(); // just wait until we are signalled _applicationServer->wait(); From 3d066d2f0355992f65a726083968883c81b45d38 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 9 Mar 2016 12:29:01 +0100 Subject: [PATCH 40/61] Store intestinals into cpp file. Write to db ok. --- arangod/Agency/Agent.cpp | 6 +- arangod/Agency/Store.cpp | 71 ++++++++++++++++------- arangod/Agency/Store.h | 6 +- arangod/RestHandler/RestAgencyHandler.cpp | 4 +- 4 files changed, 60 insertions(+), 27 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 8d77504909..9bcc74f461 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -212,6 +212,8 @@ write_ret_t Agent::write (query_t const& query) { if (_constituent.leading()) { // Leading MUTEX_LOCKER(mutexLocker, _confirmedLock); std::vector applied = _spear_head.apply(query); // Apply to spearhead + std::cout << _spear_head("/") < indices = _state.log (query, applied, term(), id()); // Append to log w/ indicies @@ -230,8 +232,8 @@ write_ret_t Agent::write (query_t const& query) { read_ret_t Agent::read (query_t const& query) const { if (_constituent.leading()) { // We are leading - _read_db.read (query); - return read_ret_t(true,_constituent.leaderID());//(query); //TODO: + auto result = _read_db.read (query); + return read_ret_t(true,_constituent.leaderID(),result);//(query); //TODO: } else { // We redirect return read_ret_t(false,_constituent.leaderID()); } diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index 4aec2b5eff..04dd1a0f2b 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -27,22 +27,26 @@ #include #include +#include using namespace arangodb::consensus; +struct NotEmpty { + bool operator()(const std::string& s) { return !s.empty(); } +}; -static inline std::vector split ( - std::string str, const std::string& dlm) { - std::vector sv; - size_t start = (str.find('/') == 0) ? 1:0, end = 0; - while (end != std::string::npos) { - end = str.find (dlm, start); - sv.push_back(str.substr(start, (end == std::string::npos) ? - std::string::npos : end - start)); - start = ((end > (std::string::npos - dlm.size())) ? - std::string::npos : end + dlm.size()); - } - return sv; +std::vector split(const std::string& value, char separator) { + std::vector 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) {} @@ -63,7 +67,7 @@ Node& Node::operator= (Node const& node) { // Assign node return *this; } -inline NodeType Node::type() const {return _children.size() ? NODE : LEAF;} +NodeType Node::type() const {return _children.size() ? NODE : LEAF;} Node& Node::operator [](std::string name) { return *_children[name]; @@ -78,9 +82,9 @@ bool Node::append (std::string const name, std::shared_ptr const node) { _children[name]->_parent = this; return true; } -#include Node& Node::operator ()(std::vector& pv) { + std::cout << "const" << pv << pv.size() << std::endl; if (pv.size()) { auto found = _children.find(pv[0]); if (found == _children.end()) { @@ -96,6 +100,7 @@ Node& Node::operator ()(std::vector& pv) { } Node const& Node::operator ()(std::vector& pv) const { + std::cout << "non const" << std::endl; if (pv.size()) { auto found = _children.find(pv[0]); if (found == _children.end()) { @@ -109,22 +114,22 @@ Node const& Node::operator ()(std::vector& pv) const { } Node const& Node::operator ()(std::string const& path) const { - PathType pv = split(path,"/"); + PathType pv = split(path,'/'); return this->operator()(pv); } Node& Node::operator ()(std::string const& path) { - PathType pv = split(path,"/"); + PathType pv = split(path,'/'); return this->operator()(pv); } Node const& Node::read (std::string const& path) const { - PathType pv = split(path,"/"); + PathType pv = split(path,'/'); return this->operator()(pv); } Node& Node::write (std::string const& path) { - PathType pv = split(path,"/"); + PathType pv = split(path,'/'); return this->operator()(pv); } @@ -157,6 +162,7 @@ bool Node::apply (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); auto found = _children.find(key); if (found == _children.end()) { _children[key] = std::make_shared(key); @@ -171,15 +177,38 @@ bool Node::apply (arangodb::velocypack::Slice const& slice) { return true; } -bool Node::check (arangodb::velocypack::Slice const& slice) { +bool Node::check (arangodb::velocypack::Slice const& slice) const{ return true; } query_t Node::read (query_t const& query) const { MUTEX_LOCKER(storeLocker, _storeLock); + query_t result = std::make_shared(); + result->add(VPackValue(VPackValueType::Object)); + for (auto const& i : VPackArrayIterator(query->slice())) { + read (i, *result); + } // TODO: Run through JSON and asseble result - query_t ret = std::make_shared(); - return ret; + result->close(); + return result; +} + +bool Node::read (arangodb::velocypack::Slice const& slice, + Builder& ret) const { + LOG(WARN)<second->read(i.value, ret); + } + } else { + ret.add(slice); + } + return true; } diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index dc390e6aac..df546c1843 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -117,11 +117,13 @@ public: protected: Node* _parent; - bool apply (arangodb::velocypack::Slice const&); private: - bool check (arangodb::velocypack::Slice const&); + bool apply (arangodb::velocypack::Slice const&); + bool read (arangodb::velocypack::Slice const&, + arangodb::velocypack::Builder&) const; + bool check (arangodb::velocypack::Slice const&) const; typedef Buffer value_t; diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index 6978a81cbb..77ccb398e7 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -85,7 +85,7 @@ inline HttpHandler::status_t RestAgencyHandler::handleWrite () { 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(ret.applied[i]), Value(ret.indices[i])); + body.add(std::to_string(i), Value(ret.indices[i])); } body.close(); generateResult(body.slice()); @@ -100,7 +100,7 @@ inline HttpHandler::status_t RestAgencyHandler::handleWrite () { inline HttpHandler::status_t RestAgencyHandler::handleRead () { arangodb::velocypack::Options options; - if (_request->requestType() != HttpRequest::HTTP_REQUEST_POST) { + if (_request->requestType() == HttpRequest::HTTP_REQUEST_POST) { read_ret_t ret = _agent->read (_request->toVelocyPack(&options)); if (ret.accepted) { generateResult(ret.result->slice()); From 3b59217c214ea3a1b814601ad049b9a052bc7f03 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Thu, 10 Mar 2016 17:37:37 +0100 Subject: [PATCH 41/61] merging from devel --- arangod/Agency/AgencyCommon.h | 10 ++- arangod/Agency/Agent.cpp | 4 +- arangod/Agency/Store.cpp | 146 +++++++++++++++++++++++----------- arangod/Agency/Store.h | 58 +++++++------- 4 files changed, 137 insertions(+), 81 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index dc928b7c32..370de2e8d6 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -71,9 +72,16 @@ enum AGENT_FAILURE { template inline std::ostream& operator<< (std::ostream& l, std::vector const& v) { for (auto const& i : v) - l << i << " "; + l << i << "|"; return l; } +template +inline std::ostream& operator<< (std::ostream& os, std::list const& l) { + for (auto const& i : l) + os << i << "|"; + return os; +} + struct AgentConfiguration { id_t id; float min_ping; diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 9bcc74f461..b0b6e50c9a 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -212,7 +212,6 @@ write_ret_t Agent::write (query_t const& query) { if (_constituent.leading()) { // Leading MUTEX_LOCKER(mutexLocker, _confirmedLock); std::vector applied = _spear_head.apply(query); // Apply to spearhead - std::cout << _spear_head("/") < indices = _state.log (query, applied, term(), id()); // Append to log w/ indicies @@ -232,7 +231,8 @@ write_ret_t Agent::write (query_t const& query) { read_ret_t Agent::read (query_t const& query) const { if (_constituent.leading()) { // We are leading - auto result = _read_db.read (query); + 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()); diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index 04dd1a0f2b..b79a5b5e2f 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -34,6 +34,9 @@ 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 split(const std::string& value, char separator) { std::vector result; @@ -50,6 +53,7 @@ std::vector split(const std::string& value, char separator) { } Node::Node (std::string const& name) : _parent(nullptr), _name(name) {} +Node::Node (std::string const& name, Node const* parent) : _parent(parent), _name(name) {} Node::~Node() {} @@ -63,7 +67,12 @@ Node& Node::operator= (Slice const& slice) { // Assign value (become leaf) } Node& Node::operator= (Node const& node) { // Assign node - *this = node; + + _value.reset(); + _value.append((char const*)node._value.data(),node._value.byteSize()); + _name = node._name; + _type = node._type; + _children = node._children; return *this; } @@ -84,13 +93,11 @@ bool Node::append (std::string const name, std::shared_ptr const node) { } Node& Node::operator ()(std::vector& pv) { - std::cout << "const" << pv << pv.size() << std::endl; if (pv.size()) { auto found = _children.find(pv[0]); if (found == _children.end()) { - _children[pv[0]] = std::make_shared(pv[0]); + _children[pv[0]] = std::make_shared(pv[0], this); found = _children.find(pv[0]); - found->second->_parent = this; } pv.erase(pv.begin()); return (*found->second)(pv); @@ -100,7 +107,6 @@ Node& Node::operator ()(std::vector& pv) { } Node const& Node::operator ()(std::vector& pv) const { - std::cout << "non const" << std::endl; if (pv.size()) { auto found = _children.find(pv[0]); if (found == _children.end()) { @@ -133,17 +139,38 @@ Node& Node::write (std::string const& path) { return this->operator()(pv); } -std::vector Node::apply (query_t const& query) { +bool Node::apply (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); + auto found = _children.find(key); + if (found == _children.end()) { + _children[key] = std::make_shared(key, this); + found = _children.find(key); + } + found->second->apply(i.value); + } + } else { + *this = slice; + } + return true; +} + +Store::Store () : Node("root") {} +Store::~Store () {} + +std::vector Store::apply (query_t const& query) { std::vector applied; std::vector path; MUTEX_LOCKER(storeLocker, _storeLock); for (auto const& i : VPackArrayIterator(query->slice())) { switch (i.length()) { case 1: - applied.push_back(apply(i[0])); break; // no precond + applied.push_back(this->apply(i[0])); break; // no precond case 2: if (check(i[0])) { - applied.push_back(apply(i[1])); // precondition + applied.push_back(this->apply(i[1])); // precondition } else { LOG(WARN) << "Precondition failed!"; applied.push_back(false); @@ -155,60 +182,85 @@ std::vector Node::apply (query_t const& query) { break; } } + std::cout << *this << std::endl; return applied; } -bool Node::apply (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); - auto found = _children.find(key); - if (found == _children.end()) { - _children[key] = std::make_shared(key); - found = _children.find(key); - found->second->_parent = this; - } - found->second->apply(i.value); - } - } else { - *this = slice; - } +bool Store::apply (arangodb::velocypack::Slice const& slice) { + return Node::apply(slice); +} + +bool Store::check (arangodb::velocypack::Slice const& slice) const{ return true; } -bool Node::check (arangodb::velocypack::Slice const& slice) const{ - return true; -} - -query_t Node::read (query_t const& query) const { +query_t Store::read (query_t const& queries) const { // list of list of paths MUTEX_LOCKER(storeLocker, _storeLock); query_t result = std::make_shared(); - result->add(VPackValue(VPackValueType::Object)); - for (auto const& i : VPackArrayIterator(query->slice())) { - read (i, *result); - } - // TODO: Run through JSON and asseble result - result->close(); + if (queries->slice().type() == VPackValueType::Array) { + for (auto const& query : VPackArrayIterator(queries->slice())) { + result->add(VPackValue(VPackValueType::Array)); // top node array + read (query, *result); + result->close(); + } + // TODO: Run through JSON and asseble result + } else { + LOG(FATAL) << "Read queries to stores must be arrays"; + } + return result; } -bool Node::read (arangodb::velocypack::Slice const& slice, +bool Store::read (arangodb::velocypack::Slice const& query, Builder& ret) const { - LOG(WARN)<second->read(i.value, ret); - } + std::list 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 { - ret.add(slice); + return false; } + query_strs.sort(); // sort paths + + // remove "double" entries + 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()); + + Node node("root"); + for (auto i = query_strs.begin(); i != query_strs.end(); ++i) { + node(*i) = (*this)(*i); + } + + std::cout << node << std::endl; return true; } +/* +{ + Node* 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._value.toString() << std::endl; + } + return os; +} +*/ + diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index df546c1843..6da6b9ac73 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -46,16 +47,9 @@ namespace consensus { enum NodeType {NODE, LEAF}; -inline std::ostream& operator<< (std::ostream& os, std::vector const& sv) { - for (auto const& i : sv) - os << i << " "; - return os; -} - - using namespace arangodb::velocypack; -enum NODE_EXPECTION {PATH_NOT_FOUND}; +enum NODE_EXCEPTION {PATH_NOT_FOUND}; class Node { public: @@ -64,15 +58,17 @@ public: typedef std::map> Children; Node (std::string const& name); - - ~Node (); + + Node (std::string const& name, Node const* parent); + + virtual ~Node (); std::string const& name() const; - Node& operator= (arangodb::velocypack::Slice const& t); - Node& operator= (Node const& node); + Node& operator= (arangodb::velocypack::Slice const& t); + NodeType type() const; Node& operator [](std::string name); @@ -94,12 +90,8 @@ public: friend std::ostream& operator<<(std::ostream& os, const Node& n); - std::vector apply (query_t const& query); - - query_t read (query_t const& query) const; - friend std::ostream& operator<<(std::ostream& os, const Node& n) { - Node* par = n._parent; + Node const* par = n._parent; while (par != 0) { par = par->_parent; os << " "; @@ -115,23 +107,16 @@ public: return os; } + virtual bool apply (arangodb::velocypack::Slice const&); + protected: - Node* _parent; + Node const* _parent; + Children _children; private: - - bool apply (arangodb::velocypack::Slice const&); - bool read (arangodb::velocypack::Slice const&, - arangodb::velocypack::Builder&) const; - bool check (arangodb::velocypack::Slice const&) const; - - typedef Buffer value_t; - NodeType _type; std::string _name; - value_t _value; - Children _children; - mutable arangodb::Mutex _storeLock; + Buffer _value; }; @@ -139,9 +124,20 @@ private: class Store : public Node { // Root node public: - Store () : Node("root") {} - ~Store () {} + Store (); + virtual ~Store (); + std::vector apply (query_t const& query); + query_t read (query_t const& query) const; + virtual bool apply (arangodb::velocypack::Slice const&); + +private: + bool read (arangodb::velocypack::Slice const&, + arangodb::velocypack::Builder&) const; + bool check (arangodb::velocypack::Slice const&) const; + + mutable arangodb::Mutex _storeLock; + }; }} From 9f51fe5fe915b889b6291f429ed8b4426adfd82f Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Thu, 10 Mar 2016 18:19:54 +0100 Subject: [PATCH 42/61] merging from devel --- arangod/Agency/AgencyCommon.h | 2 +- arangod/Agency/ApplicationAgency.cpp | 2 +- arangod/Agency/Constituent.cpp | 2 +- arangod/Agency/Store.h | 2 -- arangod/RestHandler/RestAgencyHandler.cpp | 2 +- arangod/RestHandler/RestAgencyPrivHandler.cpp | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/arangod/Agency/AgencyCommon.h b/arangod/Agency/AgencyCommon.h index 370de2e8d6..a8831d666b 100644 --- a/arangod/Agency/AgencyCommon.h +++ b/arangod/Agency/AgencyCommon.h @@ -24,7 +24,7 @@ #ifndef __ARANGODB_CONSENSUS_AGENCY_COMMON__ #define __ARANGODB_CONSENSUS_AGENCY_COMMON__ -#include +#include #include #include diff --git a/arangod/Agency/ApplicationAgency.cpp b/arangod/Agency/ApplicationAgency.cpp index e1aa132d2c..0a7c383a59 100644 --- a/arangod/Agency/ApplicationAgency.cpp +++ b/arangod/Agency/ApplicationAgency.cpp @@ -25,7 +25,7 @@ #include "Basics/win-utils.h" #endif -#include "Basics/Logger.h" +#include "Logger/Logger.h" #include "Scheduler/PeriodicTask.h" #include "ApplicationAgency.h" diff --git a/arangod/Agency/Constituent.cpp b/arangod/Agency/Constituent.cpp index 36151a98cc..96b7505cdd 100644 --- a/arangod/Agency/Constituent.cpp +++ b/arangod/Agency/Constituent.cpp @@ -22,7 +22,7 @@ //////////////////////////////////////////////////////////////////////////////// #include "Cluster/ClusterComm.h" -#include "Basics/Logger.h" +#include "Logger/Logger.h" #include "Basics/ConditionLocker.h" #include "Constituent.h" diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index 6da6b9ac73..044372a896 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -88,8 +88,6 @@ public: Node& write (std::string const& path); - friend std::ostream& operator<<(std::ostream& os, const Node& n); - friend std::ostream& operator<<(std::ostream& os, const Node& n) { Node const* par = n._parent; while (par != 0) { diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index 77ccb398e7..523c730107 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -32,7 +32,7 @@ #include #include -#include "Basics/Logger.h" +#include "Logger/Logger.h" using namespace arangodb; diff --git a/arangod/RestHandler/RestAgencyPrivHandler.cpp b/arangod/RestHandler/RestAgencyPrivHandler.cpp index 2c2c8239a4..db0fa0dbc1 100644 --- a/arangod/RestHandler/RestAgencyPrivHandler.cpp +++ b/arangod/RestHandler/RestAgencyPrivHandler.cpp @@ -33,7 +33,7 @@ #include -#include "Basics/Logger.h" +#include "Logger/Logger.h" using namespace arangodb; From ac52b477085c4efa9df34d694e0ea1f564941619 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Fri, 11 Mar 2016 12:10:39 +0100 Subject: [PATCH 43/61] actually getting out what was put into kv-store --- arangod/Agency/Store.cpp | 53 ++++++++++++++++++++-------------------- arangod/Agency/Store.h | 4 ++- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index b79a5b5e2f..81dfbe92da 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -67,11 +67,9 @@ Node& Node::operator= (Slice const& slice) { // Assign value (become leaf) } Node& Node::operator= (Node const& node) { // Assign node - - _value.reset(); - _value.append((char const*)node._value.data(),node._value.byteSize()); _name = node._name; _type = node._type; + _value = node._value; _children = node._children; return *this; } @@ -157,6 +155,23 @@ bool Node::apply (arangodb::velocypack::Slice const& slice) { return true; } +void Node::toBuilder (Builder& builder) const { + try { + if (type()==NODE) { + VPackObjectBuilder guard(&builder); + for (auto const& child : _children) { + std::cout << _name << " : " <toBuilder(builder); + } + } else { + builder.add(Slice(_value.data())); + } + } catch (std::exception const& e) { + LOG(FATAL) << e.what(); + } +} + Store::Store () : Node("root") {} Store::~Store () {} @@ -211,8 +226,9 @@ query_t Store::read (query_t const& queries) const { // list of list of paths return result; } -bool Store::read (arangodb::velocypack::Slice const& query, - Builder& ret) const { +bool Store::read (arangodb::velocypack::Slice const& query, Builder& ret) const { + + // Collect all paths std::list query_strs; if (query.type() == VPackValueType::Array) { for (auto const& sub_query : VPackArrayIterator(query)) @@ -224,7 +240,7 @@ bool Store::read (arangodb::velocypack::Slice const& query, } query_strs.sort(); // sort paths - // remove "double" entries + // 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=""; @@ -235,32 +251,17 @@ bool Store::read (arangodb::velocypack::Slice const& query, auto cut = std::remove_if(query_strs.begin(), query_strs.end(), Empty()); query_strs.erase (cut,query_strs.end()); + // Create response tree Node node("root"); for (auto i = query_strs.begin(); i != query_strs.end(); ++i) { node(*i) = (*this)(*i); } - - std::cout << node << std::endl; + + // Assemble builder from node + node.toBuilder(ret); + return true; } -/* -{ - Node* 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._value.toString() << std::endl; - } - return os; -} -*/ diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index 044372a896..dc79996a3b 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -100,13 +100,15 @@ public: for (auto const& i : n._children) os << *(i.second); } else { - os << n._value.toString() << std::endl; + os << Slice(n._value.data()).toJson() << std::endl; } return os; } virtual bool apply (arangodb::velocypack::Slice const&); + void toBuilder (Builder&) const; + protected: Node const* _parent; Children _children; From b59e7adfeb0b311447b2becd00ff99712acb319d Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Fri, 11 Mar 2016 17:07:16 +0100 Subject: [PATCH 44/61] actually getting out what was put into kv-store --- arangod/Agency/Store.cpp | 50 +++++++++++++++++++++++++++++----------- arangod/Agency/Store.h | 13 +++++++++-- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index 81dfbe92da..0ddaabcd04 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -52,11 +52,20 @@ std::vector split(const std::string& value, char separator) { return result; } -Node::Node (std::string const& name) : _parent(nullptr), _name(name) {} -Node::Node (std::string const& name, Node const* parent) : _parent(parent), _name(name) {} +Node::Node (std::string const& name) : _parent(nullptr), _name(name) { + _value.clear(); +} +Node::Node (std::string const& name, Node const* parent) : + _parent(parent), _name(name) { + _value.clear(); +} Node::~Node() {} +Slice Node::slice() const { + return (_value.size()==0) ? Slice() : Slice(_value.data()); +} + std::string const& Node::name() const {return _name;} Node& Node::operator= (Slice const& slice) { // Assign value (become leaf) @@ -80,6 +89,16 @@ Node& Node::operator [](std::string name) { return *_children[name]; } +bool Node::isSingular () const { + if (type() == LEAF) { + return true; + } else if (_children.size()==1) { + return _children.begin()->second->isSingular(); + } else { + return false; + } +} + bool Node::append (std::string const name, std::shared_ptr const node) { if (node != nullptr) { _children[name] = node; @@ -160,15 +179,14 @@ void Node::toBuilder (Builder& builder) const { if (type()==NODE) { VPackObjectBuilder guard(&builder); for (auto const& child : _children) { - std::cout << _name << " : " <toBuilder(builder); } } else { - builder.add(Slice(_value.data())); + builder.add(slice()); } } catch (std::exception const& e) { - LOG(FATAL) << e.what(); + std::cout << e.what() << std::endl; } } @@ -205,6 +223,10 @@ bool Store::apply (arangodb::velocypack::Slice const& slice) { return Node::apply(slice); } +Node const& Store::read (std::string const& path) const { + return Node::read(path); +} + bool Store::check (arangodb::velocypack::Slice const& slice) const{ return true; } @@ -213,16 +235,14 @@ query_t Store::read (query_t const& queries) const { // list of list of paths MUTEX_LOCKER(storeLocker, _storeLock); query_t result = std::make_shared(); if (queries->slice().type() == VPackValueType::Array) { - for (auto const& query : VPackArrayIterator(queries->slice())) { result->add(VPackValue(VPackValueType::Array)); // top node array - read (query, *result); + for (auto const& query : VPackArrayIterator(queries->slice())) { + read (query, *result); + } result->close(); - } - // TODO: Run through JSON and asseble result } else { LOG(FATAL) << "Read queries to stores must be arrays"; } - return result; } @@ -254,11 +274,15 @@ bool Store::read (arangodb::velocypack::Slice const& query, Builder& ret) const // Create response tree Node node("root"); for (auto i = query_strs.begin(); i != query_strs.end(); ++i) { - node(*i) = (*this)(*i); + node(*i) = (*this).read(*i); } // Assemble builder from node - node.toBuilder(ret); + if (query_strs.size() == 1 && node(*query_strs.begin()).type() == LEAF) { + ret.add(node(*query_strs.begin()).slice()); + } else { + node.toBuilder(ret); + } return true; } diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index dc79996a3b..657d0e6f09 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -100,7 +100,7 @@ public: for (auto const& i : n._children) os << *(i.second); } else { - os << Slice(n._value.data()).toJson() << std::endl; + os << n.slice().toJson() << std::endl; } return os; } @@ -109,14 +109,22 @@ public: void toBuilder (Builder&) const; + Buffer const& value() const { + return _value; + } + + bool isSingular () const; + + Slice slice() const; + protected: Node const* _parent; Children _children; + Buffer _value; private: NodeType _type; std::string _name; - Buffer _value; }; @@ -130,6 +138,7 @@ public: std::vector apply (query_t const& query); query_t read (query_t const& query) const; virtual bool apply (arangodb::velocypack::Slice const&); + Node const& read (std::string const&) const; private: bool read (arangodb::velocypack::Slice const&, From 8bf2264e88e5e1266ecccfde5723bf2471fafccf Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Tue, 15 Mar 2016 16:28:27 +0100 Subject: [PATCH 45/61] null result for non-existent key in agency dbs --- .../velocypack/include/velocypack/Slice.h | 1 + 3rdParty/velocypack/src/Slice.cpp | 2 + arangod/Agency/Store.cpp | 85 ++++++++++++------- arangod/Agency/Store.h | 29 +++++-- 4 files changed, 81 insertions(+), 36 deletions(-) diff --git a/3rdParty/velocypack/include/velocypack/Slice.h b/3rdParty/velocypack/include/velocypack/Slice.h index 8426689555..4b6796f39d 100644 --- a/3rdParty/velocypack/include/velocypack/Slice.h +++ b/3rdParty/velocypack/include/velocypack/Slice.h @@ -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 diff --git a/3rdParty/velocypack/src/Slice.cpp b/3rdParty/velocypack/src/Slice.cpp index dbf423106c..515531394f 100644 --- a/3rdParty/velocypack/src/Slice.cpp +++ b/3rdParty/velocypack/src/Slice.cpp @@ -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) { diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index 0ddaabcd04..ff8e74a4b4 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -63,7 +63,8 @@ Node::Node (std::string const& name, Node const* parent) : Node::~Node() {} Slice Node::slice() const { - return (_value.size()==0) ? Slice() : Slice(_value.data()); + return (_value.size()==0) ? + Slice("\x018",&Options::Defaults):Slice(_value.data()); } std::string const& Node::name() const {return _name;} @@ -71,7 +72,7 @@ std::string const& Node::name() const {return _name;} Node& Node::operator= (Slice const& slice) { // Assign value (become leaf) _children.clear(); _value.reset(); - _value.append((char const*)slice.begin(), slice.byteSize()); + _value.append(reinterpret_cast(slice.begin()), slice.byteSize()); return *this; } @@ -80,25 +81,45 @@ Node& Node::operator= (Node const& node) { // Assign node _type = node._type; _value = node._value; _children = node._children; + _ttl = node._ttl; return *this; } +/*Node& Node::parent () { + return *_parent; + } + +Node const& Node::parent () const { + return *_parent; + }*/ + +bool Node::remove (std::string const& path) { + std::vector 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::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]; } -bool Node::isSingular () const { - if (type() == LEAF) { - return true; - } else if (_children.size()==1) { - return _children.begin()->second->isSingular(); - } else { - return false; - } -} - bool Node::append (std::string const name, std::shared_ptr const node) { if (node != nullptr) { _children[name] = node; @@ -111,13 +132,12 @@ bool Node::append (std::string const name, std::shared_ptr const node) { Node& Node::operator ()(std::vector& pv) { if (pv.size()) { - auto found = _children.find(pv[0]); - if (found == _children.end()) { - _children[pv[0]] = std::make_shared(pv[0], this); - found = _children.find(pv[0]); + std::string const key = pv[0]; + if (_children.find(key) == _children.end()) { + _children[key] = std::make_shared(pv[0], this); } pv.erase(pv.begin()); - return (*found->second)(pv); + return (*_children[key])(pv); } else { return *this; } @@ -125,12 +145,9 @@ Node& Node::operator ()(std::vector& pv) { Node const& Node::operator ()(std::vector& pv) const { if (pv.size()) { - auto found = _children.find(pv[0]); - if (found == _children.end()) { - throw PATH_NOT_FOUND; - } + std::string const key = pv[0]; pv.erase(pv.begin()); - return (*found->second)(pv); + return (*_children.at(key))(pv); } else { return *this; } @@ -164,13 +181,13 @@ bool Node::apply (arangodb::velocypack::Slice const& slice) { auto found = _children.find(key); if (found == _children.end()) { _children[key] = std::make_shared(key, this); - found = _children.find(key); } - found->second->apply(i.value); + _children[key]->apply(i.value); } } else { *this = slice; } +// remove("/g/b"); return true; } @@ -190,7 +207,7 @@ void Node::toBuilder (Builder& builder) const { } } -Store::Store () : Node("root") {} +Store::Store (std::string const& name) : Node(name) {} Store::~Store () {} std::vector Store::apply (query_t const& query) { @@ -227,7 +244,8 @@ Node const& Store::read (std::string const& path) const { return Node::read(path); } -bool Store::check (arangodb::velocypack::Slice const& slice) const{ +bool Store::check (arangodb::velocypack::Slice const& slice) const { + return true; } @@ -272,12 +290,19 @@ bool Store::read (arangodb::velocypack::Slice const& query, Builder& ret) const query_strs.erase (cut,query_strs.end()); // Create response tree - Node node("root"); + Node node("copy"); + const Node rhs(*this); for (auto i = query_strs.begin(); i != query_strs.end(); ++i) { - node(*i) = (*this).read(*i); + try { + rhs(*i); + } catch (StoreException const& e) { + std::cout << e.what(); + continue; + } + node(*i) = rhs(*i); } - - // Assemble builder from node + + // Assemble builder from response tree if (query_strs.size() == 1 && node(*query_strs.begin()).type() == LEAF) { ret.add(node(*query_strs.begin()).slice()); } else { diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index 657d0e6f09..aacbe154d4 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -49,6 +49,14 @@ 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}; class Node { @@ -80,14 +88,22 @@ public: Node const& operator ()(std::vector& pv) const; - Node const& operator ()(std::string const& path) const; - Node& operator ()(std::string const& path); + Node const& operator ()(std::string const& path) const; + Node const& read (std::string const& path) const; Node& write (std::string const& path); +/* Node& parent (); + + Node const& parent () const;*/ + + bool remove (std::string const& path); + + bool removeChild (std::string const& key); + friend std::ostream& operator<<(std::ostream& os, const Node& n) { Node const* par = n._parent; while (par != 0) { @@ -100,7 +116,7 @@ public: for (auto const& i : n._children) os << *(i.second); } else { - os << n.slice().toJson() << std::endl; + os << ((n.slice().type() == ValueType::None) ? "NONE" : n.slice().toJson()) << std::endl; } return os; } @@ -113,14 +129,15 @@ public: return _value; } - bool isSingular () const; - Slice slice() const; + + protected: Node const* _parent; Children _children; Buffer _value; + std::chrono::system_clock::time_point _ttl; private: NodeType _type; @@ -132,7 +149,7 @@ private: class Store : public Node { // Root node public: - Store (); + Store (std::string const& name = "root"); virtual ~Store (); std::vector apply (query_t const& query); From 6f8841fcc893b322a31654252ba491c4826f344e Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Tue, 15 Mar 2016 20:44:06 +0100 Subject: [PATCH 46/61] correct const/non const iterations in operator() for Store --- arangod/Agency/Store.cpp | 47 +++++++++++++++++++++++++++++----------- arangod/Agency/Store.h | 4 +++- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index ff8e74a4b4..a916a8a506 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -85,6 +85,10 @@ Node& Node::operator= (Node const& node) { // Assign node return *this; } +bool Node::operator== (arangodb::velocypack::Slice const& rhs) const { + return rhs.equals(slice()); +} + /*Node& Node::parent () { return *_parent; } @@ -99,6 +103,7 @@ bool Node::remove (std::string const& path) { pv.pop_back(); try { Node& parent = (*this)(pv); + return parent.removeChild(key); } catch (StoreException const& e) { return false; @@ -147,7 +152,8 @@ Node const& Node::operator ()(std::vector& pv) const { if (pv.size()) { std::string const key = pv[0]; pv.erase(pv.begin()); - return (*_children.at(key))(pv); + const Node& child = *_children.at(key); + return child(pv); } else { return *this; } @@ -187,7 +193,6 @@ bool Node::apply (arangodb::velocypack::Slice const& slice) { } else { *this = slice; } -// remove("/g/b"); return true; } @@ -219,8 +224,8 @@ std::vector Store::apply (query_t const& query) { case 1: applied.push_back(this->apply(i[0])); break; // no precond case 2: - if (check(i[0])) { - applied.push_back(this->apply(i[1])); // precondition + if (check(i[1])) { + applied.push_back(this->apply(i[0])); // precondition } else { LOG(WARN) << "Precondition failed!"; applied.push_back(false); @@ -232,7 +237,6 @@ std::vector Store::apply (query_t const& query) { break; } } - std::cout << *this << std::endl; return applied; } @@ -245,6 +249,30 @@ Node const& Store::read (std::string const& path) const { } 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); + if (precond.value.type() == VPackValueType::Object) { //"old", "oldEmpty", "isArray" + for (auto const& op : VPackObjectIterator(precond.value)) { + std::string const& oper = op.key.copyString(); + if (oper == "old") { + std::cout << path << std::endl; + std::cout << op.value.toJson() << std::endl; + std::cout << (*this)(path) << std::endl; + return (*this)(path) == op.value; + } else if ("oldEmpty") { + } + } + + } else { + + } + } + // return true; } @@ -291,15 +319,8 @@ bool Store::read (arangodb::velocypack::Slice const& query, Builder& ret) const // Create response tree Node node("copy"); - const Node rhs(*this); for (auto i = query_strs.begin(); i != query_strs.end(); ++i) { - try { - rhs(*i); - } catch (StoreException const& e) { - std::cout << e.what(); - continue; - } - node(*i) = rhs(*i); + node(*i) = (*this)(*i); } // Assemble builder from response tree diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index aacbe154d4..0935c033db 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -75,7 +75,9 @@ public: Node& operator= (Node const& node); - Node& operator= (arangodb::velocypack::Slice const& t); + Node& operator= (arangodb::velocypack::Slice const&); + + bool operator== (arangodb::velocypack::Slice const&) const; NodeType type() const; From 9671c386b431550c5b087ca4552b0489f8a6e011 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Tue, 15 Mar 2016 21:49:56 +0100 Subject: [PATCH 47/61] correct const/non const iterations in operator() for Store --- arangod/Agency/Store.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index a916a8a506..a9501bb066 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -152,6 +152,9 @@ Node const& Node::operator ()(std::vector& 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 { @@ -260,11 +263,9 @@ bool Store::check (arangodb::velocypack::Slice const& slice) const { for (auto const& op : VPackObjectIterator(precond.value)) { std::string const& oper = op.key.copyString(); if (oper == "old") { - std::cout << path << std::endl; - std::cout << op.value.toJson() << std::endl; - std::cout << (*this)(path) << std::endl; return (*this)(path) == op.value; } else if ("oldEmpty") { + } } @@ -281,11 +282,11 @@ query_t Store::read (query_t const& queries) const { // list of list of paths MUTEX_LOCKER(storeLocker, _storeLock); query_t result = std::make_shared(); 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(); + 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"; } @@ -318,16 +319,17 @@ bool Store::read (arangodb::velocypack::Slice const& query, Builder& ret) const query_strs.erase (cut,query_strs.end()); // Create response tree - Node node("copy"); + Node copy("copy"); for (auto i = query_strs.begin(); i != query_strs.end(); ++i) { - node(*i) = (*this)(*i); + try { + copy(*i) = (*this)(*i); + } catch (StoreException const&) {} } - // Assemble builder from response tree - if (query_strs.size() == 1 && node(*query_strs.begin()).type() == LEAF) { - ret.add(node(*query_strs.begin()).slice()); + if (query_strs.size() == 1 && copy(*query_strs.begin()).type() == LEAF) { + ret.add(copy(*query_strs.begin()).slice()); } else { - node.toBuilder(ret); + copy.toBuilder(ret); } return true; From a2de635eba958e93228f97d29ba3a20708d11ada Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 16 Mar 2016 00:24:15 +0100 Subject: [PATCH 48/61] correct const/non const iterations in operator() for Store --- arangod/Agency/Store.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index a9501bb066..a7464a548f 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -262,10 +262,17 @@ bool Store::check (arangodb::velocypack::Slice const& slice) const { if (precond.value.type() == VPackValueType::Object) { //"old", "oldEmpty", "isArray" for (auto const& op : VPackObjectIterator(precond.value)) { std::string const& oper = op.key.copyString(); + Node const& node = (*this)(path); if (oper == "old") { - return (*this)(path) == op.value; - } else if ("oldEmpty") { - + return (node == op.value); + } else if ("isArray") { + if (op.value.type()!=VPackValueType::Bool) { + return false; + } + bool isArray = + (node.type() == LEAF && + node.slice().type() == VPackValueType::Array); + return op.value.getBool() ? isArray : !isArray; } } From 5acdc36d0784e5933bf7cbf777b0bb44ea0cc070 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 16 Mar 2016 23:40:53 +0100 Subject: [PATCH 49/61] Construct builder with prefilled buffer. Agency store increment, decrement, push pop --- .../velocypack/include/velocypack/Builder.h | 2 +- arangod/Agency/Store.cpp | 123 +++++++++++++----- arangod/Agency/Store.h | 19 +-- 3 files changed, 94 insertions(+), 50 deletions(-) diff --git a/3rdParty/velocypack/include/velocypack/Builder.h b/3rdParty/velocypack/include/velocypack/Builder.h index 8fb4576078..b520f8677f 100644 --- a/3rdParty/velocypack/include/velocypack/Builder.h +++ b/3rdParty/velocypack/include/velocypack/Builder.h @@ -161,7 +161,7 @@ class Builder { explicit Builder(Buffer& buffer, Options const* options = &Options::Defaults) - : _pos(0), _keyWritten(false), options(options) { + : _pos(buffer.size()), _keyWritten(false), options(options) { _buffer.reset(&buffer, BufferNonDeleter()); _start = _buffer->data(); _size = _buffer->size(); diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index a7464a548f..4af3d700ff 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -55,7 +56,7 @@ std::vector split(const std::string& value, char separator) { Node::Node (std::string const& name) : _parent(nullptr), _name(name) { _value.clear(); } -Node::Node (std::string const& name, Node const* parent) : +Node::Node (std::string const& name, Node* parent) : _parent(parent), _name(name) { _value.clear(); } @@ -89,21 +90,12 @@ bool Node::operator== (arangodb::velocypack::Slice const& rhs) const { return rhs.equals(slice()); } -/*Node& Node::parent () { - return *_parent; - } - -Node const& Node::parent () const { - return *_parent; - }*/ - bool Node::remove (std::string const& path) { std::vector 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; @@ -182,17 +174,74 @@ Node& Node::write (std::string const& path) { return this->operator()(pv); } -bool Node::apply (arangodb::velocypack::Slice const& slice) { +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); + VPackObjectIterator sit (slice); + + auto b = sit.begin(); + auto i = *b; + std::string key = i.key.toString(); + key = key.substr(1,key.length()-2); + if (key == "op") { + std::string oper = i.value.toString(); + oper = oper.substr(1,oper.length()-2); + Slice const& self = this->slice(); + if (oper == "delete") { + return _parent->removeChild(_name); + } else if (oper == "increment") { // Increment + if (self.isInt() || self.isUInt()) { + Builder tmp; + tmp.add(Value(self.isInt() ? int64_t(self.getInt()+1) : uint64_t(self.getUInt()+1))); + *this = tmp.slice(); + return true; + } else { + return false; + } + } else if (oper == "increment") { // Decrement + if (self.isInt() || self.isUInt()) { + Builder tmp; + tmp.add(Value(self.isInt() ? int64_t(self.getInt()-1) : uint64_t(self.getUInt()-1))); + *this = tmp.slice(); + return true; + } else { + return false; + } + } else if (oper == "push") { // Push + if (self.isArray()) { + Builder tmp; + tmp.openArray(); + for (auto const& old : VPackArrayIterator(self)) + tmp.add(old); + tmp.close(); + auto nval = *++b; + std::cout << nval.value.toString() << std::endl; + *this = tmp.slice(); + } + } else if (oper == "pop") { // Pop + if (self.isArray()) { + Builder tmp; + tmp.openArray(); + 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(); + } + } + } 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(key, this); } - _children[key]->apply(i.value); + _children[key]->applies(i.value); } + } else { *this = slice; } @@ -216,25 +265,25 @@ void Node::toBuilder (Builder& builder) const { } Store::Store (std::string const& name) : Node(name) {} + Store::~Store () {} std::vector Store::apply (query_t const& query) { std::vector applied; - std::vector path; MUTEX_LOCKER(storeLocker, _storeLock); for (auto const& i : VPackArrayIterator(query->slice())) { switch (i.length()) { case 1: - applied.push_back(this->apply(i[0])); break; // no precond + applied.push_back(applies(i[0])); break; // no precond case 2: if (check(i[1])) { - applied.push_back(this->apply(i[0])); // precondition + applied.push_back(applies(i[0])); // precondition } else { LOG(WARN) << "Precondition failed!"; applied.push_back(false); } break; - default: // wrong + default: // wrong LOG(FATAL) << "We can only handle log entry with or without precondition!"; applied.push_back(false); break; @@ -243,14 +292,6 @@ std::vector Store::apply (query_t const& query) { return applied; } -bool Store::apply (arangodb::velocypack::Slice const& slice) { - return Node::apply(slice); -} - -Node const& Store::read (std::string const& path) const { - return Node::read(path); -} - bool Store::check (arangodb::velocypack::Slice const& slice) const { if (slice.type() != VPackValueType::Object) { LOG(WARN) << "Cannot check precondition: " << slice.toJson(); @@ -259,28 +300,40 @@ bool Store::check (arangodb::velocypack::Slice const& slice) const { for (auto const& precond : VPackObjectIterator(slice)) { std::string path = precond.key.toString(); path = path.substr(1,path.size()-2); - if (precond.value.type() == VPackValueType::Object) { //"old", "oldEmpty", "isArray" + + 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(); - Node const& node = (*this)(path); - if (oper == "old") { + if (oper == "old") { // old return (node == op.value); - } else if ("isArray") { - if (op.value.type()!=VPackValueType::Bool) { + } 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; } diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index 0935c033db..d6dfb25993 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -67,7 +67,7 @@ public: Node (std::string const& name); - Node (std::string const& name, Node const* parent); + Node (std::string const& name, Node* parent); virtual ~Node (); @@ -98,16 +98,12 @@ public: Node& write (std::string const& path); -/* Node& parent (); - - Node const& parent () const;*/ - bool remove (std::string const& path); bool removeChild (std::string const& key); friend std::ostream& operator<<(std::ostream& os, const Node& n) { - Node const* par = n._parent; + Node* par = n._parent; while (par != 0) { par = par->_parent; os << " "; @@ -123,7 +119,7 @@ public: return os; } - virtual bool apply (arangodb::velocypack::Slice const&); + bool applies (arangodb::velocypack::Slice const&); void toBuilder (Builder&) const; @@ -133,15 +129,12 @@ public: Slice slice() const; - - protected: - Node const* _parent; + Node* _parent; Children _children; Buffer _value; std::chrono::system_clock::time_point _ttl; - -private: + NodeType _type; std::string _name; @@ -156,8 +149,6 @@ public: std::vector apply (query_t const& query); query_t read (query_t const& query) const; - virtual bool apply (arangodb::velocypack::Slice const&); - Node const& read (std::string const&) const; private: bool read (arangodb::velocypack::Slice const&, From af56c183458d759d8bb74a5b5ae6b5b3b1109194 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 16 Mar 2016 23:53:09 +0100 Subject: [PATCH 50/61] Construct builder with prefilled buffer. Agency store increment, decrement, push pop --- arangod/Agency/Store.cpp | 120 +++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 62 deletions(-) diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index 4af3d700ff..55de290987 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -176,72 +176,68 @@ Node& Node::write (std::string const& path) { bool Node::applies (arangodb::velocypack::Slice const& slice) { if (slice.type() == ValueType::Object) { - VPackObjectIterator sit (slice); - - auto b = sit.begin(); - auto i = *b; - std::string key = i.key.toString(); - key = key.substr(1,key.length()-2); - if (key == "op") { - std::string oper = i.value.toString(); - oper = oper.substr(1,oper.length()-2); - Slice const& self = this->slice(); - if (oper == "delete") { - return _parent->removeChild(_name); - } else if (oper == "increment") { // Increment - if (self.isInt() || self.isUInt()) { - Builder tmp; - tmp.add(Value(self.isInt() ? int64_t(self.getInt()+1) : uint64_t(self.getUInt()+1))); - *this = tmp.slice(); - return true; - } else { - return false; - } - } else if (oper == "increment") { // Decrement - if (self.isInt() || self.isUInt()) { - Builder tmp; - tmp.add(Value(self.isInt() ? int64_t(self.getInt()-1) : uint64_t(self.getUInt()-1))); - *this = tmp.slice(); - return true; - } else { - return false; - } - } else if (oper == "push") { // Push - if (self.isArray()) { - Builder tmp; - tmp.openArray(); - for (auto const& old : VPackArrayIterator(self)) - tmp.add(old); - tmp.close(); - auto nval = *++b; - std::cout << nval.value.toString() << std::endl; - *this = tmp.slice(); - } - } else if (oper == "pop") { // Pop - if (self.isArray()) { - Builder tmp; - tmp.openArray(); - VPackArrayIterator it(self); - size_t j = it.size()-1; - for (auto old : it) { - tmp.add(old); - if (--j==0) - break; + for (auto const& i : VPackObjectIterator(slice)) { + std::string key = i.key.toString(); + key = key.substr(1,key.length()-2); + if (key == "op") { + std::string oper = i.value.toString(); + oper = oper.substr(1,oper.length()-2); + Slice const& self = this->slice(); + if (oper == "delete") { + return _parent->removeChild(_name); + } else if (oper == "increment") { // Increment + if (self.isInt() || self.isUInt()) { + Builder tmp; + tmp.add(Value(self.isInt() ? int64_t(self.getInt()+1) : uint64_t(self.getUInt()+1))); + *this = tmp.slice(); + return true; + } else { + return false; + } + } else if (oper == "increment") { // Decrement + if (self.isInt() || self.isUInt()) { + Builder tmp; + tmp.add(Value(self.isInt() ? int64_t(self.getInt()-1) : uint64_t(self.getUInt()-1))); + *this = tmp.slice(); + return true; + } else { + return false; + } + } else if (oper == "push") { // Push + if (self.isArray()) { + Builder tmp; + tmp.openArray(); + for (auto const& old : VPackArrayIterator(self)) + tmp.add(old); + tmp.close(); + *this = tmp.slice(); + } + } else if (oper == "pop") { // Pop + if (self.isArray()) { + Builder tmp; + tmp.openArray(); + VPackArrayIterator it(self); + size_t j = it.size()-1; + std::cout << j << std::endl; + for (auto old : it) { + tmp.add(old); + if (--j==0) + break; + } + tmp.close(); + *this = tmp.slice(); } - tmp.close(); - *this = tmp.slice(); } - } - } 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(key, this); - } + } 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(key, this); + } _children[key]->applies(i.value); + } } - } else { *this = slice; } From 92bcc0c37604f6de1ddb847f91363f794de92cc7 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Thu, 17 Mar 2016 12:55:18 +0100 Subject: [PATCH 51/61] All operators + new without set --- arangod/Agency/Store.cpp | 138 +++++++++++++++++++++++++++++---------- 1 file changed, 103 insertions(+), 35 deletions(-) diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index 55de290987..5979e40c7c 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -175,59 +175,127 @@ Node& Node::write (std::string const& path) { } 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 (key == "op") { - std::string oper = i.value.toString(); + + 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; + } + *this = slice.get("new"); + return true; } else if (oper == "increment") { // Increment - if (self.isInt() || self.isUInt()) { - Builder tmp; - tmp.add(Value(self.isInt() ? int64_t(self.getInt()+1) : uint64_t(self.getUInt()+1))); - *this = tmp.slice(); - return true; - } else { + if (!(self.isInt() || self.isUInt())) { + LOG(WARN) << "Element to increment must be integral type"; + LOG(WARN) << slice.toJson(); return false; } - } else if (oper == "increment") { // Decrement - if (self.isInt() || self.isUInt()) { - Builder tmp; - tmp.add(Value(self.isInt() ? int64_t(self.getInt()-1) : uint64_t(self.getUInt()-1))); - *this = tmp.slice(); - return true; - } else { + 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"; + LOG(WARN) << 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 (self.isArray()) { - Builder tmp; - tmp.openArray(); - for (auto const& old : VPackArrayIterator(self)) - tmp.add(old); - tmp.close(); - *this = tmp.slice(); + if (!self.isArray()) { + LOG(WARN) << "Push operation only on array types! We are " << + self.toString(); } + if (!slice.hasKey("new")) { + LOG(WARN) << "Operator push without new value"; + LOG(WARN) << slice.toJson(); + return false; + } + Builder tmp; + tmp.openArray(); + 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 - if (self.isArray()) { - Builder tmp; - tmp.openArray(); - VPackArrayIterator it(self); - size_t j = it.size()-1; - std::cout << j << std::endl; - for (auto old : it) { - tmp.add(old); - if (--j==0) - break; - } - tmp.close(); - *this = tmp.slice(); + if (!self.isArray()) { + LOG(WARN) << "Pop operation only on array types! We are " << + self.toString(); } + Builder tmp; + tmp.openArray(); + 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 (!self.isArray()) { + LOG(WARN) << "Prepend operation only on array types! We are " << + self.toString(); + } + if (!slice.hasKey("new")) { + LOG(WARN) << "Operator push without new value"; + LOG(WARN) << slice.toJson(); + return false; + } + Builder tmp; + tmp.openArray(); + tmp.add(slice.get("new")); + for (auto const& old : VPackArrayIterator(self)) + tmp.add(old); + tmp.close(); + *this = tmp.slice(); + return true; + } else if (oper == "shift") { // Shift + if (!self.isArray()) { + LOG(WARN) << "Shift operation only on array types! We are " << + self.toString(); + } + Builder tmp; + tmp.openArray(); + 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; } + } 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 { From 0051c9d3d41300b5be2ba2549fb2d79247bd27bb Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Thu, 17 Mar 2016 21:04:12 +0100 Subject: [PATCH 52/61] we have ttl --- arangod/Agency/Agent.cpp | 1 + arangod/Agency/Store.cpp | 74 ++++++++++++++++++++++++++++--------- arangod/Agency/Store.h | 79 +++++++++++++++++++++++++++++++--------- 3 files changed, 118 insertions(+), 36 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index b0b6e50c9a..98b84f30db 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -51,6 +51,7 @@ Agent::~Agent () { void Agent::start() { _constituent.start(); + _spear_head.start(); } term_t Agent::term () const { diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index 5979e40c7c..6f9f36ac1a 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -28,6 +28,8 @@ #include #include +#include + #include using namespace arangodb::consensus; @@ -102,6 +104,11 @@ bool Node::remove (std::string const& path) { } } +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()) @@ -117,16 +124,6 @@ Node& Node::operator [](std::string name) { return *_children[name]; } -bool Node::append (std::string const name, std::shared_ptr const node) { - if (node != nullptr) { - _children[name] = node; - } else { - _children[name] = std::make_shared(name); - } - _children[name]->_parent = this; - return true; -} - Node& Node::operator ()(std::vector& pv) { if (pv.size()) { std::string const key = pv[0]; @@ -164,14 +161,24 @@ Node& Node::operator ()(std::string const& path) { return this->operator()(pv); } -Node const& Node::read (std::string const& path) const { - 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 +std::ostream& operator<< (std::ostream& o, std::map const& d) { + for (auto const& i : d) + o << i.first << ":" << i.second << std::endl; + return o; } -Node& Node::write (std::string const& path) { - PathType pv = split(path,'/'); - return this->operator()(pv); +bool Node::addTimeToLive (long millis) { + _time_table[ + std::chrono::system_clock::now() + std::chrono::milliseconds(millis)] = + _parent->_children[_name]; + return true; } bool Node::applies (arangodb::velocypack::Slice const& slice) { @@ -188,12 +195,15 @@ bool Node::applies (arangodb::velocypack::Slice const& slice) { Slice const& self = this->slice(); if (oper == "delete") { return _parent->removeChild(_name); - } else if (oper == "set") { + } 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 @@ -328,7 +338,7 @@ void Node::toBuilder (Builder& builder) const { } } -Store::Store (std::string const& name) : Node(name) {} +Store::Store (std::string const& name) : Node(name), Thread(name) {} Store::~Store () {} @@ -353,6 +363,8 @@ std::vector Store::apply (query_t const& query) { break; } } + _cv.signal(); // Wake up run + return applied; } @@ -459,5 +471,31 @@ bool Store::read (arangodb::velocypack::Slice const& query, Builder& ret) const 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()) { + std::cout << "clearing time table: " << it->second->name() << std::endl; + 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(1000000); // better wait to next known time point + clearTimeTable(); + + } +} diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index d6dfb25993..3bee198744 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -39,6 +39,8 @@ #include #include +#include +#include #include #include @@ -59,51 +61,64 @@ private: enum NODE_EXCEPTION {PATH_NOT_FOUND}; +/// @brief Simple tree implementation class Node { + public: typedef std::vector PathType; typedef std::map> Children; + typedef std::chrono::system_clock::time_point TimePoint; + typedef std::map> 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; - bool append (std::string const name, - std::shared_ptr const node = nullptr); - + /// @brief Get node specified by path vector Node& operator ()(std::vector& pv); - Node const& operator ()(std::vector& pv) const; + /// @brief Get node specified by path string Node& operator ()(std::string const& path); - Node const& operator ()(std::string const& path) const; - Node const& read (std::string const& path) const; - - Node& write (std::string const& path); - + /// @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 Dump friend std::ostream& operator<<(std::ostream& os, const Node& n) { - Node* par = n._parent; + Node const* par = n._parent; while (par != 0) { par = par->_parent; os << " "; @@ -119,19 +134,23 @@ public: return os; } + /// @brief Apply single slice bool applies (arangodb::velocypack::Slice const&); + /// @brief Create Builder representing this store void toBuilder (Builder&) const; - Buffer const& value() const { - return _value; - } - + /// @brief Create slice from value Slice slice() const; protected: + + /// @brief Add time to live entry + bool addTimeToLive (long millis); + Node* _parent; Children _children; + TimeTable _time_table; Buffer _value; std::chrono::system_clock::time_point _ttl; @@ -141,20 +160,44 @@ protected: }; -class Store : public Node { // Root node +/// @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 apply (query_t const& query); - query_t read (query_t const& query) const; + /// @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; }; From 67d46b948561e01fe658da210d01654e02094a7e Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Thu, 17 Mar 2016 21:52:07 +0100 Subject: [PATCH 53/61] we have ttl --- arangod/Agency/Store.cpp | 17 ++++++++++++----- arangod/Agency/Store.h | 6 +++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index 6f9f36ac1a..0e7d2a847a 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -174,8 +174,18 @@ std::ostream& operator<< (std::ostream& o, std::map const& d) { return o; } +Node& Node::root() { + Node *par = _parent, *tmp; + while (par != 0) { + tmp = par; + par = par->_parent; + } + std::cout << par << std::endl; + return *tmp; +} + bool Node::addTimeToLive (long millis) { - _time_table[ + root()._time_table[ std::chrono::system_clock::now() + std::chrono::milliseconds(millis)] = _parent->_children[_name]; return true; @@ -478,7 +488,6 @@ void Store::beginShutdown() { void Store::clearTimeTable () { for (auto it = _time_table.cbegin(); it != _time_table.cend() ;) { if (it->first < std::chrono::system_clock::now()) { - std::cout << "clearing time table: " << it->second->name() << std::endl; it->second->remove(); _time_table.erase(it++); } else { @@ -491,10 +500,8 @@ void Store::run() { CONDITION_LOCKER(guard, _cv); while (!this->isStopping()) { // Check timetable and remove overage entries - - _cv.wait(1000000); // better wait to next known time point + _cv.wait(200000); // better wait to next known time point clearTimeTable(); - } } diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index 3bee198744..66126f07a6 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -116,6 +116,10 @@ public: /// @brief Remove this node bool remove(); + /// @brief Root node + Node& root(); + + /// @brief Dump friend std::ostream& operator<<(std::ostream& os, const Node& n) { Node const* par = n._parent; @@ -146,7 +150,7 @@ public: protected: /// @brief Add time to live entry - bool addTimeToLive (long millis); + virtual bool addTimeToLive (long millis); Node* _parent; Children _children; From eb71e96e0cd10692244bbc2291296e6297b69a9f Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Fri, 18 Mar 2016 11:03:13 +0100 Subject: [PATCH 54/61] minor minor --- arangod/Agency/Agent.cpp | 2 +- arangod/Agency/Store.cpp | 3 +-- arangod/Agency/Store.h | 6 ++++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index 98b84f30db..ffaa8a20a4 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -76,7 +76,7 @@ priv_rpc_ret_t Agent::requestVote(term_t t, id_t id, index_t lastLogIndex, ++j; } } - LOG(WARN) << _config; + //LOG(WARN) << _config; } return priv_rpc_ret_t( diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index 0e7d2a847a..425668cae0 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -498,9 +498,8 @@ void Store::clearTimeTable () { void Store::run() { CONDITION_LOCKER(guard, _cv); - while (!this->isStopping()) { // Check timetable and remove overage entries - _cv.wait(200000); // better wait to next known time point + _cv.wait(100000); // better wait to next known time point clearTimeTable(); } } diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index 66126f07a6..9cc4931d3d 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -118,9 +118,8 @@ public: /// @brief Root node Node& root(); - - /// @brief Dump + /// @brief Dump to ostream friend std::ostream& operator<<(std::ostream& os, const Node& n) { Node const* par = n._parent; while (par != 0) { @@ -138,6 +137,9 @@ public: return os; } + /// #brief Get path of this node + std::string path (); + /// @brief Apply single slice bool applies (arangodb::velocypack::Slice const&); From bb2475544a0d5d86052ce5ecb352138e623977b0 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Fri, 18 Mar 2016 11:28:48 +0100 Subject: [PATCH 55/61] merged in from devel --- arangod/Agency/State.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arangod/Agency/State.cpp b/arangod/Agency/State.cpp index f65ac564f6..7eca548ed0 100644 --- a/arangod/Agency/State.cpp +++ b/arangod/Agency/State.cpp @@ -71,8 +71,9 @@ bool State::save (arangodb::velocypack::Slice const& slice, index_t index, "1", 1, _end_point, HttpRequest::HTTP_REQUEST_POST, path, body.toJson(), headerFields, 0.0); - if (res->status != CL_COMM_SENT) - LOG(WARN) << res->errorMessage; + if (res->status != CL_COMM_SENT) { + //LOG(WARN) << res->errorMessage; + } return (res->status == CL_COMM_SENT); // TODO: More verbose result From 062e403dba4d2c79335f9eb9f36a395a13a3b071 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Fri, 18 Mar 2016 11:47:24 +0100 Subject: [PATCH 56/61] correct array pop/push/shift/prepend behaviours if non-array entry on store --- arangod/Agency/Store.cpp | 56 +++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index 425668cae0..04fbcad0ff 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -239,10 +239,6 @@ bool Node::applies (arangodb::velocypack::Slice const& slice) { *this = tmp.slice(); return true; } else if (oper == "push") { // Push - if (!self.isArray()) { - LOG(WARN) << "Push operation only on array types! We are " << - self.toString(); - } if (!slice.hasKey("new")) { LOG(WARN) << "Operator push without new value"; LOG(WARN) << slice.toJson(); @@ -250,34 +246,30 @@ bool Node::applies (arangodb::velocypack::Slice const& slice) { } Builder tmp; tmp.openArray(); - for (auto const& old : VPackArrayIterator(self)) - tmp.add(old); + 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 - if (!self.isArray()) { - LOG(WARN) << "Pop operation only on array types! We are " << - self.toString(); - } Builder tmp; tmp.openArray(); - VPackArrayIterator it(self); - size_t j = it.size()-1; - for (auto old : it) { - tmp.add(old); - if (--j==0) - break; + 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 (!self.isArray()) { - LOG(WARN) << "Prepend operation only on array types! We are " << - self.toString(); - } if (!slice.hasKey("new")) { LOG(WARN) << "Operator push without new value"; LOG(WARN) << slice.toJson(); @@ -286,27 +278,27 @@ bool Node::applies (arangodb::velocypack::Slice const& slice) { 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 - if (!self.isArray()) { - LOG(WARN) << "Shift operation only on array types! We are " << - self.toString(); - } Builder tmp; tmp.openArray(); - VPackArrayIterator it(self); - bool first = true; - for (auto old : it) { - if (first) { - first = false; - } else { - tmp.add(old); + 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; From 3d3fb23a62b77c361834b5dcf9c0504b6794c410 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Fri, 18 Mar 2016 18:59:06 +0100 Subject: [PATCH 57/61] writing test --- arangod/Agency/Store.cpp | 37 ++++++++++++++++------- arangod/Agency/Store.h | 3 ++ arangod/RestHandler/RestAgencyHandler.cpp | 10 ++++++ js/client/modules/@arangodb/testing.js | 8 ++--- js/client/tests/agency/agency-test.js | 9 +++--- 5 files changed, 48 insertions(+), 19 deletions(-) diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index 04fbcad0ff..0156bb760b 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -67,7 +67,7 @@ Node::~Node() {} Slice Node::slice() const { return (_value.size()==0) ? - Slice("\x018",&Options::Defaults):Slice(_value.data()); + Slice("\x00a",&Options::Defaults):Slice(_value.data()); } std::string const& Node::name() const {return _name;} @@ -184,6 +184,10 @@ Node& Node::root() { 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)] = @@ -200,6 +204,7 @@ bool Node::applies (arangodb::velocypack::Slice const& slice) { 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(); @@ -218,8 +223,8 @@ bool Node::applies (arangodb::velocypack::Slice const& slice) { return true; } else if (oper == "increment") { // Increment if (!(self.isInt() || self.isUInt())) { - LOG(WARN) << "Element to increment must be integral type"; - LOG(WARN) << slice.toJson(); + LOG(WARN) << "Element to increment must be integral type: We are " + << slice.toJson(); return false; } Builder tmp; @@ -229,8 +234,8 @@ bool Node::applies (arangodb::velocypack::Slice const& slice) { return true; } else if (oper == "decrement") { // Decrement if (!(self.isInt() || self.isUInt())) { - LOG(WARN) << "Element to decrement must be integral type"; - LOG(WARN) << slice.toJson(); + LOG(WARN) << "Element to decrement must be integral type. We are " + << slice.toJson(); return false; } Builder tmp; @@ -240,8 +245,7 @@ bool Node::applies (arangodb::velocypack::Slice const& slice) { return true; } else if (oper == "push") { // Push if (!slice.hasKey("new")) { - LOG(WARN) << "Operator push without new value"; - LOG(WARN) << slice.toJson(); + LOG(WARN) << "Operator push without new value: " << slice.toJson(); return false; } Builder tmp; @@ -271,8 +275,8 @@ bool Node::applies (arangodb::velocypack::Slice const& slice) { return true; } else if (oper == "prepend") { // Prepend if (!slice.hasKey("new")) { - LOG(WARN) << "Operator push without new value"; - LOG(WARN) << slice.toJson(); + LOG(WARN) << "Operator prepend without new value: " + << slice.toJson(); return false; } Builder tmp; @@ -304,6 +308,7 @@ bool Node::applies (arangodb::velocypack::Slice const& slice) { return true; } else { LOG(WARN) << "Unknown operation " << oper; + return false; } } else if (slice.hasKey("new")) { // new without set *this = slice.get("new"); @@ -325,6 +330,7 @@ bool Node::applies (arangodb::velocypack::Slice const& slice) { } void Node::toBuilder (Builder& builder) const { + try { if (type()==NODE) { VPackObjectBuilder guard(&builder); @@ -338,6 +344,7 @@ void Node::toBuilder (Builder& builder) const { } catch (std::exception const& e) { std::cout << e.what() << std::endl; } + } Store::Store (std::string const& name) : Node(name), Thread(name) {} @@ -463,14 +470,22 @@ bool Store::read (arangodb::velocypack::Slice const& query, Builder& ret) const copy(*i) = (*this)(*i); } catch (StoreException const&) {} } + // Assemble builder from response tree - if (query_strs.size() == 1 && copy(*query_strs.begin()).type() == LEAF) { + if (query.type() == VPackValueType::String && + copy(*query_strs.begin()).type() == LEAF) { ret.add(copy(*query_strs.begin()).slice()); } else { - copy.toBuilder(ret); + if (copy.type() == LEAF && copy.valueType() == VPackValueType::Null) { + ret.add(VPackValue(VPackValueType::Object)); + ret.close(); + } else { + copy.toBuilder(ret); + } } return true; + } void Store::beginShutdown() { diff --git a/arangod/Agency/Store.h b/arangod/Agency/Store.h index 9cc4931d3d..a73477c49d 100644 --- a/arangod/Agency/Store.h +++ b/arangod/Agency/Store.h @@ -149,6 +149,9 @@ public: /// @brief Create slice from value Slice slice() const; + /// @brief Get value type + ValueType valueType () const; + protected: /// @brief Add time to live entry diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index 523c730107..d38c0da0c6 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -80,14 +80,24 @@ inline HttpHandler::status_t RestAgencyHandler::handleWrite () { arangodb::velocypack::Options options; // TODO: User not wait. if (_request->requestType() == HttpRequest::HTTP_REQUEST_POST) { write_ret_t ret = _agent->write (_request->toVelocyPack(&options)); + 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++; + } } body.close(); + if (errors == ret.indices.size()) { // epic fail + _response->setResponseCode(HttpResponse::PRECONDITION_FAILED); + } else if (errors == 0) {// full success + } else { // + _response->setResponseCode(HttpResponse::PRECONDITION_FAILED); + } generateResult(body.slice()); } else { generateError(HttpResponse::TEMPORARY_REDIRECT,307); diff --git a/js/client/modules/@arangodb/testing.js b/js/client/modules/@arangodb/testing.js index 90d4062a87..6cd39fc987 100644 --- a/js/client/modules/@arangodb/testing.js +++ b/js/client/modules/@arangodb/testing.js @@ -1,4 +1,4 @@ -*jshint strict: false, sub: true */ +/*jshint strict: false, sub: true */ /*global print, arango */ 'use strict'; @@ -1580,8 +1580,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"); @@ -1599,7 +1599,7 @@ function startInstanceAgency(instanceInfo, protocol, options, l.push("--agency.endpoint"); l.push(endpoints[j]); } - //args["flatCommands"] = l; + args["flatCommands"] = l; } argss.push(args); } diff --git a/js/client/tests/agency/agency-test.js b/js/client/tests/agency/agency-test.js index 33b848a274..71dfeddbf1 100644 --- a/js/client/tests/agency/agency-test.js +++ b/js/client/tests/agency/agency-test.js @@ -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; } @@ -131,7 +132,7 @@ function agencyTestSuite () { assertEqual(readAndCheck([["a"]]), [{a:13}]); var res = writeAgency([[{"a":14},{"a":12}]]); assertEqual(res.statusCode, 412); - assertEqual(res.bodyParsed, {error:true, successes:[]}); + // assertEqual(res.bodyParsed, {error:true, successes:[]}); writeAndCheck([[{a:{op:"delete"}}]]); } From 3beb3450b1c3d861132846d0c81c71a7c43c0262 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 21 Mar 2016 08:00:54 +0100 Subject: [PATCH 58/61] catch malformed input. --- arangod/RestHandler/RestAgencyHandler.cpp | 20 ++++++++++++++++++-- js/client/tests/agency/agency-test.js | 16 +++++++++++----- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index d38c0da0c6..fcdc601585 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -79,7 +79,15 @@ inline HttpHandler::status_t RestAgencyHandler::redirect (id_t leader_id) { inline HttpHandler::status_t RestAgencyHandler::handleWrite () { arangodb::velocypack::Options options; // TODO: User not wait. if (_request->requestType() == HttpRequest::HTTP_REQUEST_POST) { - write_ret_t ret = _agent->write (_request->toVelocyPack(&options)); + 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; @@ -111,7 +119,15 @@ inline HttpHandler::status_t RestAgencyHandler::handleWrite () { inline HttpHandler::status_t RestAgencyHandler::handleRead () { arangodb::velocypack::Options options; if (_request->requestType() == HttpRequest::HTTP_REQUEST_POST) { - read_ret_t ret = _agent->read (_request->toVelocyPack(&options)); + 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 { diff --git a/js/client/tests/agency/agency-test.js b/js/client/tests/agency/agency-test.js index 71dfeddbf1..8d7dff4cdd 100644 --- a/js/client/tests/agency/agency-test.js +++ b/js/client/tests/agency/agency-test.js @@ -128,17 +128,23 @@ function agencyTestSuite () { testPrecondition : function () { writeAndCheck([[{"a":12}]]); assertEqual(readAndCheck([["a"]]), [{a:12}]); - writeAndCheck([[{"a":13},{"a":12}]]); - assertEqual(readAndCheck([["a"]]), [{a:13}]); + //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:[]}); - writeAndCheck([[{a:{op:"delete"}}]]); + //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}}]); } }; } + //////////////////////////////////////////////////////////////////////////////// /// @brief executes the test suite //////////////////////////////////////////////////////////////////////////////// From 41d804d765ae5213aae8b3d20cdd665fe1f5a61a Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 21 Mar 2016 08:14:58 +0100 Subject: [PATCH 59/61] need to find out how to define the headers properly --- arangod/Agency/Agent.cpp | 5 +---- arangod/RestHandler/RestAgencyHandler.cpp | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/arangod/Agency/Agent.cpp b/arangod/Agency/Agent.cpp index ffaa8a20a4..b0cc02e954 100644 --- a/arangod/Agency/Agent.cpp +++ b/arangod/Agency/Agent.cpp @@ -76,7 +76,7 @@ priv_rpc_ret_t Agent::requestVote(term_t t, id_t id, index_t lastLogIndex, ++j; } } - //LOG(WARN) << _config; + LOG(WARN) << _config; } return priv_rpc_ret_t( @@ -213,16 +213,13 @@ write_ret_t Agent::write (query_t const& query) { if (_constituent.leading()) { // Leading MUTEX_LOCKER(mutexLocker, _confirmedLock); std::vector applied = _spear_head.apply(query); // Apply to spearhead - std::vector 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 diff --git a/arangod/RestHandler/RestAgencyHandler.cpp b/arangod/RestHandler/RestAgencyHandler.cpp index fcdc601585..91ee617524 100644 --- a/arangod/RestHandler/RestAgencyHandler.cpp +++ b/arangod/RestHandler/RestAgencyHandler.cpp @@ -99,13 +99,13 @@ inline HttpHandler::status_t RestAgencyHandler::handleWrite () { errors++; } } - body.close(); - if (errors == ret.indices.size()) { // epic fail +/* 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); From eba9a26891e357f3f15c40d1baa2c138e30b2d60 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Mon, 21 Mar 2016 08:42:35 +0100 Subject: [PATCH 60/61] writing test --- js/client/tests/agency/agency-test.js | 61 ++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/js/client/tests/agency/agency-test.js b/js/client/tests/agency/agency-test.js index 8d7dff4cdd..2b6c1bb18b 100644 --- a/js/client/tests/agency/agency-test.js +++ b/js/client/tests/agency/agency-test.js @@ -128,19 +128,68 @@ function agencyTestSuite () { testPrecondition : function () { writeAndCheck([[{"a":12}]]); assertEqual(readAndCheck([["a"]]), [{a:12}]); - //writeAndCheck([[{"a":13},{"a":12}]]); - //assertEqual(readAndCheck([["a"]]), [{a:13}]); + writeAndCheck([[{"a":13},{"a":12}]]); + assertEqual(readAndCheck([["a"]]), [{a:13}]); var res = writeAgency([[{"a":14},{"a":12}]]); //assertEqual(res.statusCode, 412); - //writeAndCheck([[{a:{op:"delete"}}]]); + 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}}]); - } + 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:[]}}]); + } + }; } From 0d15a3ceaf4e6edc6c6a8ef3d81819374eb78661 Mon Sep 17 00:00:00 2001 From: Kaveh Vahedipour Date: Wed, 23 Mar 2016 08:58:31 +0000 Subject: [PATCH 61/61] No need for hexdump header anymore --- arangod/Agency/Store.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/arangod/Agency/Store.cpp b/arangod/Agency/Store.cpp index 0156bb760b..d6999888d1 100644 --- a/arangod/Agency/Store.cpp +++ b/arangod/Agency/Store.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include