Commit 02270b44 authored by Sergey Vojtovich's avatar Sergey Vojtovich

MDEV-14756 - Remove trx_sys_t::rw_trx_list

Let lock_validate_table_locks(), lock_rec_other_trx_holds_expl(),
lock_table_locks_lookup(), trx_recover_for_mysql(), trx_get_trx_by_xid(),
trx_roll_must_shutdown(), fetch_data_into_cache() iterate rw_trx_hash
instead of rw_trx_list.
parent d8c0caad
...@@ -757,7 +757,20 @@ class rw_trx_hash_t ...@@ -757,7 +757,20 @@ class rw_trx_hash_t
@param argument opque argument passed to action @param argument opque argument passed to action
May return the same element multiple times if hash is under contention. May return the same element multiple times if hash is under contention.
Elements can be added or removed while this method is being executed. If caller doesn't like to see the same transaction multiple times, it has
to call iterate_no_dups() instead.
May return element with committed transaction. If caller doesn't like to
see committed transactions, it has to skip those under element mutex:
mutex_enter(&element->mutex);
if (trx_t trx= element->trx)
{
// trx is protected against commit in this branch
}
mutex_exit(&element->mutex);
May miss concurrently inserted transactions.
@return @return
@retval 0 iteration completed successfully @retval 0 iteration completed successfully
......
...@@ -269,7 +269,7 @@ int ...@@ -269,7 +269,7 @@ int
trx_recover_for_mysql( trx_recover_for_mysql(
/*==================*/ /*==================*/
XID* xid_list, /*!< in/out: prepared transactions */ XID* xid_list, /*!< in/out: prepared transactions */
ulint len); /*!< in: number of slots in xid_list */ uint len); /*!< in: number of slots in xid_list */
/*******************************************************************//** /*******************************************************************//**
This function is used to find one X/Open XA distributed transaction This function is used to find one X/Open XA distributed transaction
which is in the prepared state which is in the prepared state
......
...@@ -1533,61 +1533,6 @@ lock_sec_rec_some_has_impl( ...@@ -1533,61 +1533,6 @@ lock_sec_rec_some_has_impl(
return(trx); return(trx);
} }
#ifdef UNIV_DEBUG
/*********************************************************************//**
Checks if some transaction, other than given trx_id, has an explicit
lock on the given rec, in the given precise_mode.
FIXME: if the current transaction holds implicit lock from INSERT, a
subsequent locking read should not convert it to explicit. See also
MDEV-11215.
@return the transaction, whose id is not equal to trx_id, that has an
explicit lock on the given rec, in the given precise_mode or NULL.*/
static
trx_t*
lock_rec_other_trx_holds_expl(
/*==========================*/
ulint precise_mode, /*!< in: LOCK_S or LOCK_X
possibly ORed to LOCK_GAP or
LOCK_REC_NOT_GAP. */
trx_t* trx, /*!< in: trx holding implicit
lock on rec */
const rec_t* rec, /*!< in: user record */
const buf_block_t* block) /*!< in: buffer block
containing the record */
{
ut_ad(!page_rec_is_default_row(rec));
trx_t* holds = NULL;
ulint heap_no = page_rec_get_heap_no(rec);
lock_mutex_enter();
mutex_enter(&trx_sys->mutex);
for (trx_t* t = UT_LIST_GET_FIRST(trx_sys->rw_trx_list);
t != NULL;
t = UT_LIST_GET_NEXT(trx_list, t)) {
lock_t* expl_lock = lock_rec_has_expl(
precise_mode, block, heap_no, t);
if (expl_lock && expl_lock->trx != trx) {
/* An explicit lock is held by trx other than
the trx holding the implicit lock. */
holds = expl_lock->trx;
break;
}
}
mutex_exit(&trx_sys->mutex);
lock_mutex_exit();
return(holds);
}
#endif /* UNIV_DEBUG */
/*********************************************************************//** /*********************************************************************//**
Return approximate number or record locks (bits set in the bitmap) for Return approximate number or record locks (bits set in the bitmap) for
this transaction. Since delete-marked records may be removed, the this transaction. Since delete-marked records may be removed, the
...@@ -6464,45 +6409,6 @@ lock_rec_validate_page( ...@@ -6464,45 +6409,6 @@ lock_rec_validate_page(
return(TRUE); return(TRUE);
} }
/*********************************************************************//**
Validates the table locks.
@return TRUE if ok */
static
ibool
lock_validate_table_locks(
/*======================*/
const trx_ut_list_t* trx_list) /*!< in: trx list */
{
const trx_t* trx;
ut_ad(lock_mutex_own());
ut_ad(trx_sys_mutex_own());
ut_ad(trx_list == &trx_sys->rw_trx_list);
for (trx = UT_LIST_GET_FIRST(*trx_list);
trx != NULL;
trx = UT_LIST_GET_NEXT(trx_list, trx)) {
const lock_t* lock;
check_trx_state(trx);
for (lock = UT_LIST_GET_FIRST(trx->lock.trx_locks);
lock != NULL;
lock = UT_LIST_GET_NEXT(trx_locks, lock)) {
if (lock_get_type_low(lock) & LOCK_TABLE) {
lock_table_queue_validate(
lock->un_member.tab_lock.table);
}
}
}
return(TRUE);
}
/*********************************************************************//** /*********************************************************************//**
Validate record locks up to a limit. Validate record locks up to a limit.
@return lock at limit or NULL if no more locks in the hash bucket */ @return lock at limit or NULL if no more locks in the hash bucket */
...@@ -6590,6 +6496,29 @@ lock_rec_block_validate( ...@@ -6590,6 +6496,29 @@ lock_rec_block_validate(
} }
} }
static my_bool lock_validate_table_locks(rw_trx_hash_element_t *element,
void *arg)
{
ut_ad(lock_mutex_own());
ut_ad(trx_sys_mutex_own()); /* Do we really need this. */
mutex_enter(&element->mutex);
if (element->trx)
{
check_trx_state(element->trx);
for (const lock_t *lock= UT_LIST_GET_FIRST(element->trx->lock.trx_locks);
lock != NULL;
lock= UT_LIST_GET_NEXT(trx_locks, lock))
{
if (lock_get_type_low(lock) & LOCK_TABLE)
lock_table_queue_validate(lock->un_member.tab_lock.table);
}
}
mutex_exit(&element->mutex);
return 0;
}
/*********************************************************************//** /*********************************************************************//**
Validates the lock system. Validates the lock system.
@return TRUE if ok */ @return TRUE if ok */
...@@ -6609,7 +6538,9 @@ lock_validate() ...@@ -6609,7 +6538,9 @@ lock_validate()
lock_mutex_enter(); lock_mutex_enter();
mutex_enter(&trx_sys->mutex); mutex_enter(&trx_sys->mutex);
ut_a(lock_validate_table_locks(&trx_sys->rw_trx_list)); /* Validate table locks */
trx_sys->rw_trx_hash.iterate(reinterpret_cast<my_hash_walk_action>
(lock_validate_table_locks), 0);
/* Iterate over all the record locks and validate the locks. We /* Iterate over all the record locks and validate the locks. We
don't want to hog the lock_sys_t::mutex and the trx_sys_t::mutex. don't want to hog the lock_sys_t::mutex and the trx_sys_t::mutex.
...@@ -6838,6 +6769,70 @@ lock_rec_convert_impl_to_expl_for_trx( ...@@ -6838,6 +6769,70 @@ lock_rec_convert_impl_to_expl_for_trx(
DEBUG_SYNC_C("after_lock_rec_convert_impl_to_expl_for_trx"); DEBUG_SYNC_C("after_lock_rec_convert_impl_to_expl_for_trx");
} }
#ifdef UNIV_DEBUG
struct lock_rec_other_trx_holds_expl_arg
{
const ulint heap_no;
const buf_block_t * const block;
const trx_t *impl_trx;
};
static my_bool lock_rec_other_trx_holds_expl_callback(
rw_trx_hash_element_t *element,
lock_rec_other_trx_holds_expl_arg *arg)
{
mutex_enter(&element->mutex);
if (element->trx)
{
lock_t *expl_lock= lock_rec_has_expl(LOCK_S | LOCK_REC_NOT_GAP, arg->block,
arg->heap_no, element->trx);
/*
An explicit lock is held by trx other than the trx holding the implicit
lock.
*/
ut_ad(!expl_lock || expl_lock->trx == arg->impl_trx);
}
mutex_exit(&element->mutex);
return 0;
}
/**
Checks if some transaction, other than given trx_id, has an explicit
lock on the given rec.
FIXME: if the current transaction holds implicit lock from INSERT, a
subsequent locking read should not convert it to explicit. See also
MDEV-11215.
@param caller_trx trx of current thread
@param[in] trx trx holding implicit lock on rec
@param[in] rec user record
@param[in] block buffer block containing the record
*/
static void lock_rec_other_trx_holds_expl(trx_t *caller_trx, trx_t *trx,
const rec_t *rec,
const buf_block_t *block)
{
if (trx)
{
ut_ad(!page_rec_is_default_row(rec));
lock_mutex_enter();
lock_rec_other_trx_holds_expl_arg arg= { page_rec_get_heap_no(rec), block,
trx };
trx_sys->rw_trx_hash.iterate(caller_trx,
reinterpret_cast<my_hash_walk_action>
(lock_rec_other_trx_holds_expl_callback),
&arg);
lock_mutex_exit();
}
}
#endif /* UNIV_DEBUG */
/*********************************************************************//** /*********************************************************************//**
If a transaction has an implicit x-lock on a record, but no explicit x-lock If a transaction has an implicit x-lock on a record, but no explicit x-lock
set on the record, sets one for it. */ set on the record, sets one for it. */
...@@ -6872,8 +6867,8 @@ lock_rec_convert_impl_to_expl( ...@@ -6872,8 +6867,8 @@ lock_rec_convert_impl_to_expl(
trx = lock_sec_rec_some_has_impl(caller_trx, rec, index, trx = lock_sec_rec_some_has_impl(caller_trx, rec, index,
offsets); offsets);
ut_ad(!trx || !lock_rec_other_trx_holds_expl( ut_d(lock_rec_other_trx_holds_expl(caller_trx, trx, rec,
LOCK_S | LOCK_REC_NOT_GAP, trx, rec, block)); block));
} }
if (trx != 0) { if (trx != 0) {
...@@ -7771,52 +7766,38 @@ lock_table_get_n_locks( ...@@ -7771,52 +7766,38 @@ lock_table_get_n_locks(
} }
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
/*******************************************************************//** /**
Do an exhaustive check for any locks (table or rec) against the table. Do an exhaustive check for any locks (table or rec) against the table.
@return lock if found */
static
const lock_t*
lock_table_locks_lookup(
/*====================*/
const dict_table_t* table, /*!< in: check if there are
any locks held on records in
this table or on the table
itself */
const trx_ut_list_t* trx_list) /*!< in: trx list to check */
{
trx_t* trx;
ut_a(table != NULL);
ut_ad(lock_mutex_own());
ut_ad(trx_sys_mutex_own());
for (trx = UT_LIST_GET_FIRST(*trx_list);
trx != NULL;
trx = UT_LIST_GET_NEXT(trx_list, trx)) {
const lock_t* lock;
check_trx_state(trx); @param[in] table check if there are any locks held on records in this table
or on the table itself
*/
for (lock = UT_LIST_GET_FIRST(trx->lock.trx_locks); static my_bool lock_table_locks_lookup(rw_trx_hash_element_t *element,
const dict_table_t *table)
{
ut_ad(lock_mutex_own());
mutex_enter(&element->mutex);
if (element->trx)
{
check_trx_state(element->trx);
for (const lock_t *lock= UT_LIST_GET_FIRST(element->trx->lock.trx_locks);
lock != NULL; lock != NULL;
lock = UT_LIST_GET_NEXT(trx_locks, lock)) { lock= UT_LIST_GET_NEXT(trx_locks, lock))
{
ut_a(lock->trx == trx); ut_ad(lock->trx == element->trx);
if (lock_get_type_low(lock) == LOCK_REC)
if (lock_get_type_low(lock) == LOCK_REC) { {
ut_ad(!dict_index_is_online_ddl(lock->index) ut_ad(!dict_index_is_online_ddl(lock->index) ||
|| dict_index_is_clust(lock->index)); dict_index_is_clust(lock->index));
if (lock->index->table == table) { ut_ad(lock->index->table != table);
return(lock);
}
} else if (lock->un_member.tab_lock.table == table) {
return(lock);
} }
else
ut_ad(lock->un_member.tab_lock.table != table);
} }
} }
mutex_exit(&element->mutex);
return(NULL); return 0;
} }
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
...@@ -7832,17 +7813,17 @@ lock_table_has_locks( ...@@ -7832,17 +7813,17 @@ lock_table_has_locks(
{ {
ibool has_locks; ibool has_locks;
ut_ad(table != NULL);
lock_mutex_enter(); lock_mutex_enter();
has_locks = UT_LIST_GET_LEN(table->locks) > 0 || table->n_rec_locks > 0; has_locks = UT_LIST_GET_LEN(table->locks) > 0 || table->n_rec_locks > 0;
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
if (!has_locks) { if (!has_locks) {
mutex_enter(&trx_sys->mutex); trx_sys->rw_trx_hash.iterate(
reinterpret_cast<my_hash_walk_action>
ut_ad(!lock_table_locks_lookup(table, &trx_sys->rw_trx_list)); (lock_table_locks_lookup),
const_cast<dict_table_t*>(table));
mutex_exit(&trx_sys->mutex);
} }
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
......
...@@ -1245,102 +1245,90 @@ trx_i_s_cache_clear( ...@@ -1245,102 +1245,90 @@ trx_i_s_cache_clear(
ha_storage_empty(&cache->storage); ha_storage_empty(&cache->storage);
} }
/*******************************************************************//**
Fetches the data needed to fill the 3 INFORMATION SCHEMA tables into the
table cache buffer. Cache must be locked for write. */
static
void
fetch_data_into_cache_low(
/*======================*/
trx_i_s_cache_t* cache, /*!< in/out: cache */
bool read_write, /*!< in: only read-write
transactions */
trx_ut_list_t* trx_list) /*!< in: trx list */
{
const trx_t* trx;
bool rw_trx_list = trx_list == &trx_sys->rw_trx_list;
ut_ad(rw_trx_list || trx_list == &trx_sys->mysql_trx_list);
/* Iterate over the transaction list and add each one /**
to innodb_trx's cache. We also add all locks that are relevant Add transactions to innodb_trx's cache.
to each transaction into innodb_locks' and innodb_lock_waits'
caches. */
for (trx = UT_LIST_GET_FIRST(*trx_list); We also add all locks that are relevant to each transaction into
trx != NULL; innodb_locks' and innodb_lock_waits' caches.
trx = */
(rw_trx_list
? UT_LIST_GET_NEXT(trx_list, trx)
: UT_LIST_GET_NEXT(mysql_trx_list, trx))) {
i_s_trx_row_t* trx_row;
i_s_locks_row_t* requested_lock_row;
/* Note: Read only transactions that modify temporary
tables an have a transaction ID */
if (!trx_is_started(trx)
|| (!rw_trx_list && trx->id != 0 && !trx->read_only)) {
continue; static void fetch_data_into_cache_low(trx_i_s_cache_t *cache, const trx_t *trx)
} {
i_s_locks_row_t *requested_lock_row;
assert_trx_nonlocking_or_in_list(trx); assert_trx_nonlocking_or_in_list(trx);
ut_ad(trx->in_rw_trx_list == rw_trx_list); if (add_trx_relevant_locks_to_cache(cache, trx, &requested_lock_row))
{
if (!add_trx_relevant_locks_to_cache(cache, trx, if (i_s_trx_row_t *trx_row= reinterpret_cast<i_s_trx_row_t*>(
&requested_lock_row)) { table_cache_create_empty_row(&cache->innodb_trx, cache)))
{
cache->is_truncated = TRUE; if (fill_trx_row(trx_row, trx, requested_lock_row, cache))
return; return;
--cache->innodb_trx.rows_used;
}
} }
trx_row = reinterpret_cast<i_s_trx_row_t*>(
table_cache_create_empty_row(
&cache->innodb_trx, cache));
/* memory could not be allocated */ /* memory could not be allocated */
if (trx_row == NULL) { cache->is_truncated= TRUE;
}
cache->is_truncated = TRUE;
return;
}
if (!fill_trx_row(trx_row, trx, requested_lock_row, cache)) {
/* memory could not be allocated */ static my_bool fetch_data_into_cache_callback(
--cache->innodb_trx.rows_used; rw_trx_hash_element_t *element, trx_i_s_cache_t *cache)
cache->is_truncated = TRUE; {
return; mutex_enter(&element->mutex);
} if (element->trx)
{
assert_trx_in_rw_list(element->trx);
fetch_data_into_cache_low(cache, element->trx);
} }
mutex_exit(&element->mutex);
return cache->is_truncated;
} }
/*******************************************************************//**
Fetches the data needed to fill the 3 INFORMATION SCHEMA tables into the /**
table cache buffer. Cache must be locked for write. */ Fetches the data needed to fill the 3 INFORMATION SCHEMA tables into the
static table cache buffer. Cache must be locked for write.
void */
fetch_data_into_cache(
/*==================*/ static void fetch_data_into_cache(trx_i_s_cache_t *cache)
trx_i_s_cache_t* cache) /*!< in/out: cache */
{ {
ut_ad(lock_mutex_own()); ut_ad(lock_mutex_own());
ut_ad(trx_sys_mutex_own());
trx_i_s_cache_clear(cache); trx_i_s_cache_clear(cache);
/* Capture the state of the read-write transactions. This includes /*
internal transactions too. They are not on mysql_trx_list */ Capture the state of the read-write transactions. This includes
fetch_data_into_cache_low(cache, true, &trx_sys->rw_trx_list); internal transactions too. They are not on mysql_trx_list
*/
trx_sys->rw_trx_hash.iterate_no_dups(reinterpret_cast<my_hash_walk_action>
(fetch_data_into_cache_callback),
cache);
/* Capture the state of the read-only active transactions */ /* Capture the state of the read-only active transactions */
fetch_data_into_cache_low(cache, false, &trx_sys->mysql_trx_list); trx_sys_mutex_enter();
for (const trx_t *trx= UT_LIST_GET_FIRST(trx_sys->mysql_trx_list);
cache->is_truncated = FALSE; trx != NULL;
trx= UT_LIST_GET_NEXT(mysql_trx_list, trx))
{
/*
Skip transactions that have trx->id > 0: they were added in previous
iteration. Although we may miss concurrently started transactions.
*/
if (trx_is_started(trx) && trx->id == 0)
{
fetch_data_into_cache_low(cache, trx);
if (cache->is_truncated)
break;
}
}
trx_sys_mutex_exit();
cache->is_truncated= FALSE;
} }
/*******************************************************************//** /*******************************************************************//**
Update the transactions cache if it has not been read for some time. Update the transactions cache if it has not been read for some time.
Called from handler/i_s.cc. Called from handler/i_s.cc.
...@@ -1358,13 +1346,7 @@ trx_i_s_possibly_fetch_data_into_cache( ...@@ -1358,13 +1346,7 @@ trx_i_s_possibly_fetch_data_into_cache(
/* We need to read trx_sys and record/table lock queues */ /* We need to read trx_sys and record/table lock queues */
lock_mutex_enter(); lock_mutex_enter();
trx_sys_mutex_enter();
fetch_data_into_cache(cache); fetch_data_into_cache(cache);
trx_sys_mutex_exit();
lock_mutex_exit(); lock_mutex_exit();
/* update cache last read time */ /* update cache last read time */
......
...@@ -791,6 +791,33 @@ trx_rollback_resurrected( ...@@ -791,6 +791,33 @@ trx_rollback_resurrected(
goto func_exit; goto func_exit;
} }
struct trx_roll_count_callback_arg
{
uint32_t n_trx;
uint64_t n_rows;
trx_roll_count_callback_arg(): n_trx(0), n_rows(0) {}
};
static my_bool trx_roll_count_callback(rw_trx_hash_element_t *element,
trx_roll_count_callback_arg *arg)
{
mutex_enter(&element->mutex);
if (trx_t *trx= element->trx)
{
assert_trx_in_rw_list(trx);
if (trx->is_recovered && trx_state_eq(trx, TRX_STATE_ACTIVE))
{
arg->n_trx++;
arg->n_rows+= trx->undo_no;
}
}
mutex_exit(&element->mutex);
return 0;
}
/** Report progress when rolling back a row of a recovered transaction. /** Report progress when rolling back a row of a recovered transaction.
@return whether the rollback should be aborted due to pending shutdown */ @return whether the rollback should be aborted due to pending shutdown */
bool bool
...@@ -808,31 +835,24 @@ trx_roll_must_shutdown() ...@@ -808,31 +835,24 @@ trx_roll_must_shutdown()
} }
ib_time_t time = ut_time(); ib_time_t time = ut_time();
mutex_enter(&trx_sys->mutex);
mutex_enter(&recv_sys->mutex); mutex_enter(&recv_sys->mutex);
if (recv_sys->report(time)) { if (recv_sys->report(time)) {
ulint n_trx = 0; trx_roll_count_callback_arg arg;
ulonglong n_rows = 0;
for (const trx_t* t = UT_LIST_GET_FIRST(trx_sys->rw_trx_list); /* Get number of recovered active transactions and number of
t != NULL; rows they modified. Numbers must be accurate, because only this
t = UT_LIST_GET_NEXT(trx_list, t)) { thread is allowed to touch recovered transactions. */
trx_sys->rw_trx_hash.iterate_no_dups(
assert_trx_in_rw_list(t); reinterpret_cast<my_hash_walk_action>
if (t->is_recovered (trx_roll_count_callback), &arg);
&& trx_state_eq(t, TRX_STATE_ACTIVE)) { ib::info() << "To roll back: " << arg.n_trx
n_trx++; << " transactions, " << arg.n_rows << " rows";
n_rows += t->undo_no; sd_notifyf(0, "STATUS=To roll back: " UINT32PF " transactions,"
} " " UINT64PF " rows", arg.n_trx, arg.n_rows);
}
ib::info() << "To roll back: " << n_trx << " transactions, "
<< n_rows << " rows";
sd_notifyf(0, "STATUS=To roll back: " ULINTPF " transactions, "
"%llu rows", n_trx, n_rows);
} }
mutex_exit(&recv_sys->mutex); mutex_exit(&recv_sys->mutex);
mutex_exit(&trx_sys->mutex);
return false; return false;
} }
......
...@@ -2668,141 +2668,121 @@ trx_prepare_for_mysql(trx_t* trx) ...@@ -2668,141 +2668,121 @@ trx_prepare_for_mysql(trx_t* trx)
return(DB_SUCCESS); return(DB_SUCCESS);
} }
/**********************************************************************//**
This function is used to find number of prepared transactions and
their transaction objects for a recovery.
@return number of prepared transactions stored in xid_list */
int
trx_recover_for_mysql(
/*==================*/
XID* xid_list, /*!< in/out: prepared transactions */
ulint len) /*!< in: number of slots in xid_list */
{
const trx_t* trx;
ulint count = 0;
ut_ad(xid_list);
ut_ad(len);
/* We should set those transactions which are in the prepared state
to the xid_list */
trx_sys_mutex_enter();
for (trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list);
trx != NULL;
trx = UT_LIST_GET_NEXT(trx_list, trx)) {
assert_trx_in_rw_list(trx); struct trx_recover_for_mysql_callback_arg
{
XID *xid_list;
uint len;
uint count;
};
/* The state of a read-write transaction cannot change
from or to NOT_STARTED while we are holding the
trx_sys->mutex. It may change to PREPARED, but not if
trx->is_recovered. It may also change to COMMITTED. */
if (trx_state_eq(trx, TRX_STATE_PREPARED)) {
xid_list[count] = *trx->xid;
if (count == 0) { static my_bool trx_recover_for_mysql_callback(rw_trx_hash_element_t *element,
ib::info() << "Starting recovery for" trx_recover_for_mysql_callback_arg *arg)
" XA transactions..."; {
} mutex_enter(&element->mutex);
if (trx_t *trx= element->trx)
{
assert_trx_in_rw_list(element->trx);
ib::info() << "Transaction " /*
<< trx_get_id_for_print(trx) The state of a read-write transaction can only change from ACTIVE to
PREPARED while we are holding the element->mutex. But since it is
executed at startup no state change should occur.
*/
if (trx_state_eq(trx, TRX_STATE_PREPARED))
{
ut_ad(trx->is_recovered);
if (arg->count == 0)
ib::info() << "Starting recovery for XA transactions...";
ib::info() << "Transaction " << trx_get_id_for_print(trx)
<< " in prepared state after recovery"; << " in prepared state after recovery";
ib::info() << "Transaction contains changes to " << trx->undo_no
ib::info() << "Transaction contains changes to " << " rows";
<< trx->undo_no << " rows"; arg->xid_list[arg->count++]= *trx->xid;
count++;
if (count == len) {
break;
}
} }
} }
mutex_exit(&element->mutex);
return arg->count == arg->len;
}
trx_sys_mutex_exit();
if (count > 0){ /**
ib::info() << count << " transactions in prepared state" Find prepared transaction objects for recovery.
" after recovery";
}
return(int (count)); @param[out] xid_list prepared transactions
} @param[in] len number of slots in xid_list
/*******************************************************************//** @return number of prepared transactions stored in xid_list
This function is used to find one X/Open XA distributed transaction */
which is in the prepared state
@return trx on match, the trx->xid will be invalidated; int trx_recover_for_mysql(XID *xid_list, uint len)
note that the trx may have been committed, unless the caller is
holding lock_sys->mutex */
static MY_ATTRIBUTE((warn_unused_result))
trx_t*
trx_get_trx_by_xid_low(
/*===================*/
XID* xid) /*!< in: X/Open XA transaction
identifier */
{ {
trx_t* trx; trx_recover_for_mysql_callback_arg arg= { xid_list, len, 0 };
ut_ad(trx_sys_mutex_own()); ut_ad(xid_list);
ut_ad(len);
for (trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list); /* Fill xid_list with PREPARED transactions. */
trx != NULL; trx_sys->rw_trx_hash.iterate_no_dups(reinterpret_cast<my_hash_walk_action>
trx = UT_LIST_GET_NEXT(trx_list, trx)) { (trx_recover_for_mysql_callback), &arg);
if (arg.count)
ib::info() << arg.count
<< " transactions in prepared state after recovery";
return(arg.count);
}
assert_trx_in_rw_list(trx);
/* Compare two X/Open XA transaction id's: their struct trx_get_trx_by_xid_callback_arg
length should be the same and binary comparison {
of gtrid_length+bqual_length bytes should be XID *xid;
the same */ trx_t *trx;
};
if (trx->is_recovered
&& trx_state_eq(trx, TRX_STATE_PREPARED)
&& xid->eq((XID*)trx->xid)) {
/* Invalidate the XID, so that subsequent calls static my_bool trx_get_trx_by_xid_callback(rw_trx_hash_element_t *element,
will not find it. */ trx_get_trx_by_xid_callback_arg *arg)
{
my_bool found= 0;
mutex_enter(&element->mutex);
if (trx_t *trx= element->trx)
{
assert_trx_in_rw_list(element->trx);
if (trx->is_recovered && trx_state_eq(trx, TRX_STATE_PREPARED) &&
arg->xid->eq(reinterpret_cast<XID*>(trx->xid)))
{
/* Invalidate the XID, so that subsequent calls will not find it. */
trx->xid->null(); trx->xid->null();
break; arg->trx= trx;
found= 1;
} }
} }
mutex_exit(&element->mutex);
return(trx); return found;
} }
/*******************************************************************//**
This function is used to find one X/Open XA distributed transaction
which is in the prepared state
@return trx or NULL; on match, the trx->xid will be invalidated;
note that the trx may have been committed, unless the caller is
holding lock_sys->mutex */
trx_t*
trx_get_trx_by_xid(
/*===============*/
XID* xid) /*!< in: X/Open XA transaction identifier */
{
trx_t* trx;
if (xid == NULL) { /**
Finds PREPARED XA transaction by xid.
return(NULL); trx may have been committed, unless the caller is holding lock_sys->mutex.
}
trx_sys_mutex_enter(); @param[in] xid X/Open XA transaction identifier
/* Recovered/Resurrected transactions are always only on the @return trx or NULL; on match, the trx->xid will be invalidated;
trx_sys_t::rw_trx_list. */ */
trx = trx_get_trx_by_xid_low((XID*)xid);
trx_sys_mutex_exit(); trx_t *trx_get_trx_by_xid(XID *xid)
{
trx_get_trx_by_xid_callback_arg arg= { xid, 0 };
return(trx); if (xid)
trx_sys->rw_trx_hash.iterate(reinterpret_cast<my_hash_walk_action>
(trx_get_trx_by_xid_callback), &arg);
return arg.trx;
} }
/*************************************************************//** /*************************************************************//**
Starts the transaction if it is not yet started. */ Starts the transaction if it is not yet started. */
void void
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment