mirror of
https://github.com/MariaDB/server.git
synced 2025-12-01 17:39:21 +03: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:
@@ -3503,7 +3503,7 @@ ha_innobase::init_table_handle_for_HANDLER(void)
|
|||||||
|
|
||||||
/* Assign a read view if the transaction does not have it yet */
|
/* 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);
|
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));
|
thd_get_trx_isolation(thd));
|
||||||
|
|
||||||
if (trx->isolation_level == TRX_ISO_REPEATABLE_READ) {
|
if (trx->isolation_level == TRX_ISO_REPEATABLE_READ) {
|
||||||
trx_assign_read_view(trx);
|
trx_sys.mvcc.view_open(trx);
|
||||||
} else {
|
} else {
|
||||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||||
HA_ERR_UNSUPPORTED,
|
HA_ERR_UNSUPPORTED,
|
||||||
@@ -16220,7 +16220,7 @@ ha_innobase::external_lock(
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
|
} 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);
|
mutex_enter(&trx_sys.mutex);
|
||||||
trx_sys.mvcc.view_close(trx->read_view);
|
trx_sys.mvcc.view_close(trx->read_view);
|
||||||
mutex_exit(&trx_sys.mutex);
|
mutex_exit(&trx_sys.mutex);
|
||||||
@@ -16885,7 +16885,7 @@ ha_innobase::store_lock(
|
|||||||
(enum_tx_isolation) thd_tx_isolation(thd));
|
(enum_tx_isolation) thd_tx_isolation(thd));
|
||||||
|
|
||||||
if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
|
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
|
/* At low transaction isolation levels we let
|
||||||
each consistent read set its own snapshot */
|
each consistent read set its own snapshot */
|
||||||
|
|||||||
@@ -5562,7 +5562,7 @@ error_handling_drop_uncached:
|
|||||||
if (ctx->online && ctx->num_to_add_index) {
|
if (ctx->online && ctx->num_to_add_index) {
|
||||||
/* Assign a consistent read view for
|
/* Assign a consistent read view for
|
||||||
row_merge_read_clustered_index(). */
|
row_merge_read_clustered_index(). */
|
||||||
trx_assign_read_view(ctx->prebuilt->trx);
|
trx_sys.mvcc.view_open(ctx->prebuilt->trx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fts_index) {
|
if (fts_index) {
|
||||||
|
|||||||
@@ -31,60 +31,51 @@ Created 2/16/1997 Heikki Tuuri
|
|||||||
#include "read0types.h"
|
#include "read0types.h"
|
||||||
|
|
||||||
/** The MVCC read view manager */
|
/** The MVCC read view manager */
|
||||||
class MVCC {
|
class MVCC
|
||||||
public:
|
{
|
||||||
/** Constructor
|
/** Active views. */
|
||||||
@param size Number of views to pre-allocate */
|
UT_LIST_BASE_NODE_T(ReadView) m_views;
|
||||||
void create(ulint size);
|
|
||||||
|
|
||||||
|
/** 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); }
|
||||||
|
|
||||||
/** Destructor.
|
|
||||||
Free all the views in the m_free list */
|
|
||||||
void close();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Allocate and create a view.
|
Allocate and create a view.
|
||||||
@param view view owned by this class created for the
|
@param trx transaction creating the view
|
||||||
caller. Must be freed by calling close()
|
*/
|
||||||
@param trx transaction creating the view */
|
void view_open(trx_t *trx);
|
||||||
void view_open(ReadView*& view, trx_t* trx);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Close a view created by the above function.
|
Close a view created by the above function.
|
||||||
@param view view allocated by trx_open. */
|
@param view view allocated by view_open.
|
||||||
void view_close(ReadView*& view);
|
*/
|
||||||
|
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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
@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();
|
view.close();
|
||||||
|
view.set_registered(false);
|
||||||
|
UT_LIST_REMOVE(m_views, &view);
|
||||||
|
ut_ad(validate());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Validates a read view list. */
|
Clones the oldest view and stores it in view. No need to
|
||||||
bool validate() const;
|
call view_close(). The caller owns the view that is passed in.
|
||||||
|
This function is called by Purge to create it view.
|
||||||
|
|
||||||
typedef UT_LIST_BASE_NODE_T(ReadView) view_list_t;
|
@param view Preallocated view, owned by the caller
|
||||||
|
*/
|
||||||
|
void clone_oldest_view(ReadView *view);
|
||||||
|
|
||||||
/** Free views ready for reuse. */
|
|
||||||
view_list_t m_free;
|
|
||||||
|
|
||||||
/** Active and closed views, the closed views will have the
|
/** @return the number of active views */
|
||||||
creator trx id set to TRX_ID_MAX */
|
size_t size() const;
|
||||||
view_list_t m_views;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* read0read_h */
|
#endif /* read0read_h */
|
||||||
|
|||||||
@@ -198,19 +198,13 @@ public:
|
|||||||
{
|
{
|
||||||
ut_ad(m_creator_trx_id != TRX_ID_MAX);
|
ut_ad(m_creator_trx_id != TRX_ID_MAX);
|
||||||
m_creator_trx_id = TRX_ID_MAX;
|
m_creator_trx_id = TRX_ID_MAX;
|
||||||
|
set_open(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
bool is_open() const { return(m_open); }
|
||||||
@return true if the view is closed */
|
void set_open(bool open) { m_open= open; }
|
||||||
bool is_closed() const
|
bool is_registered() const { return(m_registered); }
|
||||||
{
|
void set_registered(bool registered) { m_registered= registered; }
|
||||||
return(m_closed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_closed(bool closed)
|
|
||||||
{
|
|
||||||
m_closed= closed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Write the limits to the file.
|
Write the limits to the file.
|
||||||
@@ -325,14 +319,15 @@ private:
|
|||||||
they can be removed in purge if not needed by other views */
|
they can be removed in purge if not needed by other views */
|
||||||
trx_id_t m_low_limit_no;
|
trx_id_t m_low_limit_no;
|
||||||
|
|
||||||
/** AC-NL-RO transaction view that has been "closed". */
|
/** true if view is open. */
|
||||||
bool m_closed;
|
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[CACHE_LINE_SIZE];
|
||||||
byte pad1[64 - sizeof(node_t)];
|
UT_LIST_NODE_T(ReadView) m_view_list;
|
||||||
node_t m_view_list;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -883,17 +883,6 @@ public:
|
|||||||
|
|
||||||
MY_ALIGNED(CACHE_LINE_SIZE) rw_trx_hash_t rw_trx_hash;
|
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.
|
Constructor.
|
||||||
|
|||||||
@@ -45,13 +45,11 @@ Created 3/26/1996 Heikki Tuuri
|
|||||||
#include "ut0vec.h"
|
#include "ut0vec.h"
|
||||||
#include "fts0fts.h"
|
#include "fts0fts.h"
|
||||||
#include "srv0srv.h"
|
#include "srv0srv.h"
|
||||||
|
#include "read0types.h"
|
||||||
|
|
||||||
// Forward declaration
|
// Forward declaration
|
||||||
struct mtr_t;
|
struct mtr_t;
|
||||||
|
|
||||||
// Forward declaration
|
|
||||||
class ReadView;
|
|
||||||
|
|
||||||
// Forward declaration
|
// Forward declaration
|
||||||
class FlushObserver;
|
class FlushObserver;
|
||||||
|
|
||||||
@@ -290,23 +288,6 @@ void
|
|||||||
trx_mark_sql_stat_end(
|
trx_mark_sql_stat_end(
|
||||||
/*==================*/
|
/*==================*/
|
||||||
trx_t* trx); /*!< in: trx handle */
|
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. */
|
Prepares a transaction for commit/rollback. */
|
||||||
void
|
void
|
||||||
@@ -571,7 +552,7 @@ Check transaction state */
|
|||||||
ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED) \
|
ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED) \
|
||||||
|| trx_state_eq((t), TRX_STATE_FORCED_ROLLBACK)); \
|
|| trx_state_eq((t), TRX_STATE_FORCED_ROLLBACK)); \
|
||||||
ut_ad(!trx->has_logged()); \
|
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((t)->lock.wait_thr == NULL); \
|
||||||
ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0); \
|
ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0); \
|
||||||
ut_ad((t)->dict_operation == TRX_DICT_OP_NONE); \
|
ut_ad((t)->dict_operation == TRX_DICT_OP_NONE); \
|
||||||
@@ -1000,7 +981,7 @@ public:
|
|||||||
|
|
||||||
trx_state_t state;
|
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 */
|
transaction, or NULL if not yet set */
|
||||||
|
|
||||||
UT_LIST_NODE_T(trx_t)
|
UT_LIST_NODE_T(trx_t)
|
||||||
|
|||||||
@@ -211,17 +211,6 @@ ok:
|
|||||||
trx->dict_operation = op;
|
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
|
@param[in] trx Transaction to check
|
||||||
@return true if the transaction is a high priority transaction.*/
|
@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
|
operate on same temp-table and so read of temp-table is
|
||||||
always consistent read. */
|
always consistent read. */
|
||||||
if (srv_read_only_mode || dict_table_is_temporary(index->table)) {
|
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);
|
return(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5719,15 +5720,13 @@ lock_trx_print_wait_and_mvcc_state(
|
|||||||
|
|
||||||
trx_print_latched(file, trx, 600);
|
trx_print_latched(file, trx, 600);
|
||||||
|
|
||||||
/* Note: this read_view access is data race. Further
|
/* Note: read_view->is_active() check is race condition. But it
|
||||||
read_view->is_active() check is race condition. But it should
|
should "kind of work" because read_view is freed only at shutdown.
|
||||||
"kind of work" because read_view is freed only at shutdown.
|
|
||||||
Worst thing that may happen is that it'll get transferred to
|
Worst thing that may happen is that it'll get transferred to
|
||||||
another thread and print wrong values. */
|
another thread and print wrong values. */
|
||||||
const ReadView* read_view = trx->read_view;
|
|
||||||
|
|
||||||
if (read_view != NULL && !read_view->is_closed()) {
|
if (trx->read_view.is_open()) {
|
||||||
read_view->print_limits(file);
|
trx->read_view.print_limits(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
|
if (trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
|
||||||
|
|||||||
@@ -182,8 +182,9 @@ struct ViewCheck {
|
|||||||
|
|
||||||
void operator()(const ReadView* view)
|
void operator()(const ReadView* view)
|
||||||
{
|
{
|
||||||
|
ut_ad(view->is_registered());
|
||||||
ut_a(m_prev_view == NULL
|
ut_a(m_prev_view == NULL
|
||||||
|| view->is_closed()
|
|| !view->is_open()
|
||||||
|| view->le(m_prev_view));
|
|| view->le(m_prev_view));
|
||||||
|
|
||||||
m_prev_view = view;
|
m_prev_view = view;
|
||||||
@@ -325,7 +326,9 @@ ReadView::ReadView()
|
|||||||
m_up_limit_id(),
|
m_up_limit_id(),
|
||||||
m_creator_trx_id(),
|
m_creator_trx_id(),
|
||||||
m_ids(),
|
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)));
|
ut_d(::memset(&m_view_list, 0x0, sizeof(m_view_list)));
|
||||||
}
|
}
|
||||||
@@ -337,34 +340,6 @@ ReadView::~ReadView()
|
|||||||
// Do nothing
|
// 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 */
|
Copy the transaction ids from the source vector */
|
||||||
|
|
||||||
@@ -488,75 +463,88 @@ ReadView::complete()
|
|||||||
m_up_limit_id = !m_ids.empty() ? m_ids.front() : m_low_limit_id;
|
m_up_limit_id = !m_ids.empty() ? m_ids.front() : m_low_limit_id;
|
||||||
|
|
||||||
ut_ad(m_up_limit_id <= m_low_limit_id);
|
ut_ad(m_up_limit_id <= m_low_limit_id);
|
||||||
|
set_open(true);
|
||||||
m_closed = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Allocate and create a view.
|
Create a view.
|
||||||
@param view view owned by this class created for the
|
|
||||||
caller. Must be freed by calling view_close()
|
Assigns a read view for a consistent read query. All the consistent reads
|
||||||
@param trx transaction instance of caller */
|
within the same transaction will get the same read view, which is created
|
||||||
void
|
when this function is first called for a new started transaction.
|
||||||
MVCC::view_open(ReadView*& view, trx_t* trx)
|
|
||||||
|
@param trx transaction instance of caller
|
||||||
|
*/
|
||||||
|
|
||||||
|
void MVCC::view_open(trx_t* trx)
|
||||||
{
|
{
|
||||||
ut_ad(!srv_read_only_mode);
|
if (srv_read_only_mode)
|
||||||
|
{
|
||||||
/** If no new RW transaction has been started since the last view
|
ut_ad(!trx->read_view.is_open());
|
||||||
was created then reuse the the existing view. */
|
|
||||||
if (view != NULL) {
|
|
||||||
|
|
||||||
uintptr_t p = reinterpret_cast<uintptr_t>(view);
|
|
||||||
|
|
||||||
view = reinterpret_cast<ReadView*>(p & ~1);
|
|
||||||
|
|
||||||
ut_ad(view->m_closed);
|
|
||||||
|
|
||||||
/* NOTE: This can be optimised further, for now we only
|
|
||||||
resuse the view iff there are no active RW transactions.
|
|
||||||
|
|
||||||
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. */
|
|
||||||
|
|
||||||
if (trx_is_autocommit_non_locking(trx) && view->empty()) {
|
|
||||||
|
|
||||||
view->m_closed = false;
|
|
||||||
|
|
||||||
if (view->m_low_limit_id == trx_sys.get_max_trx_id()) {
|
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
view->m_closed = true;
|
|
||||||
}
|
}
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
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_open(true) 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
|
||||||
|
InnoDB monitor thread (see lock_trx_print_wait_and_mvcc_state()).
|
||||||
|
|
||||||
|
What bad things can happen because we allow this race?
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Second, scary things start when there's a read-write transaction starting
|
||||||
|
concurrently.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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);
|
mutex_enter(&trx_sys.mutex);
|
||||||
|
trx->read_view.prepare(trx->id);
|
||||||
UT_LIST_REMOVE(m_views, view);
|
trx->read_view.complete();
|
||||||
|
if (trx->read_view.is_registered())
|
||||||
} else {
|
UT_LIST_REMOVE(m_views, &trx->read_view);
|
||||||
mutex_enter(&trx_sys.mutex);
|
else
|
||||||
if ((view = UT_LIST_GET_FIRST(m_free))) {
|
trx->read_view.set_registered(true);
|
||||||
UT_LIST_REMOVE(m_free, view);
|
UT_LIST_ADD_FIRST(m_views, &trx->read_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());
|
ut_ad(validate());
|
||||||
}
|
|
||||||
|
|
||||||
mutex_exit(&trx_sys.mutex);
|
mutex_exit(&trx_sys.mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,7 +613,7 @@ MVCC::clone_oldest_view(ReadView* view)
|
|||||||
oldest_view != NULL;
|
oldest_view != NULL;
|
||||||
oldest_view = UT_LIST_GET_PREV(m_view_list, oldest_view))
|
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);
|
view->copy_prepare(*oldest_view);
|
||||||
mutex_exit(&trx_sys.mutex);
|
mutex_exit(&trx_sys.mutex);
|
||||||
@@ -642,18 +630,18 @@ MVCC::clone_oldest_view(ReadView* view)
|
|||||||
/**
|
/**
|
||||||
@return the number of active views */
|
@return the number of active views */
|
||||||
|
|
||||||
ulint
|
size_t
|
||||||
MVCC::size() const
|
MVCC::size() const
|
||||||
{
|
{
|
||||||
mutex_enter(&trx_sys.mutex);
|
mutex_enter(&trx_sys.mutex);
|
||||||
|
|
||||||
ulint size = 0;
|
size_t size = 0;
|
||||||
|
|
||||||
for (const ReadView* view = UT_LIST_GET_FIRST(m_views);
|
for (const ReadView* view = UT_LIST_GET_FIRST(m_views);
|
||||||
view != NULL;
|
view != NULL;
|
||||||
view = UT_LIST_GET_NEXT(m_view_list, view)) {
|
view = UT_LIST_GET_NEXT(m_view_list, view)) {
|
||||||
|
|
||||||
if (!view->is_closed()) {
|
if (view->is_open()) {
|
||||||
++size;
|
++size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -662,18 +650,3 @@ MVCC::size() const
|
|||||||
|
|
||||||
return(size);
|
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
|
ONLINE_INDEX_COMPLETE state between the time
|
||||||
the DML thread has updated the clustered index
|
the DML thread has updated the clustered index
|
||||||
but has not yet accessed secondary 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);
|
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_trx_id, old_table->name)) {
|
||||||
rec_t* old_vers;
|
rec_t* old_vers;
|
||||||
|
|
||||||
row_vers_build_for_consistent_read(
|
row_vers_build_for_consistent_read(
|
||||||
rec, &mtr, clust_index, &offsets,
|
rec, &mtr, clust_index, &offsets,
|
||||||
trx->read_view, &row_heap,
|
&trx->read_view, &row_heap,
|
||||||
row_heap, &old_vers, NULL);
|
row_heap, &old_vers, NULL);
|
||||||
|
|
||||||
if (!old_vers) {
|
if (!old_vers) {
|
||||||
@@ -4526,8 +4526,8 @@ row_merge_is_index_usable(
|
|||||||
return(!dict_index_is_corrupted(index)
|
return(!dict_index_is_corrupted(index)
|
||||||
&& (dict_table_is_temporary(index->table)
|
&& (dict_table_is_temporary(index->table)
|
||||||
|| index->trx_id == 0
|
|| index->trx_id == 0
|
||||||
|| !MVCC::is_view_active(trx->read_view)
|
|| !trx->read_view.is_open()
|
||||||
|| trx->read_view->changes_visible(
|
|| trx->read_view.changes_visible(
|
||||||
index->trx_id,
|
index->trx_id,
|
||||||
index->table->name)));
|
index->table->name)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2268,15 +2268,11 @@ row_sel_step(
|
|||||||
plan_reset_cursor(sel_node_get_nth_plan(node, 0));
|
plan_reset_cursor(sel_node_get_nth_plan(node, 0));
|
||||||
|
|
||||||
if (node->consistent_read) {
|
if (node->consistent_read) {
|
||||||
|
trx_t *trx = thr_get_trx(thr);
|
||||||
/* Assign a read view for the query */
|
/* Assign a read view for the query */
|
||||||
trx_assign_read_view(thr_get_trx(thr));
|
trx_sys.mvcc.view_open(trx);
|
||||||
|
node->read_view = trx->read_view.is_open() ?
|
||||||
if (thr_get_trx(thr)->read_view != NULL) {
|
&trx->read_view : NULL;
|
||||||
node->read_view = thr_get_trx(thr)->read_view;
|
|
||||||
} else {
|
|
||||||
node->read_view = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
sym_node_t* table_node;
|
sym_node_t* table_node;
|
||||||
lock_mode i_lock_mode;
|
lock_mode i_lock_mode;
|
||||||
@@ -3440,12 +3436,12 @@ row_sel_get_clust_rec_for_mysql(
|
|||||||
if (trx->isolation_level > TRX_ISO_READ_UNCOMMITTED
|
if (trx->isolation_level > TRX_ISO_READ_UNCOMMITTED
|
||||||
&& !lock_clust_rec_cons_read_sees(
|
&& !lock_clust_rec_cons_read_sees(
|
||||||
clust_rec, clust_index, *offsets,
|
clust_rec, clust_index, *offsets,
|
||||||
trx_get_read_view(trx))) {
|
&trx->read_view)) {
|
||||||
|
|
||||||
/* The following call returns 'offsets' associated with
|
/* The following call returns 'offsets' associated with
|
||||||
'old_vers' */
|
'old_vers' */
|
||||||
err = row_sel_build_prev_vers_for_mysql(
|
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,
|
clust_rec, offsets, offset_heap, &old_vers,
|
||||||
vrow, mtr);
|
vrow, mtr);
|
||||||
|
|
||||||
@@ -3864,8 +3860,8 @@ exhausted:
|
|||||||
*offsets = rec_get_offsets(rec, index, *offsets, true,
|
*offsets = rec_get_offsets(rec, index, *offsets, true,
|
||||||
ULINT_UNDEFINED, heap);
|
ULINT_UNDEFINED, heap);
|
||||||
|
|
||||||
if (!lock_clust_rec_cons_read_sees(
|
if (!lock_clust_rec_cons_read_sees(rec, index, *offsets,
|
||||||
rec, index, *offsets, trx_get_read_view(trx))) {
|
&trx->read_view)) {
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4269,7 +4265,7 @@ row_search_mvcc(
|
|||||||
|
|
||||||
if (prebuilt->select_lock_type == LOCK_NONE
|
if (prebuilt->select_lock_type == LOCK_NONE
|
||||||
&& trx->isolation_level > TRX_ISO_READ_UNCOMMITTED
|
&& 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,
|
/* This is a SELECT query done as a consistent read,
|
||||||
and the read view has already been allocated:
|
and the read view has already been allocated:
|
||||||
@@ -4367,7 +4363,7 @@ row_search_mvcc(
|
|||||||
|
|
||||||
ut_ad(prebuilt->sql_stat_start
|
ut_ad(prebuilt->sql_stat_start
|
||||||
|| prebuilt->select_lock_type != LOCK_NONE
|
|| prebuilt->select_lock_type != LOCK_NONE
|
||||||
|| MVCC::is_view_active(trx->read_view)
|
|| trx->read_view.is_open()
|
||||||
|| prebuilt->table->no_rollback()
|
|| prebuilt->table->no_rollback()
|
||||||
|| srv_read_only_mode);
|
|| srv_read_only_mode);
|
||||||
|
|
||||||
@@ -4414,7 +4410,7 @@ row_search_mvcc(
|
|||||||
} else if (!prebuilt->sql_stat_start) {
|
} else if (!prebuilt->sql_stat_start) {
|
||||||
/* No need to set an intention lock or assign a read view */
|
/* 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
|
&& !srv_read_only_mode
|
||||||
&& prebuilt->select_lock_type == LOCK_NONE) {
|
&& prebuilt->select_lock_type == LOCK_NONE) {
|
||||||
|
|
||||||
@@ -4430,9 +4426,7 @@ row_search_mvcc(
|
|||||||
/* Assign a read view for the query */
|
/* Assign a read view for the query */
|
||||||
trx_start_if_not_started(trx, false);
|
trx_start_if_not_started(trx, false);
|
||||||
|
|
||||||
if (!srv_read_only_mode) {
|
trx_sys.mvcc.view_open(trx);
|
||||||
trx_assign_read_view(trx);
|
|
||||||
}
|
|
||||||
|
|
||||||
prebuilt->sql_stat_start = FALSE;
|
prebuilt->sql_stat_start = FALSE;
|
||||||
} else {
|
} else {
|
||||||
@@ -5036,14 +5030,13 @@ no_gap_lock:
|
|||||||
|
|
||||||
if (srv_force_recovery < 5
|
if (srv_force_recovery < 5
|
||||||
&& !lock_clust_rec_cons_read_sees(
|
&& !lock_clust_rec_cons_read_sees(
|
||||||
rec, index, offsets,
|
rec, index, offsets, &trx->read_view)) {
|
||||||
trx_get_read_view(trx))) {
|
|
||||||
|
|
||||||
rec_t* old_vers;
|
rec_t* old_vers;
|
||||||
/* The following call returns 'offsets'
|
/* The following call returns 'offsets'
|
||||||
associated with 'old_vers' */
|
associated with 'old_vers' */
|
||||||
err = row_sel_build_prev_vers_for_mysql(
|
err = row_sel_build_prev_vers_for_mysql(
|
||||||
trx->read_view, clust_index,
|
&trx->read_view, clust_index,
|
||||||
prebuilt, rec, &offsets, &heap,
|
prebuilt, rec, &offsets, &heap,
|
||||||
&old_vers, need_vrow ? &vrow : NULL,
|
&old_vers, need_vrow ? &vrow : NULL,
|
||||||
&mtr);
|
&mtr);
|
||||||
@@ -5073,7 +5066,7 @@ no_gap_lock:
|
|||||||
|
|
||||||
if (!srv_read_only_mode
|
if (!srv_read_only_mode
|
||||||
&& !lock_sec_rec_cons_read_sees(
|
&& !lock_sec_rec_cons_read_sees(
|
||||||
rec, index, trx->read_view)) {
|
rec, index, &trx->read_view)) {
|
||||||
/* We should look at the clustered index.
|
/* We should look at the clustered index.
|
||||||
However, as this is a non-locking read,
|
However, as this is a non-locking read,
|
||||||
we can skip the clustered index lookup if
|
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
|
const bool ret = lock_table_get_n_locks(table) == 0
|
||||||
&& ((trx->id != 0 && trx->id >= table->query_cache_inv_id)
|
&& ((trx->id != 0 && trx->id >= table->query_cache_inv_id)
|
||||||
|| !MVCC::is_view_active(trx->read_view)
|
|| !trx->read_view.is_open()
|
||||||
|| trx->read_view->low_limit_id()
|
|| trx->read_view.low_limit_id()
|
||||||
>= table->query_cache_inv_id);
|
>= table->query_cache_inv_id);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/* If the isolation level is high, assign a read view for the
|
/* If the isolation level is high, assign a read view for the
|
||||||
transaction if it does not yet have one */
|
transaction if it does not yet have one */
|
||||||
|
|
||||||
if (trx->isolation_level >= TRX_ISO_REPEATABLE_READ
|
if (trx->isolation_level >= TRX_ISO_REPEATABLE_READ) {
|
||||||
&& !srv_read_only_mode
|
|
||||||
&& !MVCC::is_view_active(trx->read_view)) {
|
|
||||||
|
|
||||||
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(serialisation_list, &trx_t::no_list);
|
||||||
UT_LIST_INIT(mysql_trx_list, &trx_t::mysql_trx_list);
|
UT_LIST_INIT(mysql_trx_list, &trx_t::mysql_trx_list);
|
||||||
|
|
||||||
mvcc.create(1024);
|
|
||||||
|
|
||||||
rw_trx_hash.init();
|
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(mysql_trx_list) == 0);
|
||||||
ut_a(UT_LIST_GET_LEN(serialisation_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;
|
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;
|
trx->lock.rec_cached = 0;
|
||||||
|
|
||||||
@@ -246,6 +246,7 @@ struct TrxFactory {
|
|||||||
new(&trx->lock.table_locks) lock_pool_t();
|
new(&trx->lock.table_locks) lock_pool_t();
|
||||||
|
|
||||||
new(&trx->hit_list) hit_list_t();
|
new(&trx->hit_list) hit_list_t();
|
||||||
|
new(&trx->read_view) ReadView();
|
||||||
|
|
||||||
trx->rw_trx_hash_pins = 0;
|
trx->rw_trx_hash_pins = 0;
|
||||||
trx_init(trx);
|
trx_init(trx);
|
||||||
@@ -301,7 +302,7 @@ struct TrxFactory {
|
|||||||
|
|
||||||
trx->mod_tables.~trx_mod_tables_t();
|
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()) {
|
if (!trx->lock.rec_pool.empty()) {
|
||||||
|
|
||||||
@@ -508,7 +509,12 @@ trx_free(trx_t*& trx)
|
|||||||
|
|
||||||
trx->mod_tables.clear();
|
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
|
/* trx locking state should have been reset before returning trx
|
||||||
to pool */
|
to pool */
|
||||||
@@ -677,7 +683,7 @@ trx_disconnect_from_mysql(
|
|||||||
|
|
||||||
UT_LIST_REMOVE(trx_sys.mysql_trx_list, trx);
|
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);
|
trx_sys.mvcc.view_close(trx->read_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1536,17 +1542,11 @@ trx_erase_lists(
|
|||||||
{
|
{
|
||||||
ut_ad(trx->id > 0);
|
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) {
|
if (serialised) {
|
||||||
|
mutex_enter(&trx_sys.mutex);
|
||||||
UT_LIST_REMOVE(trx_sys.serialisation_list, trx);
|
UT_LIST_REMOVE(trx_sys.serialisation_list, trx);
|
||||||
|
} else {
|
||||||
|
mutex_enter(&trx_sys.mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
trx_ids_t::iterator it = std::lower_bound(
|
trx_ids_t::iterator it = std::lower_bound(
|
||||||
@@ -1574,6 +1574,8 @@ trx_commit_in_memory(
|
|||||||
written */
|
written */
|
||||||
{
|
{
|
||||||
trx->must_flush_log_later = false;
|
trx->must_flush_log_later = false;
|
||||||
|
trx->read_view.set_open(false);
|
||||||
|
|
||||||
|
|
||||||
if (trx_is_autocommit_non_locking(trx)) {
|
if (trx_is_autocommit_non_locking(trx)) {
|
||||||
ut_ad(trx->id == 0);
|
ut_ad(trx->id == 0);
|
||||||
@@ -1597,10 +1599,6 @@ trx_commit_in_memory(
|
|||||||
|
|
||||||
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
|
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);
|
MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT);
|
||||||
|
|
||||||
/* AC-NL-RO transactions can't be rolled back asynchronously. */
|
/* 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) {
|
if (trx->read_only || trx->rsegs.m_redo.rseg == NULL) {
|
||||||
MONITOR_INC(MONITOR_TRX_RO_COMMIT);
|
MONITOR_INC(MONITOR_TRX_RO_COMMIT);
|
||||||
if (trx->read_view != NULL) {
|
|
||||||
trx->read_view->set_closed(true);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
trx_update_mod_tables_timestamp(trx);
|
trx_update_mod_tables_timestamp(trx);
|
||||||
MONITOR_INC(MONITOR_TRX_RW_COMMIT);
|
MONITOR_INC(MONITOR_TRX_RW_COMMIT);
|
||||||
@@ -1875,30 +1870,6 @@ trx_commit(
|
|||||||
trx_commit_low(trx, mtr);
|
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. */
|
Prepares a transaction for commit/rollback. */
|
||||||
void
|
void
|
||||||
@@ -2754,8 +2725,8 @@ trx_set_rw_mode(
|
|||||||
trx_sys.rw_trx_ids.push_back(trx->id);
|
trx_sys.rw_trx_ids.push_back(trx->id);
|
||||||
|
|
||||||
/* So that we can see our own changes. */
|
/* So that we can see our own changes. */
|
||||||
if (MVCC::is_view_active(trx->read_view)) {
|
if (trx->read_view.is_open()) {
|
||||||
trx->read_view->creator_trx_id(trx->id);
|
trx->read_view.creator_trx_id(trx->id);
|
||||||
}
|
}
|
||||||
mutex_exit(&trx_sys.mutex);
|
mutex_exit(&trx_sys.mutex);
|
||||||
trx_sys.rw_trx_hash.insert(trx);
|
trx_sys.rw_trx_hash.insert(trx);
|
||||||
|
|||||||
Reference in New Issue
Block a user