diff --git a/include/wsrep/client_state.hpp b/include/wsrep/client_state.hpp index a3fa537..d7daa8a 100644 --- a/include/wsrep/client_state.hpp +++ b/include/wsrep/client_state.hpp @@ -38,7 +38,9 @@ namespace wsrep e_deadlock_error, e_interrupted_error, e_size_exceeded_error, - e_append_fragment_error + e_append_fragment_error, + e_not_supported_error, + e_timeout_error }; static inline const char* to_c_string(enum client_error error) @@ -46,11 +48,13 @@ namespace wsrep switch (error) { case e_success: return "success"; - case e_error_during_commit: return "error_during_commit"; + case e_error_during_commit: return "commit_error"; case e_deadlock_error: return "deadlock_error"; case e_interrupted_error: return "interrupted_error"; - case e_size_exceeded_error: return "size_exceeded"; + case e_size_exceeded_error: return "size_exceeded_error"; case e_append_fragment_error: return "append_fragment_error"; + case e_not_supported_error: return "not_supported_error"; + case e_timeout_error: return "timeout_error"; } return "unknown"; } @@ -516,6 +520,37 @@ namespace wsrep { return toi_meta_; } + + /** + * Do sync wait operation. If the method fails, current_error() + * can be inspected about the reason of error. + * + * @param Sync wait timeout in seconds. + * + * @return Zero on success, non-zero on error. + */ + int sync_wait(int timeout); + + /** + * Return the current sync wait GTID. + * + * Sync wait GTID is updated on each sync_wait() call and + * reset to wsrep::gtid::undefined() in after_command_after_result() + * method. The variable can thus be used to check if a sync wait + * has been performend for the current client command. + */ + const wsrep::gtid& sync_wait_gtid() const + { + return sync_wait_gtid_; + } + /** + * Return the last written GTID. + */ + const wsrep::gtid& last_written_gtid() const + { + return last_written_gtid_; + } + /** * Set debug logging level. * @@ -586,6 +621,8 @@ namespace wsrep , transaction_(*this) , toi_meta_() , allow_dirty_reads_() + , sync_wait_gtid_() + , last_written_gtid_() , debug_log_level_(0) , current_error_(wsrep::e_success) { } @@ -599,6 +636,7 @@ namespace wsrep friend class client_toi_mode; friend class transaction; + void update_last_written_gtid(const wsrep::gtid&); void debug_log_state(const char*) const; void state(wsrep::unique_lock& lock, enum state state); void mode(wsrep::unique_lock& lock, enum mode mode); @@ -617,6 +655,8 @@ namespace wsrep wsrep::transaction transaction_; wsrep::ws_meta toi_meta_; bool allow_dirty_reads_; + wsrep::gtid sync_wait_gtid_; + wsrep::gtid last_written_gtid_; int debug_log_level_; wsrep::client_error current_error_; }; diff --git a/include/wsrep/gtid.hpp b/include/wsrep/gtid.hpp index 2cd6ed3..545dbd0 100644 --- a/include/wsrep/gtid.hpp +++ b/include/wsrep/gtid.hpp @@ -40,6 +40,22 @@ namespace wsrep wsrep::seqno seqno_; }; + /** + * Print a GTID into character buffer. + * @param buf Pointer to the beginning of the buffer + * @param buf_len Buffer length + * + * @return Number of characters printed or negative value for error + */ + ssize_t gtid_print_to_c_str(const wsrep::gtid&, char* buf, size_t buf_len); + /** + * Return minimum number of bytes guaranteed to store GTID string + * representation, terminating '\0' not included (36 + 1 + 20) + */ + static inline size_t gtid_c_str_len() + { + return 57; + } std::ostream& operator<<(std::ostream&, const wsrep::gtid&); std::istream& operator>>(std::istream&, wsrep::gtid&); } diff --git a/include/wsrep/provider.hpp b/include/wsrep/provider.hpp index c8846da..f250cdc 100644 --- a/include/wsrep/provider.hpp +++ b/include/wsrep/provider.hpp @@ -298,7 +298,8 @@ namespace wsrep * * @return Provider status indicating the result of the call. */ - virtual enum status causal_read(int timeout) const = 0; + virtual std::pair + causal_read(int timeout) const = 0; virtual enum status wait_for_gtid(const wsrep::gtid&, int timeout) const = 0; /** * Return last committed GTID. diff --git a/include/wsrep/server_state.hpp b/include/wsrep/server_state.hpp index 87068a4..dd78bd5 100644 --- a/include/wsrep/server_state.hpp +++ b/include/wsrep/server_state.hpp @@ -336,8 +336,12 @@ namespace wsrep * Method wait_for_gtid() should be used whenever possible. * * @param timeout Timeout in seconds + * + * @return Pair of GTID and result status from provider. */ - enum wsrep::provider::status causal_read(int timeout) const; + + std::pair + causal_read(int timeout) const; /** * Desynchronize the server. diff --git a/src/client_state.cpp b/src/client_state.cpp index 4c45580..dd1c629 100644 --- a/src/client_state.cpp +++ b/src/client_state.cpp @@ -158,6 +158,7 @@ void wsrep::client_state::after_command_after_result() { current_error_ = wsrep::e_success; } + sync_wait_gtid_ = wsrep::gtid::undefined(); state(lock, s_idle); debug_log_state("after_command_after_result: leave"); } @@ -293,12 +294,46 @@ int wsrep::client_state::leave_toi() wsrep::unique_lock lock(mutex_); mode(lock, toi_mode_); toi_mode_ = m_local; + if (toi_meta_.gtid().is_undefined() == false) + { + update_last_written_gtid(toi_meta_.gtid()); + } toi_meta_ = wsrep::ws_meta(); return ret; } + +int wsrep::client_state::sync_wait(int timeout) +{ + std::pair result( + server_state_.causal_read(timeout)); + int ret(1); + switch (result.second) + { + case wsrep::provider::success: + sync_wait_gtid_ = result.first; + ret = 0; + break; + case wsrep::provider::error_not_implemented: + override_error(wsrep::e_not_supported_error); + break; + default: + override_error(wsrep::e_timeout_error); + break; + } + return ret; +} + // Private +void wsrep::client_state::update_last_written_gtid(const wsrep::gtid& gtid) +{ + assert(last_written_gtid_.is_undefined() || + (last_written_gtid_.id() == gtid.id() && + last_written_gtid_.seqno() < gtid.seqno())); + last_written_gtid_ = gtid; +} + void wsrep::client_state::debug_log_state(const char* context) const { if (debug_log_level() >= 1) diff --git a/src/gtid.cpp b/src/gtid.cpp index 656f5a3..cf79ca8 100644 --- a/src/gtid.cpp +++ b/src/gtid.cpp @@ -4,7 +4,9 @@ #include "wsrep/gtid.hpp" +#include #include +#include std::ostream& wsrep::operator<<(std::ostream& os, const wsrep::gtid& gtid) { @@ -21,3 +23,16 @@ std::istream& wsrep::operator>>(std::istream& is, wsrep::gtid& gtid) std::cout << "GTID: " << gtid << "\n"; return is; } + +ssize_t wsrep::gtid_print_to_c_str( + const wsrep::gtid& gtid, char* buf, size_t buf_len) +{ + std::ostringstream os; + os << gtid; + if (os.str().size() > buf_len) + { + return -ENOBUFS; + } + std::strncpy(buf, os.str().c_str(), os.str().size()); + return os.str().size(); +} diff --git a/src/server_state.cpp b/src/server_state.cpp index ea3eb7a..620c7bf 100644 --- a/src/server_state.cpp +++ b/src/server_state.cpp @@ -499,7 +499,7 @@ wsrep::server_state::wait_for_gtid(const wsrep::gtid& gtid, int timeout) return provider_->wait_for_gtid(gtid, timeout); } -enum wsrep::provider::status +std::pair wsrep::server_state::causal_read(int timeout) const { return provider_->causal_read(timeout); diff --git a/src/transaction.cpp b/src/transaction.cpp index 518221f..2123d0d 100644 --- a/src/transaction.cpp +++ b/src/transaction.cpp @@ -961,8 +961,13 @@ void wsrep::transaction::cleanup() debug_log_state("cleanup_enter"); id_ = wsrep::transaction_id::undefined(); ws_handle_ = wsrep::ws_handle(); - // Keep the state history for troubleshooting. Reset at start_transaction(). + // Keep the state history for troubleshooting. Reset + // at start_transaction(). // state_hist_.clear(); + if (ordered()) + { + client_state_.update_last_written_gtid(ws_meta_.gtid()); + } ws_meta_ = wsrep::ws_meta(); certified_ = false; pa_unsafe_ = false; diff --git a/src/wsrep_provider_v26.cpp b/src/wsrep_provider_v26.cpp index 53a2c89..87d084e 100644 --- a/src/wsrep_provider_v26.cpp +++ b/src/wsrep_provider_v26.cpp @@ -703,10 +703,17 @@ wsrep::wsrep_provider_v26::leave_toi(wsrep::client_id client_id) wsrep_, client_id.get(), 0)); } -enum wsrep::provider::status +std::pair wsrep::wsrep_provider_v26::causal_read(int timeout) const { - return map_return_value(wsrep_->sync_wait(wsrep_, 0, timeout, 0)); + wsrep_gtid_t wsrep_gtid; + wsrep_status_t ret(wsrep_->sync_wait(wsrep_, 0, timeout, &wsrep_gtid)); + wsrep::gtid gtid(ret == WSREP_OK ? + wsrep::gtid(wsrep::id(wsrep_gtid.uuid.data, + sizeof(wsrep_gtid.uuid.data)), + wsrep::seqno(wsrep_gtid.seqno)) : + wsrep::gtid::undefined()); + return std::make_pair(gtid, map_return_value(ret)); } enum wsrep::provider::status diff --git a/src/wsrep_provider_v26.hpp b/src/wsrep_provider_v26.hpp index b5b1094..5ec1f7a 100644 --- a/src/wsrep_provider_v26.hpp +++ b/src/wsrep_provider_v26.hpp @@ -56,7 +56,8 @@ namespace wsrep wsrep::ws_meta&, int); enum wsrep::provider::status leave_toi(wsrep::client_id); - enum wsrep::provider::status causal_read(int) const; + std::pair + causal_read(int) const; enum wsrep::provider::status wait_for_gtid(const wsrep::gtid&, int) const; wsrep::gtid last_committed_gtid() const; int sst_sent(const wsrep::gtid&,int); diff --git a/test/mock_provider.hpp b/test/mock_provider.hpp index 195e42d..5de04d1 100644 --- a/test/mock_provider.hpp +++ b/test/mock_provider.hpp @@ -208,9 +208,11 @@ namespace wsrep enum wsrep::provider::status leave_toi(wsrep::client_id) { return wsrep::provider::success; } - enum wsrep::provider::status causal_read(int) const WSREP_OVERRIDE + std::pair + causal_read(int) const WSREP_OVERRIDE { - return wsrep::provider::success; + return std::make_pair(wsrep::gtid::undefined(), + wsrep::provider::error_not_implemented); } enum wsrep::provider::status wait_for_gtid(const wsrep::gtid&, int) const WSREP_OVERRIDE