MXS-1502: Prune session commands
The session command history is now compacted to contain only the first and last execution of a session command. This should still allow most of the more eccentric use-cases of user variables while keeping the session command history smaller. Added some convenience functions into the SessionCommand class to make the pruning process easier.
This commit is contained in:
@ -23,6 +23,10 @@
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
class SessionCommand;
|
||||
typedef std::tr1::shared_ptr<SessionCommand> SSessionCommand;
|
||||
typedef std::list<SSessionCommand> 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<const SSessionCommand&, bool>
|
||||
{
|
||||
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<SessionCommand> SSessionCommand;
|
||||
typedef std::list<SSessionCommand> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -16,7 +16,8 @@
|
||||
#include <maxscale/modutil.h>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <maxscale/modutil.h>
|
||||
#include <maxscale/router.h>
|
||||
#include <maxscale/server.h>
|
||||
#include <maxscale/session_command.hh>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user