MDEV-15104 - Optimise MVCC snapshot
With trx_sys_t::rw_trx_ids removal, MVCC snapshot overhead became slightly higher. That is instead of copying an array we now have to iterate LF_HASH. All this done under trx_sys.mutex protection. This patch moves MVCC snapshot out of trx_sys.mutex. Clean-ups: Removed MVCC: doesn't make too much sense to keep it in a separate class anymore. Refactored ReadView so that it now calls register()/deregister() routines (it was vice versa before). ReadView doesn't have friends anymore. :( Even less trx_sys.mutex references.
This commit is contained in:
parent
c0d5d7c0ef
commit
bc7a1dc1fb
@ -3499,7 +3499,7 @@ ha_innobase::init_table_handle_for_HANDLER(void)
|
||||
|
||||
/* Assign a read view if the transaction does not have it yet */
|
||||
|
||||
trx_sys.mvcc.view_open(m_prebuilt->trx);
|
||||
m_prebuilt->trx->read_view.open(m_prebuilt->trx);
|
||||
|
||||
innobase_register_trx(ht, m_user_thd, m_prebuilt->trx);
|
||||
|
||||
@ -4386,7 +4386,7 @@ innobase_start_trx_and_assign_read_view(
|
||||
thd_get_trx_isolation(thd));
|
||||
|
||||
if (trx->isolation_level == TRX_ISO_REPEATABLE_READ) {
|
||||
trx_sys.mvcc.view_open(trx);
|
||||
trx->read_view.open(trx);
|
||||
} else {
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
HA_ERR_UNSUPPORTED,
|
||||
@ -16063,7 +16063,7 @@ ha_innobase::external_lock(
|
||||
}
|
||||
|
||||
} else if (trx->isolation_level <= TRX_ISO_READ_COMMITTED) {
|
||||
trx_sys.mvcc.view_close(trx->read_view);
|
||||
trx->read_view.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -16728,7 +16728,7 @@ ha_innobase::store_lock(
|
||||
|
||||
/* At low transaction isolation levels we let
|
||||
each consistent read set its own snapshot */
|
||||
trx_sys.mvcc.view_close(trx->read_view);
|
||||
trx->read_view.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5562,7 +5562,7 @@ error_handling_drop_uncached:
|
||||
if (ctx->online && ctx->num_to_add_index) {
|
||||
/* Assign a consistent read view for
|
||||
row_merge_read_clustered_index(). */
|
||||
trx_sys.mvcc.view_open(ctx->prebuilt->trx);
|
||||
ctx->prebuilt->trx->read_view.open(ctx->prebuilt->trx);
|
||||
}
|
||||
|
||||
if (fts_index) {
|
||||
|
@ -35,7 +35,6 @@ Created 5/7/1996 Heikki Tuuri
|
||||
#include "row0vers.h"
|
||||
#include "que0que.h"
|
||||
#include "btr0cur.h"
|
||||
#include "read0read.h"
|
||||
#include "log0recv.h"
|
||||
|
||||
/*********************************************************************//**
|
||||
|
@ -1,75 +0,0 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1997, 2013, 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 Street, Suite 500, Boston, MA 02110-1335 USA
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
/**************************************************//**
|
||||
@file include/read0read.h
|
||||
Cursor read
|
||||
|
||||
Created 2/16/1997 Heikki Tuuri
|
||||
*******************************************************/
|
||||
|
||||
#ifndef read0read_h
|
||||
#define read0read_h
|
||||
|
||||
#include "univ.i"
|
||||
|
||||
#include "read0types.h"
|
||||
|
||||
/** The MVCC read view manager */
|
||||
class MVCC
|
||||
{
|
||||
/** Active views. */
|
||||
UT_LIST_BASE_NODE_T(ReadView) m_views;
|
||||
|
||||
|
||||
/** Validates a read view list. */
|
||||
bool validate() const;
|
||||
public:
|
||||
MVCC() { UT_LIST_INIT(m_views, &ReadView::m_view_list); }
|
||||
~MVCC() { ut_ad(UT_LIST_GET_LEN(m_views) == 0); }
|
||||
|
||||
|
||||
/**
|
||||
Allocate and create a view.
|
||||
@param trx transaction creating the view
|
||||
*/
|
||||
void view_open(trx_t *trx);
|
||||
|
||||
|
||||
/**
|
||||
Close a view created by the above function.
|
||||
@param view view allocated by view_open.
|
||||
*/
|
||||
void view_close(ReadView &view);
|
||||
|
||||
|
||||
/**
|
||||
Clones the oldest view and stores it in view. No need to
|
||||
call view_close(). The caller owns the view that is passed in.
|
||||
This function is called by Purge to create it view.
|
||||
|
||||
@param view Preallocated view, owned by the caller
|
||||
*/
|
||||
void clone_oldest_view(ReadView *view);
|
||||
|
||||
|
||||
/** @return the number of active views */
|
||||
size_t size() const;
|
||||
};
|
||||
|
||||
#endif /* read0read_h */
|
@ -32,16 +32,154 @@ Created 2/16/1997 Heikki Tuuri
|
||||
|
||||
#include "trx0types.h"
|
||||
|
||||
// Friend declaration
|
||||
class MVCC;
|
||||
|
||||
/** Read view lists the trx ids of those transactions for which a consistent
|
||||
read should not see the modifications to the database. */
|
||||
/** View is not in MVCC and not visible to purge thread. */
|
||||
#define READ_VIEW_STATE_CLOSED 0
|
||||
|
||||
/** View is in MVCC, but not visible to purge thread. */
|
||||
#define READ_VIEW_STATE_REGISTERED 1
|
||||
|
||||
/** View is in MVCC, purge thread must wait for READ_VIEW_STATE_OPEN. */
|
||||
#define READ_VIEW_STATE_SNAPSHOT 2
|
||||
|
||||
/** View is in MVCC and is visible to purge thread. */
|
||||
#define READ_VIEW_STATE_OPEN 3
|
||||
|
||||
|
||||
/**
|
||||
Read view lists the trx ids of those transactions for which a consistent read
|
||||
should not see the modifications to the database.
|
||||
*/
|
||||
class ReadView
|
||||
{
|
||||
/**
|
||||
View state.
|
||||
|
||||
It is not defined as enum as it has to be updated using atomic operations.
|
||||
Possible values are READ_VIEW_STATE_CLOSED, READ_VIEW_STATE_REGISTERED,
|
||||
READ_VIEW_STATE_SNAPSHOT and READ_VIEW_STATE_OPEN.
|
||||
|
||||
Possible state transfers...
|
||||
|
||||
Opening view for the first time:
|
||||
READ_VIEW_STATE_CLOSED -> READ_VIEW_STATE_SNAPSHOT (non-atomic)
|
||||
|
||||
Complete first time open or reopen:
|
||||
READ_VIEW_STATE_SNAPSHOT -> READ_VIEW_STATE_OPEN (atomic)
|
||||
|
||||
Close view but keep it in list:
|
||||
READ_VIEW_STATE_OPEN -> READ_VIEW_STATE_REGISTERED (atomic)
|
||||
|
||||
Close view and remove it from list:
|
||||
READ_VIEW_STATE_OPEN -> READ_VIEW_STATE_CLOSED (non-atomic)
|
||||
|
||||
Reusing view:
|
||||
READ_VIEW_STATE_REGISTERED -> READ_VIEW_STATE_SNAPSHOT (atomic)
|
||||
|
||||
Removing closed view from list:
|
||||
READ_VIEW_STATE_REGISTERED -> READ_VIEW_STATE_CLOSED (non-atomic)
|
||||
*/
|
||||
int32_t m_state;
|
||||
|
||||
|
||||
class ReadView {
|
||||
public:
|
||||
ReadView() : m_creator_trx_id(TRX_ID_MAX), m_ids(),
|
||||
m_registered(false) {}
|
||||
ReadView(): m_state(READ_VIEW_STATE_CLOSED) {}
|
||||
|
||||
|
||||
/**
|
||||
Copy state from another view.
|
||||
|
||||
@param other view to copy from
|
||||
*/
|
||||
void copy(const ReadView &other)
|
||||
{
|
||||
ut_ad(&other != this);
|
||||
m_ids= other.m_ids;
|
||||
m_up_limit_id= other.m_up_limit_id;
|
||||
m_low_limit_no= other.m_low_limit_no;
|
||||
m_low_limit_id= other.m_low_limit_id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Opens a read view where exactly the transactions serialized before this
|
||||
point in time are seen in the view.
|
||||
|
||||
View becomes visible to purge thread via trx_sys.m_views.
|
||||
|
||||
@param[in,out] trx transaction
|
||||
*/
|
||||
void open(trx_t *trx);
|
||||
|
||||
|
||||
/**
|
||||
Closes the view.
|
||||
|
||||
View becomes not visible to purge thread via trx_sys.m_views.
|
||||
*/
|
||||
void close();
|
||||
|
||||
|
||||
/**
|
||||
Marks view unused.
|
||||
|
||||
View is still in trx_sys.m_views list, but is not visible to purge threads.
|
||||
*/
|
||||
void unuse()
|
||||
{
|
||||
ut_ad(m_state == READ_VIEW_STATE_CLOSED ||
|
||||
m_state == READ_VIEW_STATE_REGISTERED ||
|
||||
m_state == READ_VIEW_STATE_OPEN);
|
||||
if (m_state == READ_VIEW_STATE_OPEN)
|
||||
my_atomic_store32_explicit(&m_state, READ_VIEW_STATE_REGISTERED,
|
||||
MY_MEMORY_ORDER_RELAXED);
|
||||
}
|
||||
|
||||
|
||||
/** m_state getter for trx_sys::clone_oldest_view() trx_sys::size(). */
|
||||
int32_t get_state() const
|
||||
{
|
||||
return my_atomic_load32_explicit(const_cast<int32*>(&m_state),
|
||||
MY_MEMORY_ORDER_ACQUIRE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Returns true if view is open.
|
||||
|
||||
Only used by view owner thread, thus we can omit atomic operations.
|
||||
*/
|
||||
bool is_open() const
|
||||
{
|
||||
ut_ad(m_state == READ_VIEW_STATE_OPEN ||
|
||||
m_state == READ_VIEW_STATE_CLOSED ||
|
||||
m_state == READ_VIEW_STATE_REGISTERED);
|
||||
return m_state == READ_VIEW_STATE_OPEN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Creates a snapshot where exactly the transactions serialized before this
|
||||
point in time are seen in the view.
|
||||
|
||||
@param[in,out] trx transaction
|
||||
*/
|
||||
void snapshot(trx_t *trx);
|
||||
|
||||
|
||||
/**
|
||||
Sets the creator transaction id.
|
||||
|
||||
This should be set only for views created by RW transactions.
|
||||
*/
|
||||
void set_creator_trx_id(trx_id_t id)
|
||||
{
|
||||
ut_ad(id > 0);
|
||||
ut_ad(m_creator_trx_id == 0);
|
||||
m_creator_trx_id= id;
|
||||
}
|
||||
|
||||
|
||||
/** Check whether transaction id is valid.
|
||||
@param[in] id transaction id to check
|
||||
@param[in] name table name */
|
||||
@ -85,25 +223,6 @@ public:
|
||||
return(id < m_up_limit_id);
|
||||
}
|
||||
|
||||
/**
|
||||
Mark the view as closed */
|
||||
void close()
|
||||
{
|
||||
set_creator_trx_id(TRX_ID_MAX);
|
||||
}
|
||||
|
||||
bool is_open() const
|
||||
{
|
||||
return static_cast<trx_id_t>(my_atomic_load64_explicit(
|
||||
const_cast<int64*>(
|
||||
reinterpret_cast<const int64*>(
|
||||
&m_creator_trx_id)),
|
||||
MY_MEMORY_ORDER_RELAXED)) != TRX_ID_MAX;
|
||||
}
|
||||
|
||||
bool is_registered() const { return(m_registered); }
|
||||
void set_registered(bool registered) { m_registered= registered; }
|
||||
|
||||
/**
|
||||
Write the limits to the file.
|
||||
@param file file to write to */
|
||||
@ -129,54 +248,8 @@ public:
|
||||
return(m_low_limit_id);
|
||||
}
|
||||
|
||||
/**
|
||||
@return true if there are no transaction ids in the snapshot */
|
||||
bool empty() const
|
||||
{
|
||||
return(m_ids.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
Set the creator transaction id, existing id must be 0.
|
||||
|
||||
Note: This shouldbe set only for views created by RW
|
||||
transactions. */
|
||||
void set_creator_trx_id(trx_id_t id)
|
||||
{
|
||||
my_atomic_store64_explicit(
|
||||
reinterpret_cast<int64*>(&m_creator_trx_id),
|
||||
id, MY_MEMORY_ORDER_RELAXED);
|
||||
}
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
/**
|
||||
@param rhs view to compare with
|
||||
@return truen if this view is less than or equal rhs */
|
||||
bool le(const ReadView* rhs) const
|
||||
{
|
||||
return(m_low_limit_no <= rhs->m_low_limit_no);
|
||||
}
|
||||
|
||||
trx_id_t up_limit_id() const
|
||||
{
|
||||
return(m_up_limit_id);
|
||||
}
|
||||
#endif /* UNIV_DEBUG */
|
||||
private:
|
||||
/**
|
||||
Opens a read view where exactly the transactions serialized before this
|
||||
point in time are seen in the view.
|
||||
|
||||
@param[in,out] trx transaction */
|
||||
void open(trx_t *trx);
|
||||
|
||||
/**
|
||||
Copy state from another view.
|
||||
@param other view to copy from */
|
||||
inline void copy(const ReadView& other);
|
||||
|
||||
friend class MVCC;
|
||||
|
||||
/** The read should not see any transaction with trx id >= this
|
||||
value. In other words, this is the "high water mark". */
|
||||
trx_id_t m_low_limit_id;
|
||||
@ -199,11 +272,8 @@ private:
|
||||
they can be removed in purge if not needed by other views */
|
||||
trx_id_t m_low_limit_no;
|
||||
|
||||
/** true if transaction is in MVCC::m_views. Only thread that owns
|
||||
this view may access it. */
|
||||
bool m_registered;
|
||||
|
||||
byte pad1[CACHE_LINE_SIZE];
|
||||
public:
|
||||
UT_LIST_NODE_T(ReadView) m_view_list;
|
||||
};
|
||||
|
||||
|
@ -810,17 +810,17 @@ private:
|
||||
/** Solves race condition between register_rw() and snapshot_ids(). */
|
||||
MY_ALIGNED(CACHE_LINE_SIZE) trx_id_t m_rw_trx_hash_version;
|
||||
|
||||
|
||||
/** Active views. */
|
||||
MY_ALIGNED(CACHE_LINE_SIZE) UT_LIST_BASE_NODE_T(ReadView) m_views;
|
||||
|
||||
bool m_initialised;
|
||||
|
||||
public:
|
||||
MY_ALIGNED(CACHE_LINE_SIZE)
|
||||
MY_ALIGNED(CACHE_LINE_SIZE) mutable
|
||||
TrxSysMutex mutex; /*!< mutex protecting most fields in
|
||||
this structure except when noted
|
||||
otherwise */
|
||||
|
||||
MY_ALIGNED(CACHE_LINE_SIZE)
|
||||
MVCC mvcc; /*!< Multi version concurrency control
|
||||
manager */
|
||||
MY_ALIGNED(CACHE_LINE_SIZE)
|
||||
trx_ut_list_t mysql_trx_list; /*!< List of transactions created
|
||||
for MySQL. All user transactions are
|
||||
@ -1032,6 +1032,59 @@ public:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Registers view in MVCC.
|
||||
|
||||
@param view view owned by the caller
|
||||
*/
|
||||
void register_view(ReadView *view)
|
||||
{
|
||||
mutex_enter(&mutex);
|
||||
UT_LIST_ADD_FIRST(m_views, view);
|
||||
mutex_exit(&mutex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Deregisters view in MVCC.
|
||||
|
||||
@param view view owned by the caller
|
||||
*/
|
||||
void deregister_view(ReadView *view)
|
||||
{
|
||||
mutex_enter(&mutex);
|
||||
UT_LIST_REMOVE(m_views, view);
|
||||
mutex_exit(&mutex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Clones the oldest view and stores it in view.
|
||||
|
||||
No need to call ReadView::close(). The caller owns the view that is passed
|
||||
in. This function is called by purge thread to determine whether it should
|
||||
purge the delete marked record or not.
|
||||
*/
|
||||
void clone_oldest_view();
|
||||
|
||||
|
||||
/** @return the number of active views */
|
||||
size_t view_count() const
|
||||
{
|
||||
size_t count= 0;
|
||||
|
||||
mutex_enter(&mutex);
|
||||
for (const ReadView* view= UT_LIST_GET_FIRST(m_views); view;
|
||||
view= UT_LIST_GET_NEXT(m_view_list, view))
|
||||
{
|
||||
if (view->get_state() == READ_VIEW_STATE_OPEN)
|
||||
++count;
|
||||
}
|
||||
mutex_exit(&mutex);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
static my_bool get_min_trx_id_callback(rw_trx_hash_element_t *element,
|
||||
trx_id_t *id)
|
||||
|
@ -24,8 +24,6 @@ The transaction
|
||||
Created 3/26/1996 Heikki Tuuri
|
||||
*******************************************************/
|
||||
|
||||
#include "read0read.h"
|
||||
|
||||
/**********************************************************************//**
|
||||
Determines if a transaction is in the given state.
|
||||
The caller must hold trx_sys.mutex, or it must be the thread
|
||||
|
@ -24,10 +24,11 @@ Cursor read
|
||||
Created 2/16/1997 Heikki Tuuri
|
||||
*******************************************************/
|
||||
|
||||
#include "read0read.h"
|
||||
#include "read0types.h"
|
||||
|
||||
#include "srv0srv.h"
|
||||
#include "trx0sys.h"
|
||||
#include "trx0purge.h"
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
@ -172,52 +173,16 @@ RW transaction can commit or rollback (or free views). AC-NL-RO transactions
|
||||
will mark their views as closed but not actually free their views.
|
||||
*/
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
/** Functor to validate the view list. */
|
||||
struct ViewCheck {
|
||||
|
||||
ViewCheck() : m_prev_view() { }
|
||||
|
||||
void operator()(const ReadView* view)
|
||||
{
|
||||
ut_ad(view->is_registered());
|
||||
ut_a(m_prev_view == NULL
|
||||
|| !view->is_open()
|
||||
|| view->le(m_prev_view));
|
||||
|
||||
m_prev_view = view;
|
||||
}
|
||||
|
||||
const ReadView* m_prev_view;
|
||||
};
|
||||
|
||||
/**
|
||||
Validates a read view list. */
|
||||
|
||||
bool
|
||||
MVCC::validate() const
|
||||
{
|
||||
ViewCheck check;
|
||||
|
||||
ut_ad(mutex_own(&trx_sys.mutex));
|
||||
|
||||
ut_list_map(m_views, check);
|
||||
|
||||
return(true);
|
||||
}
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
|
||||
/**
|
||||
Opens a read view where exactly the transactions serialized before this
|
||||
Creates a snapshot where exactly the transactions serialized before this
|
||||
point in time are seen in the view.
|
||||
|
||||
@param[in,out] trx transaction
|
||||
*/
|
||||
|
||||
void ReadView::open(trx_t *trx)
|
||||
void ReadView::snapshot(trx_t *trx)
|
||||
{
|
||||
ut_ad(mutex_own(&trx_sys.mutex));
|
||||
ut_ad(!mutex_own(&trx_sys.mutex));
|
||||
trx_sys.snapshot_ids(trx, &m_ids, &m_low_limit_id, &m_low_limit_no);
|
||||
m_up_limit_id= m_ids.empty() ? m_low_limit_id : m_ids.front();
|
||||
ut_ad(m_up_limit_id <= m_low_limit_id);
|
||||
@ -225,42 +190,33 @@ void ReadView::open(trx_t *trx)
|
||||
|
||||
|
||||
/**
|
||||
Create a view.
|
||||
Opens a read view where exactly the transactions serialized before this
|
||||
point in time are seen in the view.
|
||||
|
||||
Assigns a read view for a consistent read query. All the consistent reads
|
||||
within the same transaction will get the same read view, which is created
|
||||
when this function is first called for a new started transaction.
|
||||
View becomes visible to purge thread via trx_sys.m_views.
|
||||
|
||||
@param trx transaction instance of caller
|
||||
@param[in,out] trx transaction
|
||||
*/
|
||||
|
||||
void MVCC::view_open(trx_t* trx)
|
||||
void ReadView::open(trx_t *trx)
|
||||
{
|
||||
if (srv_read_only_mode)
|
||||
ut_ad(this == &trx->read_view);
|
||||
switch (m_state)
|
||||
{
|
||||
ut_ad(!trx->read_view.is_open());
|
||||
case READ_VIEW_STATE_OPEN:
|
||||
ut_ad(!srv_read_only_mode);
|
||||
return;
|
||||
}
|
||||
else if (trx->read_view.is_open())
|
||||
return;
|
||||
|
||||
/*
|
||||
Reuse closed view if there were no read-write transactions since (and at) it's
|
||||
creation time.
|
||||
*/
|
||||
if (trx->read_view.is_registered() &&
|
||||
trx_is_autocommit_non_locking(trx) &&
|
||||
trx->read_view.empty() &&
|
||||
trx->read_view.m_low_limit_id == trx_sys.get_max_trx_id())
|
||||
{
|
||||
case READ_VIEW_STATE_REGISTERED:
|
||||
ut_ad(!srv_read_only_mode);
|
||||
/*
|
||||
Reuse closed view if there were no read-write transactions since (and at)
|
||||
its creation time.
|
||||
|
||||
Original comment states: there is an inherent race here between purge
|
||||
and this thread.
|
||||
|
||||
To avoid this race we should've checked trx_sys.get_max_trx_id() and
|
||||
do trx->read_view.set_creator_trx_id(trx->id) atomically under
|
||||
trx_sys.mutex protection. But we're cutting edges to achieve great
|
||||
scalability.
|
||||
set state to READ_VIEW_STATE_OPEN atomically under trx_sys.mutex
|
||||
protection. But we're cutting edges to achieve great scalability.
|
||||
|
||||
There're at least two types of concurrent threads interested in this
|
||||
value: purge coordinator thread (see MVCC::clone_oldest_view()) and
|
||||
@ -280,108 +236,108 @@ void MVCC::view_open(trx_t* trx)
|
||||
Second, scary things start when there's a read-write transaction starting
|
||||
concurrently.
|
||||
|
||||
Speculative execution may reorder set_creator_trx_id() before
|
||||
get_max_trx_id(). In this case purge thread has short gap to clone
|
||||
outdated view. Which is probably not that bad: it just won't be able to
|
||||
purge things that it was actually allowed to purge for a short while.
|
||||
Speculative execution may reorder state change before get_max_trx_id().
|
||||
In this case purge thread has short gap to clone outdated view. Which is
|
||||
probably not that bad: it just won't be able to purge things that it was
|
||||
actually allowed to purge for a short while.
|
||||
|
||||
This thread may as well get suspended after trx_sys.get_max_trx_id() and
|
||||
before trx->read_view.set_creator_trx_id(trx->id). New read-write
|
||||
transaction may get started, committed and purged meanwhile. It is
|
||||
acceptable as well, since this view doesn't see it.
|
||||
before state is set to READ_VIEW_STATE_OPEN. New read-write transaction
|
||||
may get started, committed and purged meanwhile. It is acceptable as
|
||||
well, since this view doesn't see it.
|
||||
*/
|
||||
trx->read_view.set_creator_trx_id(trx->id);
|
||||
return;
|
||||
}
|
||||
if (trx_is_autocommit_non_locking(trx) && m_ids.empty() &&
|
||||
m_low_limit_id == trx_sys.get_max_trx_id())
|
||||
goto reopen;
|
||||
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
trx->read_view.open(trx);
|
||||
if (trx->read_view.is_registered())
|
||||
UT_LIST_REMOVE(m_views, &trx->read_view);
|
||||
else
|
||||
trx->read_view.set_registered(true);
|
||||
trx->read_view.set_creator_trx_id(trx->id);
|
||||
UT_LIST_ADD_FIRST(m_views, &trx->read_view);
|
||||
ut_ad(validate());
|
||||
mutex_exit(&trx_sys.mutex);
|
||||
}
|
||||
/*
|
||||
Can't reuse view, take new snapshot.
|
||||
|
||||
Alas this empty critical section is simplest way to make sure concurrent
|
||||
purge thread completed snapshot copy. Of course purge thread may come
|
||||
again and try to copy once again after we release this mutex, but in
|
||||
this case it is guaranteed to see READ_VIEW_STATE_REGISTERED and thus
|
||||
it'll skip this view.
|
||||
|
||||
void MVCC::view_close(ReadView &view)
|
||||
{
|
||||
view.close();
|
||||
if (view.is_registered())
|
||||
{
|
||||
This critical section can be replaced with new state, which purge thread
|
||||
would set to inform us to wait until it completes snapshot. However it'd
|
||||
complicate m_state even further.
|
||||
*/
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
view.set_registered(false);
|
||||
UT_LIST_REMOVE(m_views, &view);
|
||||
ut_ad(validate());
|
||||
mutex_exit(&trx_sys.mutex);
|
||||
my_atomic_store32_explicit(&m_state, READ_VIEW_STATE_SNAPSHOT,
|
||||
MY_MEMORY_ORDER_RELAXED);
|
||||
break;
|
||||
case READ_VIEW_STATE_CLOSED:
|
||||
if (srv_read_only_mode)
|
||||
return;
|
||||
m_state= READ_VIEW_STATE_SNAPSHOT;
|
||||
trx_sys.register_view(this);
|
||||
break;
|
||||
default:
|
||||
ut_ad(0);
|
||||
}
|
||||
|
||||
snapshot(trx);
|
||||
reopen:
|
||||
m_creator_trx_id= trx->id;
|
||||
my_atomic_store32_explicit(&m_state, READ_VIEW_STATE_OPEN,
|
||||
MY_MEMORY_ORDER_RELEASE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Closes the view.
|
||||
|
||||
The view will become invisible to purge (deregistered from trx_sys).
|
||||
*/
|
||||
void ReadView::close()
|
||||
{
|
||||
ut_ad(m_state == READ_VIEW_STATE_OPEN ||
|
||||
m_state == READ_VIEW_STATE_REGISTERED ||
|
||||
m_state == READ_VIEW_STATE_CLOSED);
|
||||
if (m_state != READ_VIEW_STATE_CLOSED)
|
||||
{
|
||||
trx_sys.deregister_view(this);
|
||||
m_state= READ_VIEW_STATE_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Copy state from another view.
|
||||
@param other view to copy from */
|
||||
Clones the oldest view and stores it in view.
|
||||
|
||||
void
|
||||
ReadView::copy(const ReadView& other)
|
||||
No need to call ReadView::close(). The caller owns the view that is passed
|
||||
in. This function is called by purge thread to determine whether it should
|
||||
purge the delete marked record or not.
|
||||
|
||||
Since foreign views are accessed under the mutex protection, the only
|
||||
possible state transfers are
|
||||
READ_VIEW_STATE_SNAPSHOT -> READ_VIEW_STATE_OPEN
|
||||
READ_VIEW_STATE_OPEN -> READ_VIEW_STATE_REGISTERED
|
||||
All other state transfers are eliminated by the mutex.
|
||||
*/
|
||||
void trx_sys_t::clone_oldest_view()
|
||||
{
|
||||
ut_ad(&other != this);
|
||||
m_ids= other.m_ids;
|
||||
m_up_limit_id = other.m_up_limit_id;
|
||||
m_low_limit_no = other.m_low_limit_no;
|
||||
m_low_limit_id = other.m_low_limit_id;
|
||||
}
|
||||
|
||||
/** Clones the oldest view and stores it in view. No need to
|
||||
call view_close(). The caller owns the view that is passed in.
|
||||
This function is called by Purge to determine whether it should
|
||||
purge the delete marked record or not.
|
||||
@param view Preallocated view, owned by the caller */
|
||||
|
||||
void
|
||||
MVCC::clone_oldest_view(ReadView* view)
|
||||
{
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
/* Find oldest view. */
|
||||
for (const ReadView *oldest_view = UT_LIST_GET_LAST(m_views);
|
||||
oldest_view != NULL;
|
||||
oldest_view = UT_LIST_GET_PREV(m_view_list, oldest_view))
|
||||
{
|
||||
if (oldest_view->is_open())
|
||||
{
|
||||
view->copy(*oldest_view);
|
||||
mutex_exit(&trx_sys.mutex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* No views in the list: snapshot current state. */
|
||||
view->open(0);
|
||||
mutex_exit(&trx_sys.mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
@return the number of active views */
|
||||
|
||||
size_t
|
||||
MVCC::size() const
|
||||
{
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
|
||||
size_t size = 0;
|
||||
|
||||
for (const ReadView* view = UT_LIST_GET_FIRST(m_views);
|
||||
view != NULL;
|
||||
view = UT_LIST_GET_NEXT(m_view_list, view)) {
|
||||
|
||||
if (view->is_open()) {
|
||||
++size;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_exit(&trx_sys.mutex);
|
||||
|
||||
return(size);
|
||||
const ReadView *oldest_view= &purge_sys->view;
|
||||
|
||||
purge_sys->view.snapshot(0);
|
||||
|
||||
mutex_enter(&mutex);
|
||||
/* Find oldest view. */
|
||||
for (const ReadView *v= UT_LIST_GET_FIRST(m_views); v;
|
||||
v= UT_LIST_GET_NEXT(m_view_list, v))
|
||||
{
|
||||
int32_t state;
|
||||
|
||||
while ((state= v->get_state()) == READ_VIEW_STATE_SNAPSHOT)
|
||||
ut_delay(1);
|
||||
|
||||
if (state == READ_VIEW_STATE_OPEN &&
|
||||
v->low_limit_no() < oldest_view->low_limit_no())
|
||||
oldest_view= v;
|
||||
}
|
||||
if (oldest_view != &purge_sys->view)
|
||||
purge_sys->view.copy(*oldest_view);
|
||||
mutex_exit(&mutex);
|
||||
}
|
||||
|
@ -41,7 +41,6 @@ Created 4/20/1996 Heikki Tuuri
|
||||
#include "row0ext.h"
|
||||
#include "row0upd.h"
|
||||
#include "rem0cmp.h"
|
||||
#include "read0read.h"
|
||||
#include "ut0mem.h"
|
||||
#include "gis0geo.h"
|
||||
#include "row0mysql.h"
|
||||
|
@ -51,7 +51,6 @@ Created 12/19/1997 Heikki Tuuri
|
||||
#include "pars0sym.h"
|
||||
#include "pars0pars.h"
|
||||
#include "row0mysql.h"
|
||||
#include "read0read.h"
|
||||
#include "buf0lru.h"
|
||||
#include "srv0srv.h"
|
||||
#include "ha_prototypes.h"
|
||||
@ -2270,7 +2269,7 @@ row_sel_step(
|
||||
if (node->consistent_read) {
|
||||
trx_t *trx = thr_get_trx(thr);
|
||||
/* Assign a read view for the query */
|
||||
trx_sys.mvcc.view_open(trx);
|
||||
trx->read_view.open(trx);
|
||||
node->read_view = trx->read_view.is_open() ?
|
||||
&trx->read_view : NULL;
|
||||
} else {
|
||||
@ -4426,7 +4425,7 @@ row_search_mvcc(
|
||||
/* Assign a read view for the query */
|
||||
trx_start_if_not_started(trx, false);
|
||||
|
||||
trx_sys.mvcc.view_open(trx);
|
||||
trx->read_view.open(trx);
|
||||
|
||||
prebuilt->sql_stat_start = FALSE;
|
||||
} else {
|
||||
@ -5878,8 +5877,7 @@ row_search_check_if_query_cache_permitted(
|
||||
transaction if it does not yet have one */
|
||||
|
||||
if (trx->isolation_level >= TRX_ISO_REPEATABLE_READ) {
|
||||
|
||||
trx_sys.mvcc.view_open(trx);
|
||||
trx->read_view.open(trx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,6 @@ Created 2/6/1997 Heikki Tuuri
|
||||
#include "row0row.h"
|
||||
#include "row0upd.h"
|
||||
#include "rem0cmp.h"
|
||||
#include "read0read.h"
|
||||
#include "lock0lock.h"
|
||||
#include "row0mysql.h"
|
||||
|
||||
|
@ -1365,9 +1365,8 @@ srv_printf_innodb_monitor(
|
||||
srv_conc_get_active_threads(),
|
||||
srv_conc_get_waiting_threads());
|
||||
|
||||
/* This is a dirty read, without holding trx_sys.mutex. */
|
||||
fprintf(file, ULINTPF " read views open inside InnoDB\n",
|
||||
trx_sys.mvcc.size());
|
||||
trx_sys.view_count());
|
||||
|
||||
n_reserved = fil_space_get_n_reserved_extents(0);
|
||||
if (n_reserved > 0) {
|
||||
|
@ -33,7 +33,6 @@ Created 3/26/1996 Heikki Tuuri
|
||||
#include "mtr0log.h"
|
||||
#include "os0thread.h"
|
||||
#include "que0que.h"
|
||||
#include "read0read.h"
|
||||
#include "row0purge.h"
|
||||
#include "row0upd.h"
|
||||
#include "srv0mon.h"
|
||||
@ -1615,7 +1614,7 @@ trx_purge(
|
||||
ut_a(purge_sys->n_submitted == purge_sys->n_completed);
|
||||
|
||||
rw_lock_x_lock(&purge_sys->latch);
|
||||
trx_sys.mvcc.clone_oldest_view(&purge_sys->view);
|
||||
trx_sys.clone_oldest_view();
|
||||
rw_lock_x_unlock(&purge_sys->latch);
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
|
@ -31,7 +31,6 @@ Created 3/26/1996 Heikki Tuuri
|
||||
#include "mtr0log.h"
|
||||
#include "dict0dict.h"
|
||||
#include "ut0mem.h"
|
||||
#include "read0read.h"
|
||||
#include "row0ext.h"
|
||||
#include "row0upd.h"
|
||||
#include "que0que.h"
|
||||
|
@ -37,7 +37,6 @@ Created 3/26/1996 Heikki Tuuri
|
||||
#include "mach0data.h"
|
||||
#include "pars0pars.h"
|
||||
#include "que0que.h"
|
||||
#include "read0read.h"
|
||||
#include "row0mysql.h"
|
||||
#include "row0undo.h"
|
||||
#include "srv0mon.h"
|
||||
|
@ -42,7 +42,6 @@ Created 3/26/1996 Heikki Tuuri
|
||||
#include "log0log.h"
|
||||
#include "log0recv.h"
|
||||
#include "os0file.h"
|
||||
#include "read0read.h"
|
||||
#include "fsp0sysspace.h"
|
||||
|
||||
#include <mysql/service_wsrep.h>
|
||||
@ -402,6 +401,8 @@ trx_sys_t::create()
|
||||
m_initialised = true;
|
||||
mutex_create(LATCH_ID_TRX_SYS, &mutex);
|
||||
UT_LIST_INIT(mysql_trx_list, &trx_t::mysql_trx_list);
|
||||
UT_LIST_INIT(m_views, &ReadView::m_view_list);
|
||||
|
||||
rw_trx_hash.init();
|
||||
}
|
||||
|
||||
@ -507,7 +508,7 @@ trx_sys_t::close()
|
||||
return;
|
||||
}
|
||||
|
||||
if (ulint size = mvcc.size()) {
|
||||
if (size_t size = view_count()) {
|
||||
ib::error() << "All read views were not closed before"
|
||||
" shutdown: " << size << " read views open";
|
||||
}
|
||||
@ -532,6 +533,7 @@ trx_sys_t::close()
|
||||
}
|
||||
|
||||
ut_a(UT_LIST_GET_LEN(mysql_trx_list) == 0);
|
||||
ut_ad(UT_LIST_GET_LEN(m_views) == 0);
|
||||
|
||||
/* We used placement new to create this mutex. Call the destructor. */
|
||||
mutex_free(&mutex);
|
||||
@ -564,7 +566,7 @@ ulint trx_sys_t::any_active_transactions()
|
||||
reinterpret_cast<my_hash_walk_action>
|
||||
(active_count_callback), &total_trx);
|
||||
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
mutex_enter(&mutex);
|
||||
for (trx_t* trx = UT_LIST_GET_FIRST(trx_sys.mysql_trx_list);
|
||||
trx != NULL;
|
||||
trx = UT_LIST_GET_NEXT(mysql_trx_list, trx)) {
|
||||
@ -572,7 +574,7 @@ ulint trx_sys_t::any_active_transactions()
|
||||
total_trx++;
|
||||
}
|
||||
}
|
||||
mutex_exit(&trx_sys.mutex);
|
||||
mutex_exit(&mutex);
|
||||
|
||||
return(total_trx);
|
||||
}
|
||||
|
@ -39,7 +39,6 @@ Created 3/26/1996 Heikki Tuuri
|
||||
#include "log0log.h"
|
||||
#include "os0proc.h"
|
||||
#include "que0que.h"
|
||||
#include "read0read.h"
|
||||
#include "srv0mon.h"
|
||||
#include "srv0srv.h"
|
||||
#include "fsp0sysspace.h"
|
||||
@ -511,7 +510,7 @@ trx_free(trx_t*& trx)
|
||||
trx->mod_tables.clear();
|
||||
|
||||
ut_ad(!trx->read_view.is_open());
|
||||
trx_sys.mvcc.view_close(trx->read_view);
|
||||
trx->read_view.close();
|
||||
|
||||
/* trx locking state should have been reset before returning trx
|
||||
to pool */
|
||||
@ -673,7 +672,7 @@ trx_disconnect_from_mysql(
|
||||
trx_t* trx,
|
||||
bool prepared)
|
||||
{
|
||||
trx_sys.mvcc.view_close(trx->read_view);
|
||||
trx->read_view.close();
|
||||
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
|
||||
@ -972,7 +971,7 @@ trx_lists_init_at_db_start()
|
||||
|
||||
ib::info() << "Trx id counter is " << trx_sys.get_max_trx_id();
|
||||
}
|
||||
trx_sys.mvcc.clone_oldest_view(&purge_sys->view);
|
||||
trx_sys.clone_oldest_view();
|
||||
}
|
||||
|
||||
/** Assign a persistent rollback segment in a round-robin fashion,
|
||||
@ -1512,7 +1511,7 @@ trx_commit_in_memory(
|
||||
the transaction did not modify anything */
|
||||
{
|
||||
trx->must_flush_log_later = false;
|
||||
trx->read_view.close();
|
||||
trx->read_view.unuse();
|
||||
|
||||
if (trx_is_autocommit_non_locking(trx)) {
|
||||
ut_ad(trx->id == 0);
|
||||
|
Loading…
x
Reference in New Issue
Block a user