From 28026ea4fc32df3fb53b0bedada754520c27ce8f Mon Sep 17 00:00:00 2001 From: Teemu Ollakka Date: Fri, 27 Apr 2018 12:51:05 +0300 Subject: [PATCH] Started documenting client context. --- doc/Doxyfile | 2 + src/client_context.cpp | 65 ++++++++++++ src/client_context.hpp | 198 +++++++++++++++++++++++++++++++++--- src/compiler.hpp | 16 +++ src/dbms_simulator.cpp | 4 +- src/mock_server_context.hpp | 20 ++-- src/server_context.hpp | 128 ++++++++++++++++++----- 7 files changed, 384 insertions(+), 49 deletions(-) create mode 100644 src/compiler.hpp diff --git a/doc/Doxyfile b/doc/Doxyfile index 781ecc1..4d3808f 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -229,6 +229,8 @@ TAB_SIZE = 4 # newlines. ALIASES = +ALIASES = "startuml=\if DontIgnorePlantUMLCode" +ALIASES += "enduml=\endif" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" diff --git a/src/client_context.cpp b/src/client_context.cpp index 9dfc9c0..3c445ca 100644 --- a/src/client_context.cpp +++ b/src/client_context.cpp @@ -10,6 +10,71 @@ trrep::provider& trrep::client_context::provider() const return server_context_.provider(); } +int trrep::client_context::before_command() +{ + assert(state_ == s_idle); + if (server_context_.rollback_mode() == trrep::server_context::rm_sync) + { + + /*! + * \todo Wait until the possible synchronous rollback + * has been finished. + */ + trrep::unique_lock lock(mutex_); + while (transaction_.state() == trrep::transaction_context::s_aborting) + { + // cond_.wait(lock); + } + } + state_ = s_exec; + if (transaction_.state() == trrep::transaction_context::s_must_abort) + { + return 1; + } + return 0; +} + +int trrep::client_context::after_command() +{ + int ret(0); + trrep::unique_lock lock(mutex_); + if (transaction_.state() == trrep::transaction_context::s_must_abort) + { + lock.unlock(); + rollback(transaction_); + lock.lock(); + ret = 1; + } + state_ = s_idle; + return ret; +} + +int trrep::client_context::before_statement() +{ +#if 0 + /*! + * \todo It might be beneficial to implement timed wait for + * server synced state. + */ + if (allow_dirty_reads_ == false && + server_context_.state() != trrep::server_context::s_synced) + { + return 1; + } +#endif // 0 + return 0; +} + +int trrep::client_context::after_statement() +{ +#if 0 + /*! + * \todo Check for replay state, do rollback if requested. + */ +#endif // 0 + return 0; +} + // TODO: This should be pure virtual method, implemented by // DBMS integration or mock classes only. int trrep::client_context::replay( diff --git a/src/client_context.hpp b/src/client_context.hpp index 4bd3b0c..9a7124d 100644 --- a/src/client_context.hpp +++ b/src/client_context.hpp @@ -2,10 +2,44 @@ // Copyright (C) 2018 Codership Oy // +/*! \file client_context.hpp + * + * Client Context + * ============== + * + * This file provides abstraction for integrating DBMS client + * with replication system. + * + * Client Modes + * ============ + * + * Local + * ----- + * + * Replicating + * ----------- + * + * Applier + * -------- + * + * Client State + * ============ + * + * Client state is mainly relevant for the operation if the Server + * supports synchronous rollback mode only. In this case the transactions + * of the the idle clients (controls is in application which is using the + * DBMS system) which encounter Brute Force Abort (BFA) must be rolled + * back either by applier or a background process. If the client + * state is executing, the control is inside the DBMS system and + * the rollback process should be performed by the client which + * drives the transaction. + */ + #ifndef TRREP_CLIENT_CONTEXT_HPP #define TRREP_CLIENT_CONTEXT_HPP #include "server_context.hpp" +#include "transaction_context.hpp" #include "mutex.hpp" #include "lock.hpp" #include "data.hpp" @@ -36,51 +70,179 @@ namespace trrep wsrep_conn_id_t id_; }; + /*! \class Client Context + * + * Client Contex abstract interface. + */ class client_context { public: + /*! + * Client mode enumeration. + * \todo m_toi total order isolation mode + */ enum mode { - m_local, // Operates in local only mode, no replication - m_replicating, // Generates write sets for replication - m_applier // Applying write sets from provider + /*! Operates in local only mode, no replication. */ + m_local, + /*! Generates write sets for replication by the provider. */ + m_replicating, + /*! Applies write sets from the provider. */ + m_applier }; + /*! + * Client state enumeration. + * + */ enum state { + /*! + * Client is idle, the control is in the application which + * uses the DBMS system. + */ s_idle, + /*! + * The control of the client processing is inside the DBMS + * system. + */ s_exec, + /*! + * The client session is terminating. + */ s_quitting }; virtual ~client_context() { } - // Accessors + + /*! + * Get reference to the client mutex. + * + * \return Reference to the client mutex. + */ trrep::mutex& mutex() { return mutex_; } + + /*! + * Get server context associated the the client session. + * + * \return Reference to server context. + */ trrep::server_context& server_context() const { return server_context_; } + + /*! + * Get reference to the Provider which is associated + * with the client context. + * + * \return Reference to the provider. + * \throw trrep::runtime_error if no providers are associated + * with the client context. + */ trrep::provider& provider() const; + /*! + * Get Client identifier. + * + * \return Client Identifier + */ client_id id() const { return id_; } + + /*! + * Get Client mode. + * + * \todo Enforce mutex protection if called from other threads. + * + * \return Client mode. + */ enum mode mode() const { return mode_; } + + /*! + * Get Client state. + * + * \todo Enforce mutex protection if called from other threads. + * + * \return Client state + */ enum state state() const { return state_; } + /*! + * Virtual method to return true if the client operates + * in two phase commit mode. + * + * \return True if two phase commit is required, false otherwise. + */ virtual bool do_2pc() const = 0; - // - // - // - virtual void before_command() { } - virtual void after_command() { } + /*! + * Virtual method which should be called before the client + * starts processing the command received from the application. + * This method will wait until the possible synchronous + * rollback for associated transaction has finished. + * The method has a side effect of changing the client + * context state to executing. + * + * If overridden, the implementation should call base + * class method before any implementation specific operations. + * + * \return Zero in case of success, non-zero in case of the + * associated transaction was BF aborted. + */ + virtual int before_command(); - virtual int before_statement() { return 0; } + /*! + * Virtual method which should be called before returning + * the control back to application which uses the DBMS system. + * This method will check if the transaction associated to + * the connection has been aborted. This method has a side effect + * of changing the client state to idle. + * + * If overridden, the implementation should call base + * class metods after any implementation specifict operations. + */ + virtual int after_command(); - virtual int after_statement() { return 0; } + /*! + * Before statement execution operations. + * + * Check if server is synced and if dirty reads are allowed. + * + * If the method is overridden by the implementation, base class + * method should be called before any implementation specifc + * operations. + * + * \return Zero in case of success, non-zero if the statement + * is not allowed to be executed due to read or write + * isolation requirements. + */ + virtual int before_statement(); + /*! + * After statement execution operations. + * + * * Check for must_replay state + * * Do rollback if requested + * + * If overridden by the implementation, base class method + * should be called after any implementation specific operations. + */ + virtual int after_statement(); + + /*! + * Append SR fragment to the transaction. + */ virtual int append_fragment(trrep::transaction_context&, uint32_t, const trrep::data&) { return 0; } + + /*! + * Commit the transaction. + */ virtual int commit(trrep::transaction_context&) = 0; + + /*! + * Rollback the transaction. + */ virtual int rollback(trrep::transaction_context&) = 0; virtual void will_replay(trrep::transaction_context&) { } @@ -114,9 +276,9 @@ namespace trrep abort(); } protected: - // - // Client context constuctor - // + /*! + * Client context constuctor + */ client_context(trrep::mutex& mutex, trrep::server_context& server_context, const client_id& id, @@ -126,6 +288,8 @@ namespace trrep , id_(id) , mode_(mode) , state_(s_idle) + , transaction_(*this) + , allow_dirty_reads_() { } private: @@ -136,6 +300,12 @@ namespace trrep client_id id_; enum mode mode_; enum state state_; + trrep::transaction_context transaction_; + /*! + * \todo This boolean should be converted to better read isolation + * semantics. + */ + bool allow_dirty_reads_; }; diff --git a/src/compiler.hpp b/src/compiler.hpp new file mode 100644 index 0000000..1631705 --- /dev/null +++ b/src/compiler.hpp @@ -0,0 +1,16 @@ +// +// Copyright (C) 2018 Codership Oy +// + + +/*! \file compiler.hpp + * + * Compiler specific options. + */ + +#define TRREP_UNUSED __attribute__((unused)) +#if __cplusplus >= 201103L +#define TRREP_OVERRIDE override +#else +#define TRREP_OVERRIDE +#endif // __cplusplus >= 201103L diff --git a/src/dbms_simulator.cpp b/src/dbms_simulator.cpp index b2ea0ce..ea66505 100644 --- a/src/dbms_simulator.cpp +++ b/src/dbms_simulator.cpp @@ -109,7 +109,8 @@ public: const std::string& name, const std::string& id, const std::string& address) - : trrep::server_context(name, id, address, name + "_data", + : trrep::server_context(mutex_, + name, id, address, name + "_data", trrep::server_context::rm_async) , simulator_(simulator) , mutex_() @@ -179,6 +180,7 @@ public: cond_.notify_all(); } + bool sst_before_init() const override { return false; } std::string on_sst_request() { return id(); diff --git a/src/mock_server_context.hpp b/src/mock_server_context.hpp index d0a8173..e03ab43 100644 --- a/src/mock_server_context.hpp +++ b/src/mock_server_context.hpp @@ -9,6 +9,7 @@ #include "mock_client_context.hpp" #include "mock_provider.hpp" +#include "compiler.hpp" namespace trrep { @@ -18,7 +19,8 @@ namespace trrep mock_server_context(const std::string& name, const std::string& id, enum trrep::server_context::rollback_mode rollback_mode) - : trrep::server_context(name, id, "", "./", rollback_mode) + : trrep::server_context(mutex_, name, id, "", "./", rollback_mode) + , mutex_() , provider_() , last_client_id_(0) { } @@ -30,19 +32,21 @@ namespace trrep trrep::client_context::m_local); } - void on_connect() { } - void wait_until_connected() { } - void on_view(const trrep::view&) { } - void on_sync() { } - std::string on_sst_request() { return ""; } + void on_connect() TRREP_OVERRIDE { } + void wait_until_connected() TRREP_OVERRIDE { } + void on_view(const trrep::view&) TRREP_OVERRIDE { } + void on_sync() TRREP_OVERRIDE { } + bool sst_before_init() const TRREP_OVERRIDE { return false; } + std::string on_sst_request() TRREP_OVERRIDE { return ""; } void on_sst_donate_request(const std::string&, const wsrep_gtid_t&, - bool) { } - void sst_received(const wsrep_gtid_t&) { } + bool) TRREP_OVERRIDE { } + void sst_received(const wsrep_gtid_t&) TRREP_OVERRIDE { } // void on_apply(trrep::transaction_context&) { } // void on_commit(trrep::transaction_context&) { } private: + trrep::default_mutex mutex_; mutable trrep::mock_provider provider_; unsigned long long last_client_id_; }; diff --git a/src/server_context.hpp b/src/server_context.hpp index 7a11e78..4fa1a5c 100644 --- a/src/server_context.hpp +++ b/src/server_context.hpp @@ -51,13 +51,22 @@ * In asynchronous mode the BFA victim transaction is just marked * to be aborted or in case of fully optimistic concurrency control, * the conflict is detected at commit. + * + * SST + * === + * + * Depending on SST type (physical or logical), the server storage + * engine initialization must be done before or after SST happens. + * In case of physical SST method (typically rsync, filesystem snapshot) + * the SST happens before the storage engine is initialized, in case + * of logical backup typically after the storage engine initialization. */ #ifndef TRREP_SERVER_CONTEXT_HPP #define TRREP_SERVER_CONTEXT_HPP #include "exception.hpp" - +#include "mutex.hpp" #include "wsrep_api.h" #include @@ -78,9 +87,57 @@ namespace trrep class server_context { public: - - /*! Rollback Mode enumeration + /*! + * Server state enumeration. * + * \todo Fix UML generation + * + * Server state diagram if the sst_before_init() returns false. + * + * [*] --> disconnected + * disconnected --> initializing + * initializing --> initialized + * initialized --> connected + * connected --> joiner + * joiner --> joined + * joined --> synced + * synced --> donor + * donor --> joined + * + * Server state diagram if the sst_before_init() returns true. + * + * [*] --> disconnected + * disconnected --> connected + * connected --> joiner + * joiner --> initializing + * initializing --> initialized + * initialized --> joined + * joined --> synced + * synced --> donor + * donor --> joined + */ + enum state + { + /*! Server is in disconnected state. */ + s_disconnected, + /*! Server is initializing */ + s_initializing, + /*! Server has been initialized */ + s_initialized, + /*! Server is connected to the cluster */ + s_connected, + /*! Server is receiving SST */ + s_joiner, + /*! Server has received SST succesfully but has not synced + with rest of the cluster yet. */ + s_joined, + /*! Server is donating state snapshot transfer */ + s_donor, + /*! Server has synced with the cluster */ + s_synced + }; + /*! + * Rollback Mode enumeration */ enum rollback_mode { @@ -90,29 +147,6 @@ namespace trrep rm_sync }; - /*! Server Context constructor - * - * \param name Human Readable Server Name. - * \param id Server Identifier String, UUID or some unique - * identifier. - * \param address Server address in form of IPv4 address, IPv6 address - * or hostname. - * \param working_dir Working directory for replication specific - * data files. - * \param rollback_mode Rollback mode which server operates on. - */ - server_context(const std::string& name, - const std::string& id, - const std::string& address, - const std::string& working_dir, - enum rollback_mode rollback_mode) - : provider_() - , name_(name) - , id_(id) - , address_(address) - , working_dir_(working_dir) - , rollback_mode_(rollback_mode) - { } virtual ~server_context(); /*! @@ -136,6 +170,13 @@ namespace trrep */ const std::string& address() const { return address_; } + /*! + * Get the rollback mode which server is operating in. + * + * \return Rollback mode. + */ + enum rollback_mode rollback_mode() const { return rollback_mode_; } + /*! * Create client context which acts only locally, i.e. does * not participate in replication. However, local client @@ -207,6 +248,12 @@ namespace trrep */ virtual void on_sync() = 0; + /*! + * Virtual method to return true if the configured SST + * method requires SST to be performed before DBMS storage + * engine initialization, false otherwise. + */ + virtual bool sst_before_init() const = 0; /*! * Virtual method which will be called on *joiner* when the provider * requests the SST request information. This method should @@ -275,11 +322,40 @@ namespace trrep virtual bool statement_allowed_for_streaming( const trrep::client_context& client_context, const trrep::transaction_context& transaction_context) const; + protected: + /*! Server Context constructor + * + * \param mutex Mutex provided by the DBMS implementation. + * \param name Human Readable Server Name. + * \param id Server Identifier String, UUID or some unique + * identifier. + * \param address Server address in form of IPv4 address, IPv6 address + * or hostname. + * \param working_dir Working directory for replication specific + * data files. + * \param rollback_mode Rollback mode which server operates on. + */ + server_context(trrep::mutex& mutex, + const std::string& name, + const std::string& id, + const std::string& address, + const std::string& working_dir, + enum rollback_mode rollback_mode) + : mutex_(mutex) + , provider_() + , name_(name) + , id_(id) + , address_(address) + , working_dir_(working_dir) + , rollback_mode_(rollback_mode) + { } + private: server_context(const server_context&); server_context& operator=(const server_context&); + trrep::mutex& mutex_; trrep::provider* provider_; std::string name_; std::string id_;