From 9d5b8c8de27cbf3f69c694d3082ed7325f27a8a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Sun, 1 Jul 2018 15:54:29 +0300 Subject: [PATCH] Share data between identical session commands If two or more session commands contain identical buffers, the buffer of the first session command is shared between the others. This reduces the amount of memory used to store repeated executions of session commands. The purging of session command history in readwritesplit was replaced with session command de-duplication. This was done to prevent problems that could arise when the order of session commands plays a significant role. --- include/maxscale/session_command.hh | 11 ++++ server/core/session_command.cc | 7 +++ .../readwritesplit/rwsplit_route_stmt.cc | 55 +++++-------------- .../routing/readwritesplit/rwsplitsession.hh | 2 +- 4 files changed, 34 insertions(+), 41 deletions(-) diff --git a/include/maxscale/session_command.hh b/include/maxscale/session_command.hh index d1782113a..75a846ded 100644 --- a/include/maxscale/session_command.hh +++ b/include/maxscale/session_command.hh @@ -89,6 +89,17 @@ public: */ bool eq(const SessionCommand& rhs) const; + /** + * Mark the session command as a re-execution of another command + * + * This function makes the current command's buffer a reference to the other + * command's buffer. The commands will still have separate positions and + * reply statuses. + * + * @param rhs Session command whose data is used + */ + void mark_as_duplicate(const SessionCommand& rhs); + private: mxs::Buffer m_buffer; /**< The buffer containing the command */ uint8_t m_command; /**< The command being executed */ diff --git a/server/core/session_command.cc b/server/core/session_command.cc index 241a75bc4..24d3b907a 100644 --- a/server/core/session_command.cc +++ b/server/core/session_command.cc @@ -92,4 +92,11 @@ std::string SessionCommand::to_string() return str; } +void SessionCommand::mark_as_duplicate(const SessionCommand& rhs) +{ + ss_dassert(eq(rhs)); + // The commands now share the mxs::Buffer that contains the actual command + m_buffer = rhs.m_buffer; +} + } diff --git a/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc b/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc index 347046ab0..d91768ab7 100644 --- a/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc +++ b/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc @@ -368,52 +368,27 @@ bool RWSplitSession::route_single_stmt(GWBUF *querybuf) } /** - * Purge session command history + * Compress session command history + * + * This function removes data duplication by sharing buffers between session + * commands that have identical data. Only one copy of the actual data is stored + * for each unique session command. * * @param sescmd Executed session command */ -void RWSplitSession::purge_history(mxs::SSessionCommand& sescmd) +void RWSplitSession::compress_history(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 eq = [&](mxs::SSessionCommand& scmd) { - auto eq = [&](mxs::SSessionCommand& scmd) - { - return scmd->eq(*sescmd); - }; + return scmd->eq(*sescmd); + }; - auto first = std::find_if(m_sescmd_list.begin(), m_sescmd_list.end(), eq); + auto first = std::find_if(m_sescmd_list.begin(), m_sescmd_list.end(), eq); - if (first != m_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), m_sescmd_list.end(), eq); - - if (second != m_sescmd_list.end()) - { - // We have a total of three commands, remove the middle one - auto old_cmd = *second; - m_sescmd_responses.erase(old_cmd->get_position()); - m_sescmd_list.erase(second); - } - } + if (first != m_sescmd_list.end()) + { + // Duplicate command, use a reference of the old command instead of duplicating it + sescmd->mark_as_duplicate(**first); } } @@ -541,7 +516,7 @@ bool RWSplitSession::route_session_write(GWBUF *querybuf, uint8_t command, uint3 } else { - purge_history(sescmd); + compress_history(sescmd); m_sescmd_list.push_back(sescmd); } diff --git a/server/modules/routing/readwritesplit/rwsplitsession.hh b/server/modules/routing/readwritesplit/rwsplitsession.hh index ac2e1bdb3..2826e8d79 100644 --- a/server/modules/routing/readwritesplit/rwsplitsession.hh +++ b/server/modules/routing/readwritesplit/rwsplitsession.hh @@ -164,7 +164,7 @@ private: const mxs::SRWBackendList& backends, const mxs::SRWBackend& master); void process_sescmd_response(mxs::SRWBackend& backend, GWBUF** ppPacket); - void purge_history(mxs::SSessionCommand& sescmd); + void compress_history(mxs::SSessionCommand& sescmd); bool route_session_write(GWBUF *querybuf, uint8_t command, uint32_t type); void continue_large_session_write(GWBUF *querybuf, uint32_t type);