mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 18:20:07 +01:00
MDEV-15019 - InnoDB: store ReadView on trx
This will allow us to reduce critical section protected by trx_sys.mutex: - no need to maintain global m_free list - eliminate if (trx->read_view == NULL) condition. On x86_64 sizeof(Readview) is 144 mostly due to padding, sizeof(trx_t) with ReadView is 1200. Also don't close ReadView for read-write transactions, just mark it closed similarly to read-only. Clean-up: removed n_prepared_recovered_trx and n_prepared_trx, which accidentally re-appeared after some rebase.
This commit is contained in:
parent
ec32c05072
commit
4dc30f3c17
13 changed files with 180 additions and 305 deletions
|
@ -3503,7 +3503,7 @@ ha_innobase::init_table_handle_for_HANDLER(void)
|
|||
|
||||
/* Assign a read view if the transaction does not have it yet */
|
||||
|
||||
trx_assign_read_view(m_prebuilt->trx);
|
||||
trx_sys.mvcc.view_open(m_prebuilt->trx);
|
||||
|
||||
innobase_register_trx(ht, m_user_thd, m_prebuilt->trx);
|
||||
|
||||
|
@ -4394,7 +4394,7 @@ innobase_start_trx_and_assign_read_view(
|
|||
thd_get_trx_isolation(thd));
|
||||
|
||||
if (trx->isolation_level == TRX_ISO_REPEATABLE_READ) {
|
||||
trx_assign_read_view(trx);
|
||||
trx_sys.mvcc.view_open(trx);
|
||||
} else {
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
HA_ERR_UNSUPPORTED,
|
||||
|
@ -16220,7 +16220,7 @@ ha_innobase::external_lock(
|
|||
}
|
||||
|
||||
} else if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
|
||||
&& MVCC::is_view_active(trx->read_view)) {
|
||||
&& trx->read_view.is_open()) {
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
trx_sys.mvcc.view_close(trx->read_view);
|
||||
mutex_exit(&trx_sys.mutex);
|
||||
|
@ -16885,7 +16885,7 @@ ha_innobase::store_lock(
|
|||
(enum_tx_isolation) thd_tx_isolation(thd));
|
||||
|
||||
if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
|
||||
&& MVCC::is_view_active(trx->read_view)) {
|
||||
&& trx->read_view.is_open()) {
|
||||
|
||||
/* At low transaction isolation levels we let
|
||||
each consistent read set its own snapshot */
|
||||
|
|
|
@ -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_assign_read_view(ctx->prebuilt->trx);
|
||||
trx_sys.mvcc.view_open(ctx->prebuilt->trx);
|
||||
}
|
||||
|
||||
if (fts_index) {
|
||||
|
|
|
@ -31,60 +31,51 @@ Created 2/16/1997 Heikki Tuuri
|
|||
#include "read0types.h"
|
||||
|
||||
/** The MVCC read view manager */
|
||||
class MVCC {
|
||||
class MVCC
|
||||
{
|
||||
/** Active views. */
|
||||
UT_LIST_BASE_NODE_T(ReadView) m_views;
|
||||
|
||||
|
||||
/** Validates a read view list. */
|
||||
bool validate() const;
|
||||
public:
|
||||
/** Constructor
|
||||
@param size Number of views to pre-allocate */
|
||||
void create(ulint size);
|
||||
MVCC() { UT_LIST_INIT(m_views, &ReadView::m_view_list); }
|
||||
~MVCC() { ut_ad(UT_LIST_GET_LEN(m_views) == 0); }
|
||||
|
||||
/** Destructor.
|
||||
Free all the views in the m_free list */
|
||||
void close();
|
||||
|
||||
/**
|
||||
Allocate and create a view.
|
||||
@param view view owned by this class created for the
|
||||
caller. Must be freed by calling close()
|
||||
@param trx transaction creating the view */
|
||||
void view_open(ReadView*& view, trx_t* trx);
|
||||
/**
|
||||
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 trx_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.
|
||||
It will also move the closed views from the m_views list to the
|
||||
m_free list. This function is called by Purge to create it view.
|
||||
@param view Preallocated view, owned by the caller */
|
||||
void clone_oldest_view(ReadView* view);
|
||||
/**
|
||||
Close a view created by the above function.
|
||||
@param view view allocated by view_open.
|
||||
*/
|
||||
void view_close(ReadView &view)
|
||||
{
|
||||
view.close();
|
||||
view.set_registered(false);
|
||||
UT_LIST_REMOVE(m_views, &view);
|
||||
ut_ad(validate());
|
||||
}
|
||||
|
||||
/**
|
||||
@return the number of active views */
|
||||
ulint size() const;
|
||||
|
||||
/**
|
||||
@return true if the view is active and valid */
|
||||
static bool is_view_active(ReadView* view)
|
||||
{
|
||||
return view && !view->is_closed();
|
||||
}
|
||||
/**
|
||||
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.
|
||||
|
||||
private:
|
||||
@param view Preallocated view, owned by the caller
|
||||
*/
|
||||
void clone_oldest_view(ReadView *view);
|
||||
|
||||
/**
|
||||
Validates a read view list. */
|
||||
bool validate() const;
|
||||
|
||||
typedef UT_LIST_BASE_NODE_T(ReadView) view_list_t;
|
||||
|
||||
/** Free views ready for reuse. */
|
||||
view_list_t m_free;
|
||||
|
||||
/** Active and closed views, the closed views will have the
|
||||
creator trx id set to TRX_ID_MAX */
|
||||
view_list_t m_views;
|
||||
/** @return the number of active views */
|
||||
size_t size() const;
|
||||
};
|
||||
|
||||
#endif /* read0read_h */
|
||||
|
|
|
@ -198,19 +198,13 @@ public:
|
|||
{
|
||||
ut_ad(m_creator_trx_id != TRX_ID_MAX);
|
||||
m_creator_trx_id = TRX_ID_MAX;
|
||||
set_open(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@return true if the view is closed */
|
||||
bool is_closed() const
|
||||
{
|
||||
return(m_closed);
|
||||
}
|
||||
|
||||
void set_closed(bool closed)
|
||||
{
|
||||
m_closed= closed;
|
||||
}
|
||||
bool is_open() const { return(m_open); }
|
||||
void set_open(bool open) { m_open= open; }
|
||||
bool is_registered() const { return(m_registered); }
|
||||
void set_registered(bool registered) { m_registered= registered; }
|
||||
|
||||
/**
|
||||
Write the limits to the file.
|
||||
|
@ -325,14 +319,15 @@ private:
|
|||
they can be removed in purge if not needed by other views */
|
||||
trx_id_t m_low_limit_no;
|
||||
|
||||
/** AC-NL-RO transaction view that has been "closed". */
|
||||
bool m_closed;
|
||||
/** true if view is open. */
|
||||
bool m_open;
|
||||
|
||||
typedef UT_LIST_NODE_T(ReadView) node_t;
|
||||
/** true if transaction is in MVCC::m_views. Only thread that owns
|
||||
this view may access it. */
|
||||
bool m_registered;
|
||||
|
||||
/** List of read views in trx_sys */
|
||||
byte pad1[64 - sizeof(node_t)];
|
||||
node_t m_view_list;
|
||||
byte pad1[CACHE_LINE_SIZE];
|
||||
UT_LIST_NODE_T(ReadView) m_view_list;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -883,17 +883,6 @@ public:
|
|||
|
||||
MY_ALIGNED(CACHE_LINE_SIZE) rw_trx_hash_t rw_trx_hash;
|
||||
|
||||
ulint n_prepared_trx; /*!< Number of transactions currently
|
||||
in the XA PREPARED state */
|
||||
|
||||
ulint n_prepared_recovered_trx; /*!< Number of transactions
|
||||
currently in XA PREPARED state that are
|
||||
also recovered. Such transactions cannot
|
||||
be added during runtime. They can only
|
||||
occur after recovery if mysqld crashed
|
||||
while there were XA PREPARED
|
||||
transactions. We disable query cache
|
||||
if such transactions exist. */
|
||||
|
||||
/**
|
||||
Constructor.
|
||||
|
|
|
@ -45,13 +45,11 @@ Created 3/26/1996 Heikki Tuuri
|
|||
#include "ut0vec.h"
|
||||
#include "fts0fts.h"
|
||||
#include "srv0srv.h"
|
||||
#include "read0types.h"
|
||||
|
||||
// Forward declaration
|
||||
struct mtr_t;
|
||||
|
||||
// Forward declaration
|
||||
class ReadView;
|
||||
|
||||
// Forward declaration
|
||||
class FlushObserver;
|
||||
|
||||
|
@ -290,23 +288,6 @@ void
|
|||
trx_mark_sql_stat_end(
|
||||
/*==================*/
|
||||
trx_t* trx); /*!< in: trx handle */
|
||||
/********************************************************************//**
|
||||
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. */
|
||||
ReadView*
|
||||
trx_assign_read_view(
|
||||
/*=================*/
|
||||
trx_t* trx); /*!< in: active transaction */
|
||||
|
||||
/****************************************************************//**
|
||||
@return the transaction's read view or NULL if one not assigned. */
|
||||
UNIV_INLINE
|
||||
ReadView*
|
||||
trx_get_read_view(
|
||||
/*==============*/
|
||||
trx_t* trx);
|
||||
|
||||
/****************************************************************//**
|
||||
Prepares a transaction for commit/rollback. */
|
||||
void
|
||||
|
@ -571,7 +552,7 @@ Check transaction state */
|
|||
ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED) \
|
||||
|| trx_state_eq((t), TRX_STATE_FORCED_ROLLBACK)); \
|
||||
ut_ad(!trx->has_logged()); \
|
||||
ut_ad(!MVCC::is_view_active((t)->read_view)); \
|
||||
ut_ad(!(t)->read_view.is_open()); \
|
||||
ut_ad((t)->lock.wait_thr == NULL); \
|
||||
ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0); \
|
||||
ut_ad((t)->dict_operation == TRX_DICT_OP_NONE); \
|
||||
|
@ -1000,7 +981,7 @@ public:
|
|||
|
||||
trx_state_t state;
|
||||
|
||||
ReadView* read_view; /*!< consistent read view used in the
|
||||
ReadView read_view; /*!< consistent read view used in the
|
||||
transaction, or NULL if not yet set */
|
||||
|
||||
UT_LIST_NODE_T(trx_t)
|
||||
|
|
|
@ -211,17 +211,6 @@ ok:
|
|||
trx->dict_operation = op;
|
||||
}
|
||||
|
||||
/**
|
||||
@param trx Get the active view for this transaction, if one exists
|
||||
@return the transaction's read view or NULL if one not assigned. */
|
||||
UNIV_INLINE
|
||||
ReadView*
|
||||
trx_get_read_view(
|
||||
trx_t* trx)
|
||||
{
|
||||
return(!MVCC::is_view_active(trx->read_view) ? NULL : trx->read_view);
|
||||
}
|
||||
|
||||
/**
|
||||
@param[in] trx Transaction to check
|
||||
@return true if the transaction is a high priority transaction.*/
|
||||
|
|
|
@ -415,7 +415,8 @@ lock_clust_rec_cons_read_sees(
|
|||
operate on same temp-table and so read of temp-table is
|
||||
always consistent read. */
|
||||
if (srv_read_only_mode || dict_table_is_temporary(index->table)) {
|
||||
ut_ad(view == 0 || dict_table_is_temporary(index->table));
|
||||
ut_ad(!view->is_open()
|
||||
|| dict_table_is_temporary(index->table));
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
@ -5719,15 +5720,13 @@ lock_trx_print_wait_and_mvcc_state(
|
|||
|
||||
trx_print_latched(file, trx, 600);
|
||||
|
||||
/* Note: this read_view access is data race. Further
|
||||
read_view->is_active() check is race condition. But it should
|
||||
"kind of work" because read_view is freed only at shutdown.
|
||||
/* Note: read_view->is_active() check is race condition. But it
|
||||
should "kind of work" because read_view is freed only at shutdown.
|
||||
Worst thing that may happen is that it'll get transferred to
|
||||
another thread and print wrong values. */
|
||||
const ReadView* read_view = trx->read_view;
|
||||
|
||||
if (read_view != NULL && !read_view->is_closed()) {
|
||||
read_view->print_limits(file);
|
||||
if (trx->read_view.is_open()) {
|
||||
trx->read_view.print_limits(file);
|
||||
}
|
||||
|
||||
if (trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
|
||||
|
|
|
@ -182,8 +182,9 @@ struct ViewCheck {
|
|||
|
||||
void operator()(const ReadView* view)
|
||||
{
|
||||
ut_ad(view->is_registered());
|
||||
ut_a(m_prev_view == NULL
|
||||
|| view->is_closed()
|
||||
|| !view->is_open()
|
||||
|| view->le(m_prev_view));
|
||||
|
||||
m_prev_view = view;
|
||||
|
@ -325,7 +326,9 @@ ReadView::ReadView()
|
|||
m_up_limit_id(),
|
||||
m_creator_trx_id(),
|
||||
m_ids(),
|
||||
m_low_limit_no()
|
||||
m_low_limit_no(),
|
||||
m_open(false),
|
||||
m_registered(false)
|
||||
{
|
||||
ut_d(::memset(&m_view_list, 0x0, sizeof(m_view_list)));
|
||||
}
|
||||
|
@ -337,34 +340,6 @@ ReadView::~ReadView()
|
|||
// Do nothing
|
||||
}
|
||||
|
||||
/** Constructor
|
||||
@param size Number of views to pre-allocate */
|
||||
void MVCC::create(ulint size)
|
||||
{
|
||||
UT_LIST_INIT(m_free, &ReadView::m_view_list);
|
||||
UT_LIST_INIT(m_views, &ReadView::m_view_list);
|
||||
|
||||
for (ulint i = 0; i < size; ++i) {
|
||||
ReadView* view = UT_NEW_NOKEY(ReadView());
|
||||
|
||||
UT_LIST_ADD_FIRST(m_free, view);
|
||||
}
|
||||
}
|
||||
|
||||
void MVCC::close()
|
||||
{
|
||||
for (ReadView* view = UT_LIST_GET_FIRST(m_free);
|
||||
view != NULL;
|
||||
view = UT_LIST_GET_FIRST(m_free)) {
|
||||
|
||||
UT_LIST_REMOVE(m_free, view);
|
||||
|
||||
UT_DELETE(view);
|
||||
}
|
||||
|
||||
ut_a(UT_LIST_GET_LEN(m_views) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
Copy the transaction ids from the source vector */
|
||||
|
||||
|
@ -488,76 +463,89 @@ ReadView::complete()
|
|||
m_up_limit_id = !m_ids.empty() ? m_ids.front() : m_low_limit_id;
|
||||
|
||||
ut_ad(m_up_limit_id <= m_low_limit_id);
|
||||
|
||||
m_closed = false;
|
||||
set_open(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Allocate and create a view.
|
||||
@param view view owned by this class created for the
|
||||
caller. Must be freed by calling view_close()
|
||||
@param trx transaction instance of caller */
|
||||
void
|
||||
MVCC::view_open(ReadView*& view, trx_t* trx)
|
||||
Create a 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.
|
||||
|
||||
@param trx transaction instance of caller
|
||||
*/
|
||||
|
||||
void MVCC::view_open(trx_t* trx)
|
||||
{
|
||||
ut_ad(!srv_read_only_mode);
|
||||
if (srv_read_only_mode)
|
||||
{
|
||||
ut_ad(!trx->read_view.is_open());
|
||||
return;
|
||||
}
|
||||
else if (trx->read_view.is_open())
|
||||
return;
|
||||
|
||||
/** If no new RW transaction has been started since the last view
|
||||
was created then reuse the the existing view. */
|
||||
if (view != NULL) {
|
||||
/*
|
||||
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())
|
||||
{
|
||||
/*
|
||||
Original comment states: there is an inherent race here between purge
|
||||
and this thread.
|
||||
|
||||
uintptr_t p = reinterpret_cast<uintptr_t>(view);
|
||||
To avoid this race we should've checked trx_sys.get_max_trx_id() and
|
||||
do trx->read_view.set_open(true) atomically under trx_sys.mutex
|
||||
protection. But we're cutting edges to achieve great scalability.
|
||||
|
||||
view = reinterpret_cast<ReadView*>(p & ~1);
|
||||
There're at least two types of concurrent threads interested in this
|
||||
value: purge coordinator thread (see MVCC::clone_oldest_view()) and
|
||||
InnoDB monitor thread (see lock_trx_print_wait_and_mvcc_state()).
|
||||
|
||||
ut_ad(view->m_closed);
|
||||
What bad things can happen because we allow this race?
|
||||
|
||||
/* NOTE: This can be optimised further, for now we only
|
||||
resuse the view iff there are no active RW transactions.
|
||||
First, purge thread may be affected by this race condition only if this
|
||||
view is the oldest open view. In other words this view is either last in
|
||||
m_views list or there're no open views beyond.
|
||||
|
||||
There is an inherent race here between purge and this
|
||||
thread. Purge will skip views that are marked as closed.
|
||||
Therefore we must set the low limit id after we reset the
|
||||
closed status after the check. */
|
||||
In this case purge may not catch this view and clone some younger view
|
||||
instead. It might be kind of alright, because there were no read-write
|
||||
transactions and there should be nothing to purge. Besides younger view
|
||||
must have exactly the same values.
|
||||
|
||||
if (trx_is_autocommit_non_locking(trx) && view->empty()) {
|
||||
Second, scary things start when there's a read-write transaction starting
|
||||
concurrently.
|
||||
|
||||
view->m_closed = false;
|
||||
Speculative execution may reorder set_open() 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.
|
||||
|
||||
if (view->m_low_limit_id == trx_sys.get_max_trx_id()) {
|
||||
return;
|
||||
} else {
|
||||
view->m_closed = true;
|
||||
}
|
||||
}
|
||||
This thread may as well get suspended after trx_sys.get_max_trx_id() and
|
||||
before trx->read_view.set_open(true). 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_open(true);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
|
||||
UT_LIST_REMOVE(m_views, view);
|
||||
|
||||
} else {
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
if ((view = UT_LIST_GET_FIRST(m_free))) {
|
||||
UT_LIST_REMOVE(m_free, view);
|
||||
} else if (!(view = UT_NEW_NOKEY(ReadView()))) {
|
||||
ib::error() << "Failed to allocate MVCC view";
|
||||
}
|
||||
}
|
||||
|
||||
if (view != NULL) {
|
||||
|
||||
view->prepare(trx->id);
|
||||
|
||||
view->complete();
|
||||
|
||||
UT_LIST_ADD_FIRST(m_views, view);
|
||||
|
||||
ut_ad(!view->is_closed());
|
||||
|
||||
ut_ad(validate());
|
||||
}
|
||||
|
||||
mutex_exit(&trx_sys.mutex);
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
trx->read_view.prepare(trx->id);
|
||||
trx->read_view.complete();
|
||||
if (trx->read_view.is_registered())
|
||||
UT_LIST_REMOVE(m_views, &trx->read_view);
|
||||
else
|
||||
trx->read_view.set_registered(true);
|
||||
UT_LIST_ADD_FIRST(m_views, &trx->read_view);
|
||||
ut_ad(validate());
|
||||
mutex_exit(&trx_sys.mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -625,7 +613,7 @@ MVCC::clone_oldest_view(ReadView* view)
|
|||
oldest_view != NULL;
|
||||
oldest_view = UT_LIST_GET_PREV(m_view_list, oldest_view))
|
||||
{
|
||||
if (!oldest_view->is_closed())
|
||||
if (oldest_view->is_open())
|
||||
{
|
||||
view->copy_prepare(*oldest_view);
|
||||
mutex_exit(&trx_sys.mutex);
|
||||
|
@ -642,18 +630,18 @@ MVCC::clone_oldest_view(ReadView* view)
|
|||
/**
|
||||
@return the number of active views */
|
||||
|
||||
ulint
|
||||
size_t
|
||||
MVCC::size() const
|
||||
{
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
|
||||
ulint size = 0;
|
||||
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_closed()) {
|
||||
if (view->is_open()) {
|
||||
++size;
|
||||
}
|
||||
}
|
||||
|
@ -662,18 +650,3 @@ MVCC::size() const
|
|||
|
||||
return(size);
|
||||
}
|
||||
|
||||
/**
|
||||
Close a view created by the above function.
|
||||
@param view view allocated by trx_open. */
|
||||
|
||||
void
|
||||
MVCC::view_close(ReadView*& view)
|
||||
{
|
||||
ut_ad(mutex_own(&trx_sys.mutex));
|
||||
view->close();
|
||||
UT_LIST_REMOVE(m_views, view);
|
||||
UT_LIST_ADD_LAST(m_free, view);
|
||||
ut_ad(validate());
|
||||
view = NULL;
|
||||
}
|
||||
|
|
|
@ -2101,16 +2101,16 @@ end_of_index:
|
|||
ONLINE_INDEX_COMPLETE state between the time
|
||||
the DML thread has updated the clustered index
|
||||
but has not yet accessed secondary index. */
|
||||
ut_ad(MVCC::is_view_active(trx->read_view));
|
||||
ut_ad(trx->read_view.is_open());
|
||||
ut_ad(rec_trx_id != trx->id);
|
||||
|
||||
if (!trx->read_view->changes_visible(
|
||||
if (!trx->read_view.changes_visible(
|
||||
rec_trx_id, old_table->name)) {
|
||||
rec_t* old_vers;
|
||||
|
||||
row_vers_build_for_consistent_read(
|
||||
rec, &mtr, clust_index, &offsets,
|
||||
trx->read_view, &row_heap,
|
||||
&trx->read_view, &row_heap,
|
||||
row_heap, &old_vers, NULL);
|
||||
|
||||
if (!old_vers) {
|
||||
|
@ -4526,8 +4526,8 @@ row_merge_is_index_usable(
|
|||
return(!dict_index_is_corrupted(index)
|
||||
&& (dict_table_is_temporary(index->table)
|
||||
|| index->trx_id == 0
|
||||
|| !MVCC::is_view_active(trx->read_view)
|
||||
|| trx->read_view->changes_visible(
|
||||
|| !trx->read_view.is_open()
|
||||
|| trx->read_view.changes_visible(
|
||||
index->trx_id,
|
||||
index->table->name)));
|
||||
}
|
||||
|
|
|
@ -2268,15 +2268,11 @@ row_sel_step(
|
|||
plan_reset_cursor(sel_node_get_nth_plan(node, 0));
|
||||
|
||||
if (node->consistent_read) {
|
||||
trx_t *trx = thr_get_trx(thr);
|
||||
/* Assign a read view for the query */
|
||||
trx_assign_read_view(thr_get_trx(thr));
|
||||
|
||||
if (thr_get_trx(thr)->read_view != NULL) {
|
||||
node->read_view = thr_get_trx(thr)->read_view;
|
||||
} else {
|
||||
node->read_view = NULL;
|
||||
}
|
||||
|
||||
trx_sys.mvcc.view_open(trx);
|
||||
node->read_view = trx->read_view.is_open() ?
|
||||
&trx->read_view : NULL;
|
||||
} else {
|
||||
sym_node_t* table_node;
|
||||
lock_mode i_lock_mode;
|
||||
|
@ -3440,12 +3436,12 @@ row_sel_get_clust_rec_for_mysql(
|
|||
if (trx->isolation_level > TRX_ISO_READ_UNCOMMITTED
|
||||
&& !lock_clust_rec_cons_read_sees(
|
||||
clust_rec, clust_index, *offsets,
|
||||
trx_get_read_view(trx))) {
|
||||
&trx->read_view)) {
|
||||
|
||||
/* The following call returns 'offsets' associated with
|
||||
'old_vers' */
|
||||
err = row_sel_build_prev_vers_for_mysql(
|
||||
trx->read_view, clust_index, prebuilt,
|
||||
&trx->read_view, clust_index, prebuilt,
|
||||
clust_rec, offsets, offset_heap, &old_vers,
|
||||
vrow, mtr);
|
||||
|
||||
|
@ -3864,8 +3860,8 @@ exhausted:
|
|||
*offsets = rec_get_offsets(rec, index, *offsets, true,
|
||||
ULINT_UNDEFINED, heap);
|
||||
|
||||
if (!lock_clust_rec_cons_read_sees(
|
||||
rec, index, *offsets, trx_get_read_view(trx))) {
|
||||
if (!lock_clust_rec_cons_read_sees(rec, index, *offsets,
|
||||
&trx->read_view)) {
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
@ -4269,7 +4265,7 @@ row_search_mvcc(
|
|||
|
||||
if (prebuilt->select_lock_type == LOCK_NONE
|
||||
&& trx->isolation_level > TRX_ISO_READ_UNCOMMITTED
|
||||
&& MVCC::is_view_active(trx->read_view)) {
|
||||
&& trx->read_view.is_open()) {
|
||||
|
||||
/* This is a SELECT query done as a consistent read,
|
||||
and the read view has already been allocated:
|
||||
|
@ -4367,7 +4363,7 @@ row_search_mvcc(
|
|||
|
||||
ut_ad(prebuilt->sql_stat_start
|
||||
|| prebuilt->select_lock_type != LOCK_NONE
|
||||
|| MVCC::is_view_active(trx->read_view)
|
||||
|| trx->read_view.is_open()
|
||||
|| prebuilt->table->no_rollback()
|
||||
|| srv_read_only_mode);
|
||||
|
||||
|
@ -4414,7 +4410,7 @@ row_search_mvcc(
|
|||
} else if (!prebuilt->sql_stat_start) {
|
||||
/* No need to set an intention lock or assign a read view */
|
||||
|
||||
if (!MVCC::is_view_active(trx->read_view)
|
||||
if (!trx->read_view.is_open()
|
||||
&& !srv_read_only_mode
|
||||
&& prebuilt->select_lock_type == LOCK_NONE) {
|
||||
|
||||
|
@ -4430,9 +4426,7 @@ row_search_mvcc(
|
|||
/* Assign a read view for the query */
|
||||
trx_start_if_not_started(trx, false);
|
||||
|
||||
if (!srv_read_only_mode) {
|
||||
trx_assign_read_view(trx);
|
||||
}
|
||||
trx_sys.mvcc.view_open(trx);
|
||||
|
||||
prebuilt->sql_stat_start = FALSE;
|
||||
} else {
|
||||
|
@ -5036,14 +5030,13 @@ no_gap_lock:
|
|||
|
||||
if (srv_force_recovery < 5
|
||||
&& !lock_clust_rec_cons_read_sees(
|
||||
rec, index, offsets,
|
||||
trx_get_read_view(trx))) {
|
||||
rec, index, offsets, &trx->read_view)) {
|
||||
|
||||
rec_t* old_vers;
|
||||
/* The following call returns 'offsets'
|
||||
associated with 'old_vers' */
|
||||
err = row_sel_build_prev_vers_for_mysql(
|
||||
trx->read_view, clust_index,
|
||||
&trx->read_view, clust_index,
|
||||
prebuilt, rec, &offsets, &heap,
|
||||
&old_vers, need_vrow ? &vrow : NULL,
|
||||
&mtr);
|
||||
|
@ -5073,7 +5066,7 @@ no_gap_lock:
|
|||
|
||||
if (!srv_read_only_mode
|
||||
&& !lock_sec_rec_cons_read_sees(
|
||||
rec, index, trx->read_view)) {
|
||||
rec, index, &trx->read_view)) {
|
||||
/* We should look at the clustered index.
|
||||
However, as this is a non-locking read,
|
||||
we can skip the clustered index lookup if
|
||||
|
@ -5877,18 +5870,16 @@ row_search_check_if_query_cache_permitted(
|
|||
|
||||
const bool ret = lock_table_get_n_locks(table) == 0
|
||||
&& ((trx->id != 0 && trx->id >= table->query_cache_inv_id)
|
||||
|| !MVCC::is_view_active(trx->read_view)
|
||||
|| trx->read_view->low_limit_id()
|
||||
|| !trx->read_view.is_open()
|
||||
|| trx->read_view.low_limit_id()
|
||||
>= table->query_cache_inv_id);
|
||||
if (ret) {
|
||||
/* If the isolation level is high, assign a read view for the
|
||||
transaction if it does not yet have one */
|
||||
|
||||
if (trx->isolation_level >= TRX_ISO_REPEATABLE_READ
|
||||
&& !srv_read_only_mode
|
||||
&& !MVCC::is_view_active(trx->read_view)) {
|
||||
if (trx->isolation_level >= TRX_ISO_REPEATABLE_READ) {
|
||||
|
||||
trx_sys.mvcc.view_open(trx->read_view, trx);
|
||||
trx_sys.mvcc.view_open(trx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -459,8 +459,6 @@ trx_sys_t::create()
|
|||
UT_LIST_INIT(serialisation_list, &trx_t::no_list);
|
||||
UT_LIST_INIT(mysql_trx_list, &trx_t::mysql_trx_list);
|
||||
|
||||
mvcc.create(1024);
|
||||
|
||||
rw_trx_hash.init();
|
||||
}
|
||||
|
||||
|
@ -590,8 +588,6 @@ trx_sys_t::close()
|
|||
}
|
||||
}
|
||||
|
||||
mvcc.close();
|
||||
|
||||
ut_a(UT_LIST_GET_LEN(mysql_trx_list) == 0);
|
||||
ut_a(UT_LIST_GET_LEN(serialisation_list) == 0);
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ trx_init(
|
|||
|
||||
trx->last_sql_stat_start.least_undo_no = 0;
|
||||
|
||||
ut_ad(!MVCC::is_view_active(trx->read_view));
|
||||
ut_ad(!trx->read_view.is_open());
|
||||
|
||||
trx->lock.rec_cached = 0;
|
||||
|
||||
|
@ -246,6 +246,7 @@ struct TrxFactory {
|
|||
new(&trx->lock.table_locks) lock_pool_t();
|
||||
|
||||
new(&trx->hit_list) hit_list_t();
|
||||
new(&trx->read_view) ReadView();
|
||||
|
||||
trx->rw_trx_hash_pins = 0;
|
||||
trx_init(trx);
|
||||
|
@ -301,7 +302,7 @@ struct TrxFactory {
|
|||
|
||||
trx->mod_tables.~trx_mod_tables_t();
|
||||
|
||||
ut_ad(trx->read_view == NULL);
|
||||
ut_ad(!trx->read_view.is_open());
|
||||
|
||||
if (!trx->lock.rec_pool.empty()) {
|
||||
|
||||
|
@ -508,7 +509,12 @@ trx_free(trx_t*& trx)
|
|||
|
||||
trx->mod_tables.clear();
|
||||
|
||||
ut_ad(trx->read_view == NULL);
|
||||
ut_ad(!trx->read_view.is_open());
|
||||
if (trx->read_view.is_registered()) {
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
trx_sys.mvcc.view_close(trx->read_view);
|
||||
mutex_exit(&trx_sys.mutex);
|
||||
}
|
||||
|
||||
/* trx locking state should have been reset before returning trx
|
||||
to pool */
|
||||
|
@ -677,7 +683,7 @@ trx_disconnect_from_mysql(
|
|||
|
||||
UT_LIST_REMOVE(trx_sys.mysql_trx_list, trx);
|
||||
|
||||
if (trx->read_view != NULL) {
|
||||
if (trx->read_view.is_open()) {
|
||||
trx_sys.mvcc.view_close(trx->read_view);
|
||||
}
|
||||
|
||||
|
@ -1536,17 +1542,11 @@ trx_erase_lists(
|
|||
{
|
||||
ut_ad(trx->id > 0);
|
||||
|
||||
if (trx->rsegs.m_redo.rseg && trx->read_view) {
|
||||
ut_ad(!trx->read_only);
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
trx_sys.mvcc.view_close(trx->read_view);
|
||||
} else {
|
||||
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
}
|
||||
|
||||
if (serialised) {
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
UT_LIST_REMOVE(trx_sys.serialisation_list, trx);
|
||||
} else {
|
||||
mutex_enter(&trx_sys.mutex);
|
||||
}
|
||||
|
||||
trx_ids_t::iterator it = std::lower_bound(
|
||||
|
@ -1574,6 +1574,8 @@ trx_commit_in_memory(
|
|||
written */
|
||||
{
|
||||
trx->must_flush_log_later = false;
|
||||
trx->read_view.set_open(false);
|
||||
|
||||
|
||||
if (trx_is_autocommit_non_locking(trx)) {
|
||||
ut_ad(trx->id == 0);
|
||||
|
@ -1597,10 +1599,6 @@ trx_commit_in_memory(
|
|||
|
||||
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
|
||||
|
||||
if (trx->read_view != NULL) {
|
||||
trx->read_view->set_closed(true);
|
||||
}
|
||||
|
||||
MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT);
|
||||
|
||||
/* AC-NL-RO transactions can't be rolled back asynchronously. */
|
||||
|
@ -1629,9 +1627,6 @@ trx_commit_in_memory(
|
|||
|
||||
if (trx->read_only || trx->rsegs.m_redo.rseg == NULL) {
|
||||
MONITOR_INC(MONITOR_TRX_RO_COMMIT);
|
||||
if (trx->read_view != NULL) {
|
||||
trx->read_view->set_closed(true);
|
||||
}
|
||||
} else {
|
||||
trx_update_mod_tables_timestamp(trx);
|
||||
MONITOR_INC(MONITOR_TRX_RW_COMMIT);
|
||||
|
@ -1875,30 +1870,6 @@ trx_commit(
|
|||
trx_commit_low(trx, mtr);
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
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.
|
||||
@return consistent read view */
|
||||
ReadView*
|
||||
trx_assign_read_view(
|
||||
/*=================*/
|
||||
trx_t* trx) /*!< in/out: active transaction */
|
||||
{
|
||||
ut_ad(trx->state == TRX_STATE_ACTIVE);
|
||||
|
||||
if (srv_read_only_mode) {
|
||||
|
||||
ut_ad(trx->read_view == NULL);
|
||||
return(NULL);
|
||||
|
||||
} else if (!MVCC::is_view_active(trx->read_view)) {
|
||||
trx_sys.mvcc.view_open(trx->read_view, trx);
|
||||
}
|
||||
|
||||
return(trx->read_view);
|
||||
}
|
||||
|
||||
/****************************************************************//**
|
||||
Prepares a transaction for commit/rollback. */
|
||||
void
|
||||
|
@ -2754,8 +2725,8 @@ trx_set_rw_mode(
|
|||
trx_sys.rw_trx_ids.push_back(trx->id);
|
||||
|
||||
/* So that we can see our own changes. */
|
||||
if (MVCC::is_view_active(trx->read_view)) {
|
||||
trx->read_view->creator_trx_id(trx->id);
|
||||
if (trx->read_view.is_open()) {
|
||||
trx->read_view.creator_trx_id(trx->id);
|
||||
}
|
||||
mutex_exit(&trx_sys.mutex);
|
||||
trx_sys.rw_trx_hash.insert(trx);
|
||||
|
|
Loading…
Add table
Reference in a new issue