@ -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 )
{
ut_ad ( ! srv_read_only_mode ) ;
/** If no new RW transaction has been started since the last view
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 ;
} else {
view - > m_closed = true ;
}
}
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 ) ;
/**
Create a view .
ut_ad ( ! view - > is_closed ( ) ) ;
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 .
ut_ad ( validate ( ) ) ;
}
@ param trx transaction instance of caller
*/
mutex_exit ( & trx_sys . mutex ) ;
void MVCC : : view_open ( trx_t * trx )
{
if ( srv_read_only_mode )
{
ut_ad ( ! trx - > read_view . is_open ( ) ) ;
return ;
}
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 ) ;
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 */
ulin t
size_ t
MVCC : : size ( ) const
{
mutex_enter ( & trx_sys . mutex ) ;
ulin t 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 ;
}