diff --git a/include/maxscale/session_command.hh b/include/maxscale/session_command.hh index 5cd1e8350..956e4f0ca 100644 --- a/include/maxscale/session_command.hh +++ b/include/maxscale/session_command.hh @@ -23,6 +23,10 @@ namespace maxscale { +class SessionCommand; +typedef std::tr1::shared_ptr SSessionCommand; +typedef std::list SessionCommandList; + class SessionCommand { SessionCommand(const SessionCommand&); @@ -78,6 +82,29 @@ public: */ std::string to_string(); + /** + * @brief Equality comparison + * + * @return True if @c rhs is equal + */ + bool eq(const SessionCommand& rhs) const; + + class Equals: public std::unary_function + { + public: + Equals(const SSessionCommand& base): + m_base(base) + {} + + bool operator ()(const SSessionCommand& rhs) + { + return m_base->eq(*rhs); + } + + private: + const SSessionCommand& m_base; + }; + private: mxs::Buffer m_buffer; /**< The buffer containing the command */ uint8_t m_command; /**< The command being executed */ @@ -85,7 +112,14 @@ private: bool m_reply_sent; /**< Whether the session command reply has been sent */ }; -typedef std::tr1::shared_ptr SSessionCommand; -typedef std::list SessionCommandList; +inline bool operator ==(const SessionCommand& lhs, const SessionCommand& rhs) +{ + return lhs.eq(rhs); +} + +inline bool operator !=(const SessionCommand& lhs, const SessionCommand& rhs) +{ + return !lhs.eq(rhs); +} } diff --git a/server/core/backend.cc b/server/core/backend.cc index 365abb642..a5f79cead 100644 --- a/server/core/backend.cc +++ b/server/core/backend.cc @@ -123,7 +123,7 @@ bool Backend::execute_session_command() void Backend::append_session_command(GWBUF* buffer, uint64_t sequence) { - m_session_commands.push_back(SSessionCommand(new SessionCommand(buffer, sequence))); + append_session_command(SSessionCommand(new SessionCommand(buffer, sequence))); } void Backend::append_session_command(const SSessionCommand& sescmd) diff --git a/server/core/session_command.cc b/server/core/session_command.cc index 836a818bb..498a2a6a1 100644 --- a/server/core/session_command.cc +++ b/server/core/session_command.cc @@ -16,7 +16,8 @@ #include #include -using namespace maxscale; +namespace maxscale +{ void SessionCommand::mark_reply_received() { @@ -62,6 +63,11 @@ SessionCommand::~SessionCommand() { } +bool SessionCommand::eq(const SessionCommand& rhs) const +{ + return rhs.m_buffer.compare(m_buffer) == 0; +} + std::string SessionCommand::to_string() { std::string str; @@ -80,3 +86,5 @@ std::string SessionCommand::to_string() return str; } + +} diff --git a/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc b/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc index 0a2e374cb..1ab2cbcf3 100644 --- a/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc +++ b/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include "routeinfo.hh" #include "rwsplit_internal.hh" @@ -239,6 +240,54 @@ bool route_single_stmt(RWSplit *inst, RWSplitSession *rses, GWBUF *querybuf, con return succp; } +/** + * Purge session command history + * + * @param rses Router session + * @param sescmd Executed session command + */ +static void purge_history(RWSplitSession* rses, mxs::SSessionCommand& sescmd) +{ + /** + * We can try to purge duplicate text protocol session commands. This + * makes the history size smaller but at the cost of being able to handle + * the more complex user variable modifications. To keep the best of both + * worlds, keeping the first and last copy of each command should be + * an adequate compromise. This way executing the following SQL will still + * produce the correct result. + * + * USE test; + * SET @myvar = (SELECT COUNT(*) FROM t1); + * USE test; + * + * Another option would be to keep the first session command but that would + * require more work to be done in the session command response processing. + * This would be a better alternative but the gain might not be optimal. + */ + + // As the PS handles map to explicit IDs, we must retain all COM_STMT_PREPARE commands + if (sescmd->get_command() != MXS_COM_STMT_PREPARE) + { + auto first = std::find_if(rses->sescmd_list.begin(), rses->sescmd_list.end(), + mxs::SessionCommand::Equals(sescmd)); + + if (first != rses->sescmd_list.end()) + { + // We have at least one of these commands. See if we have a second one + auto second = std::find_if(std::next(first), rses->sescmd_list.end(), + mxs::SessionCommand::Equals(sescmd)); + + if (second != rses->sescmd_list.end()) + { + // We have a total of three commands, remove the middle one + auto old_cmd = *second; + rses->sescmd_responses.erase(old_cmd->get_position()); + rses->sescmd_list.erase(second); + } + } + } +} + /** * Execute in backends used by current router session. * Save session variable commands to router session property @@ -315,7 +364,7 @@ bool route_session_write(RWSplitSession *rses, GWBUF *querybuf, } if (rses->rses_config.max_sescmd_history > 0 && - rses->sescmd_count >= rses->rses_config.max_sescmd_history) + rses->sescmd_list.size() >= rses->rses_config.max_sescmd_history) { MXS_WARNING("Router session exceeded session command history limit. " "Slave recovery is disabled and only slave servers with " @@ -338,6 +387,7 @@ bool route_session_write(RWSplitSession *rses, GWBUF *querybuf, } else { + purge_history(rses, sescmd); rses->sescmd_list.push_back(sescmd); }