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:
Sergey Vojtovich 2018-01-20 17:45:42 +04:00
parent ec32c05072
commit 4dc30f3c17
13 changed files with 180 additions and 305 deletions

View file

@ -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 */

View file

@ -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) {

View file

@ -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 */

View file

@ -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

View file

@ -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.

View file

@ -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)

View file

@ -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.*/

View file

@ -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) {

View file

@ -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;
}

View file

@ -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)));
}

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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);