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
|
namespace maxscale
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class SessionCommand;
|
||||||
|
typedef std::tr1::shared_ptr<SessionCommand> SSessionCommand;
|
||||||
|
typedef std::list<SSessionCommand> SessionCommandList;
|
||||||
|
|
||||||
class SessionCommand
|
class SessionCommand
|
||||||
{
|
{
|
||||||
SessionCommand(const SessionCommand&);
|
SessionCommand(const SessionCommand&);
|
||||||
@ -78,6 +82,29 @@ public:
|
|||||||
*/
|
*/
|
||||||
std::string to_string();
|
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:
|
private:
|
||||||
mxs::Buffer m_buffer; /**< The buffer containing the command */
|
mxs::Buffer m_buffer; /**< The buffer containing the command */
|
||||||
uint8_t m_command; /**< The command being executed */
|
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 */
|
bool m_reply_sent; /**< Whether the session command reply has been sent */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::tr1::shared_ptr<SessionCommand> SSessionCommand;
|
inline bool operator ==(const SessionCommand& lhs, const SessionCommand& rhs)
|
||||||
typedef std::list<SSessionCommand> SessionCommandList;
|
{
|
||||||
|
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)
|
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)
|
void Backend::append_session_command(const SSessionCommand& sescmd)
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
#include <maxscale/modutil.h>
|
#include <maxscale/modutil.h>
|
||||||
#include <maxscale/protocol/mysql.h>
|
#include <maxscale/protocol/mysql.h>
|
||||||
|
|
||||||
using namespace maxscale;
|
namespace maxscale
|
||||||
|
{
|
||||||
|
|
||||||
void SessionCommand::mark_reply_received()
|
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 SessionCommand::to_string()
|
||||||
{
|
{
|
||||||
std::string str;
|
std::string str;
|
||||||
@ -80,3 +86,5 @@ std::string SessionCommand::to_string()
|
|||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <maxscale/modutil.h>
|
#include <maxscale/modutil.h>
|
||||||
#include <maxscale/router.h>
|
#include <maxscale/router.h>
|
||||||
#include <maxscale/server.h>
|
#include <maxscale/server.h>
|
||||||
|
#include <maxscale/session_command.hh>
|
||||||
|
|
||||||
#include "routeinfo.hh"
|
#include "routeinfo.hh"
|
||||||
#include "rwsplit_internal.hh"
|
#include "rwsplit_internal.hh"
|
||||||
@ -239,6 +240,54 @@ bool route_single_stmt(RWSplit *inst, RWSplitSession *rses, GWBUF *querybuf, con
|
|||||||
return succp;
|
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.
|
* Execute in backends used by current router session.
|
||||||
* Save session variable commands to router session property
|
* 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 &&
|
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. "
|
MXS_WARNING("Router session exceeded session command history limit. "
|
||||||
"Slave recovery is disabled and only slave servers with "
|
"Slave recovery is disabled and only slave servers with "
|
||||||
@ -338,6 +387,7 @@ bool route_session_write(RWSplitSession *rses, GWBUF *querybuf,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
purge_history(rses, sescmd);
|
||||||
rses->sescmd_list.push_back(sescmd);
|
rses->sescmd_list.push_back(sescmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user