From 4dc30f3c170b9deab1ff0b472930baba5c65b01c Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Sat, 20 Jan 2018 17:45:42 +0400 Subject: [PATCH] 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. --- storage/innobase/handler/ha_innodb.cc | 8 +- storage/innobase/handler/handler0alter.cc | 2 +- storage/innobase/include/read0read.h | 79 +++++----- storage/innobase/include/read0types.h | 29 ++-- storage/innobase/include/trx0sys.h | 11 -- storage/innobase/include/trx0trx.h | 25 +-- storage/innobase/include/trx0trx.ic | 11 -- storage/innobase/lock/lock0lock.cc | 13 +- storage/innobase/read/read0read.cc | 183 +++++++++------------- storage/innobase/row/row0merge.cc | 10 +- storage/innobase/row/row0sel.cc | 47 +++--- storage/innobase/trx/trx0sys.cc | 4 - storage/innobase/trx/trx0trx.cc | 63 ++------ 13 files changed, 180 insertions(+), 305 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 42062e452ee..a0136782ce2 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -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 */ diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 8c1ba192b17..fa87e35df35 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -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) { diff --git a/storage/innobase/include/read0read.h b/storage/innobase/include/read0read.h index 3a03f795666..f17e7394337 100644 --- a/storage/innobase/include/read0read.h +++ b/storage/innobase/include/read0read.h @@ -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 */ diff --git a/storage/innobase/include/read0types.h b/storage/innobase/include/read0types.h index 926324e7bf0..e30976da302 100644 --- a/storage/innobase/include/read0types.h +++ b/storage/innobase/include/read0types.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 diff --git a/storage/innobase/include/trx0sys.h b/storage/innobase/include/trx0sys.h index 9e34ba40722..47ea505ca54 100644 --- a/storage/innobase/include/trx0sys.h +++ b/storage/innobase/include/trx0sys.h @@ -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. diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index 05bef6ac3d1..dfcba11fcf5 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -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) diff --git a/storage/innobase/include/trx0trx.ic b/storage/innobase/include/trx0trx.ic index 4b00a604c31..d84a4299cfb 100644 --- a/storage/innobase/include/trx0trx.ic +++ b/storage/innobase/include/trx0trx.ic @@ -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.*/ diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index 486c1379a52..2c097d1a75a 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -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) { diff --git a/storage/innobase/read/read0read.cc b/storage/innobase/read/read0read.cc index 4d79a12f52f..0ddb8c37feb 100644 --- a/storage/innobase/read/read0read.cc +++ b/storage/innobase/read/read0read.cc @@ -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(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(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; -} diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 3f906539ed8..70acd5a4f43 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -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))); } diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 6f4faad93ff..0f9daa2a17e 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -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); } } diff --git a/storage/innobase/trx/trx0sys.cc b/storage/innobase/trx/trx0sys.cc index 0663642bafd..3d9c6a48646 100644 --- a/storage/innobase/trx/trx0sys.cc +++ b/storage/innobase/trx/trx0sys.cc @@ -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); diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 17a97895fd5..312e955cf76 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -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);