187 lines
5.4 KiB
C++
187 lines
5.4 KiB
C++
/* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; version 2 of the
|
|
License.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
02110-1301 USA */
|
|
|
|
#include "rpl_context.h"
|
|
|
|
#include "rpl_gtid.h" // Gtid_set
|
|
#include "sql_class.h" // THD
|
|
|
|
|
|
Session_consistency_gtids_ctx::Session_consistency_gtids_ctx() :
|
|
m_sid_map(NULL), m_gtid_set(NULL), m_listener(NULL),
|
|
m_curr_session_track_gtids(OFF)
|
|
{ }
|
|
|
|
Session_consistency_gtids_ctx::~Session_consistency_gtids_ctx()
|
|
{
|
|
if (m_gtid_set)
|
|
{
|
|
delete m_gtid_set;
|
|
m_gtid_set= NULL;
|
|
}
|
|
|
|
if (m_sid_map)
|
|
{
|
|
delete m_sid_map;
|
|
m_sid_map= NULL;
|
|
}
|
|
}
|
|
|
|
inline bool Session_consistency_gtids_ctx::shall_collect(const THD* thd)
|
|
{
|
|
return /* Do not track OWN_GTID if session does not own a
|
|
(non-anonymous) GTID. */
|
|
(thd->owned_gtid.sidno > 0 ||
|
|
m_curr_session_track_gtids == ALL_GTIDS) &&
|
|
/* if there is no listener/tracker, then there is no reason to collect */
|
|
m_listener != NULL &&
|
|
/* ROLLBACK statements may end up calling trans_commit_stmt */
|
|
thd->lex->sql_command != SQLCOM_ROLLBACK &&
|
|
thd->lex->sql_command != SQLCOM_ROLLBACK_TO_SAVEPOINT;
|
|
}
|
|
|
|
bool Session_consistency_gtids_ctx::notify_after_transaction_commit(const THD* thd)
|
|
{
|
|
DBUG_ENTER("Rpl_consistency_ctx::notify_after_transaction_commit");
|
|
DBUG_ASSERT(thd);
|
|
bool res= false;
|
|
|
|
if (!shall_collect(thd))
|
|
DBUG_RETURN(res);
|
|
|
|
if (m_curr_session_track_gtids == ALL_GTIDS)
|
|
{
|
|
/*
|
|
If one is configured to read all writes, we always collect
|
|
the GTID_EXECUTED.
|
|
|
|
NOTE: in the future optimize to collect deltas instead maybe.
|
|
*/
|
|
global_sid_lock->wrlock();
|
|
res= m_gtid_set->add_gtid_set(gtid_state->get_executed_gtids()) != RETURN_STATUS_OK;
|
|
global_sid_lock->unlock();
|
|
|
|
if (!res)
|
|
notify_ctx_change_listener();
|
|
}
|
|
|
|
DBUG_RETURN(res);
|
|
}
|
|
|
|
bool Session_consistency_gtids_ctx::notify_after_gtid_executed_update(const THD *thd)
|
|
{
|
|
DBUG_ENTER("Rpl_consistency_ctx::notify_after_gtid_executed_update");
|
|
DBUG_ASSERT(thd);
|
|
bool res= false;
|
|
|
|
if (!shall_collect(thd))
|
|
DBUG_RETURN(res);
|
|
|
|
if (m_curr_session_track_gtids == OWN_GTID)
|
|
{
|
|
DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_SID) != GTID_MODE_OFF);
|
|
DBUG_ASSERT(thd->owned_gtid.sidno > 0);
|
|
const Gtid& gtid= thd->owned_gtid;
|
|
if (gtid.sidno == -1) // we need to add thd->owned_gtid_set
|
|
{
|
|
/* Caller must only call this function if the set was not empty. */
|
|
#ifdef HAVE_GTID_NEXT_LIST
|
|
DBUG_ASSERT(!thd->owned_gtid_set.is_empty());
|
|
res= m_gtid_set->add_gtid_set(&thd->owned_gtid_set) != RETURN_STATUS_OK;
|
|
#else
|
|
DBUG_ASSERT(0);
|
|
#endif
|
|
}
|
|
else if (gtid.sidno > 0) // only one gtid
|
|
{
|
|
/*
|
|
Note that the interface is such that m_sid_map must contain
|
|
sidno before we add the gtid to m_gtid_set.
|
|
|
|
Thus, to avoid relying on global_sid_map and thus contributing
|
|
to increased contention, we arrange for sidnos on the local
|
|
sid map.
|
|
*/
|
|
rpl_sidno local_set_sidno= m_sid_map->add_sid(thd->owned_sid);
|
|
|
|
DBUG_ASSERT(!m_gtid_set->contains_gtid(local_set_sidno, gtid.gno));
|
|
res= m_gtid_set->ensure_sidno(local_set_sidno) != RETURN_STATUS_OK;
|
|
if (!res)
|
|
m_gtid_set->_add_gtid(local_set_sidno, gtid.gno);
|
|
}
|
|
|
|
if (!res)
|
|
notify_ctx_change_listener();
|
|
}
|
|
DBUG_RETURN(res);
|
|
}
|
|
|
|
bool Session_consistency_gtids_ctx::notify_after_response_packet(const THD *thd)
|
|
{
|
|
int res= false;
|
|
DBUG_ENTER("Rpl_consistency_ctx::notify_after_response_packet");
|
|
|
|
if (m_gtid_set && !m_gtid_set->is_empty())
|
|
m_gtid_set->clear();
|
|
|
|
/*
|
|
Every time we get a notification that a packet was sent, we update
|
|
this value. It may have changed (the previous command may have been
|
|
a SET SESSION session_track_gtids=...;).
|
|
*/
|
|
m_curr_session_track_gtids= thd->variables.session_track_gtids;
|
|
DBUG_RETURN(res);
|
|
}
|
|
|
|
void
|
|
Session_consistency_gtids_ctx::register_ctx_change_listener(
|
|
Session_consistency_gtids_ctx::Ctx_change_listener* listener,
|
|
THD* thd)
|
|
{
|
|
DBUG_ASSERT(m_listener == NULL || m_listener == listener);
|
|
if (m_listener == NULL)
|
|
{
|
|
DBUG_ASSERT(m_sid_map == NULL && m_gtid_set == NULL);
|
|
m_listener= listener;
|
|
m_sid_map= new Sid_map(NULL);
|
|
m_gtid_set= new Gtid_set(m_sid_map);
|
|
|
|
/*
|
|
Caches the value at startup if needed. This is called during THD::init,
|
|
if the session_track_gtids value is set at startup time to anything
|
|
different than OFF.
|
|
*/
|
|
m_curr_session_track_gtids= thd->variables.session_track_gtids;
|
|
}
|
|
}
|
|
|
|
void Session_consistency_gtids_ctx::unregister_ctx_change_listener(
|
|
Session_consistency_gtids_ctx::Ctx_change_listener* listener)
|
|
{
|
|
DBUG_ASSERT(m_listener == listener || m_listener == NULL);
|
|
|
|
if (m_gtid_set)
|
|
delete m_gtid_set;
|
|
|
|
if (m_sid_map)
|
|
delete m_sid_map;
|
|
|
|
m_listener= NULL;
|
|
m_gtid_set= NULL;
|
|
m_sid_map= NULL;
|
|
}
|