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