mirror of
https://github.com/MariaDB/server.git
synced 2025-01-18 04:53:01 +01:00
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.
This commit is contained in:
parent
d8c0caad32
commit
02270b44d0
6 changed files with 355 additions and 379 deletions
|
@ -757,7 +757,20 @@ public:
|
|||
@param argument opque argument passed to action
|
||||
|
||||
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
|
||||
@retval 0 iteration completed successfully
|
||||
|
|
|
@ -269,7 +269,7 @@ int
|
|||
trx_recover_for_mysql(
|
||||
/*==================*/
|
||||
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
|
||||
which is in the prepared state
|
||||
|
|
|
@ -1533,61 +1533,6 @@ lock_sec_rec_some_has_impl(
|
|||
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
|
||||
this transaction. Since delete-marked records may be removed, the
|
||||
|
@ -6464,45 +6409,6 @@ function_exit:
|
|||
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.
|
||||
@return lock at limit or NULL if no more locks in the hash bucket */
|
||||
|
@ -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.
|
||||
@return TRUE if ok */
|
||||
|
@ -6609,7 +6538,9 @@ lock_validate()
|
|||
lock_mutex_enter();
|
||||
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
|
||||
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(
|
|||
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
|
||||
set on the record, sets one for it. */
|
||||
|
@ -6872,8 +6867,8 @@ lock_rec_convert_impl_to_expl(
|
|||
trx = lock_sec_rec_some_has_impl(caller_trx, rec, index,
|
||||
offsets);
|
||||
|
||||
ut_ad(!trx || !lock_rec_other_trx_holds_expl(
|
||||
LOCK_S | LOCK_REC_NOT_GAP, trx, rec, block));
|
||||
ut_d(lock_rec_other_trx_holds_expl(caller_trx, trx, rec,
|
||||
block));
|
||||
}
|
||||
|
||||
if (trx != 0) {
|
||||
|
@ -7771,52 +7766,38 @@ lock_table_get_n_locks(
|
|||
}
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
/*******************************************************************//**
|
||||
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 */
|
||||
/**
|
||||
Do an exhaustive check for any locks (table or rec) against the table.
|
||||
|
||||
@param[in] table check if there are any locks held on records in this table
|
||||
or on the table itself
|
||||
*/
|
||||
|
||||
static my_bool lock_table_locks_lookup(rw_trx_hash_element_t *element,
|
||||
const dict_table_t *table)
|
||||
{
|
||||
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);
|
||||
|
||||
for (lock = UT_LIST_GET_FIRST(trx->lock.trx_locks);
|
||||
lock != NULL;
|
||||
lock = UT_LIST_GET_NEXT(trx_locks, lock)) {
|
||||
|
||||
ut_a(lock->trx == trx);
|
||||
|
||||
if (lock_get_type_low(lock) == LOCK_REC) {
|
||||
ut_ad(!dict_index_is_online_ddl(lock->index)
|
||||
|| dict_index_is_clust(lock->index));
|
||||
if (lock->index->table == table) {
|
||||
return(lock);
|
||||
}
|
||||
} else if (lock->un_member.tab_lock.table == table) {
|
||||
return(lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
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= UT_LIST_GET_NEXT(trx_locks, lock))
|
||||
{
|
||||
ut_ad(lock->trx == element->trx);
|
||||
if (lock_get_type_low(lock) == LOCK_REC)
|
||||
{
|
||||
ut_ad(!dict_index_is_online_ddl(lock->index) ||
|
||||
dict_index_is_clust(lock->index));
|
||||
ut_ad(lock->index->table != table);
|
||||
}
|
||||
else
|
||||
ut_ad(lock->un_member.tab_lock.table != table);
|
||||
}
|
||||
}
|
||||
mutex_exit(&element->mutex);
|
||||
return 0;
|
||||
}
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
|
@ -7832,17 +7813,17 @@ lock_table_has_locks(
|
|||
{
|
||||
ibool has_locks;
|
||||
|
||||
ut_ad(table != NULL);
|
||||
lock_mutex_enter();
|
||||
|
||||
has_locks = UT_LIST_GET_LEN(table->locks) > 0 || table->n_rec_locks > 0;
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
if (!has_locks) {
|
||||
mutex_enter(&trx_sys->mutex);
|
||||
|
||||
ut_ad(!lock_table_locks_lookup(table, &trx_sys->rw_trx_list));
|
||||
|
||||
mutex_exit(&trx_sys->mutex);
|
||||
trx_sys->rw_trx_hash.iterate(
|
||||
reinterpret_cast<my_hash_walk_action>
|
||||
(lock_table_locks_lookup),
|
||||
const_cast<dict_table_t*>(table));
|
||||
}
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
|
|
|
@ -1245,102 +1245,90 @@ trx_i_s_cache_clear(
|
|||
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 */
|
||||
|
||||
/**
|
||||
Add transactions to innodb_trx's cache.
|
||||
|
||||
We also add all locks that are relevant to each transaction into
|
||||
innodb_locks' and innodb_lock_waits' caches.
|
||||
*/
|
||||
|
||||
static void fetch_data_into_cache_low(trx_i_s_cache_t *cache, const trx_t *trx)
|
||||
{
|
||||
const trx_t* trx;
|
||||
bool rw_trx_list = trx_list == &trx_sys->rw_trx_list;
|
||||
i_s_locks_row_t *requested_lock_row;
|
||||
|
||||
ut_ad(rw_trx_list || trx_list == &trx_sys->mysql_trx_list);
|
||||
assert_trx_nonlocking_or_in_list(trx);
|
||||
|
||||
/* Iterate over the transaction list and add each one
|
||||
to innodb_trx's cache. We also add all locks that are relevant
|
||||
to each transaction into innodb_locks' and innodb_lock_waits'
|
||||
caches. */
|
||||
if (add_trx_relevant_locks_to_cache(cache, trx, &requested_lock_row))
|
||||
{
|
||||
if (i_s_trx_row_t *trx_row= reinterpret_cast<i_s_trx_row_t*>(
|
||||
table_cache_create_empty_row(&cache->innodb_trx, cache)))
|
||||
{
|
||||
if (fill_trx_row(trx_row, trx, requested_lock_row, cache))
|
||||
return;
|
||||
--cache->innodb_trx.rows_used;
|
||||
}
|
||||
}
|
||||
|
||||
for (trx = UT_LIST_GET_FIRST(*trx_list);
|
||||
trx != NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
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)) {
|
||||
|
||||
cache->is_truncated = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
trx_row = reinterpret_cast<i_s_trx_row_t*>(
|
||||
table_cache_create_empty_row(
|
||||
&cache->innodb_trx, cache));
|
||||
|
||||
/* memory could not be allocated */
|
||||
if (trx_row == NULL) {
|
||||
|
||||
cache->is_truncated = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fill_trx_row(trx_row, trx, requested_lock_row, cache)) {
|
||||
|
||||
/* memory could not be allocated */
|
||||
--cache->innodb_trx.rows_used;
|
||||
cache->is_truncated = TRUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* memory could not be allocated */
|
||||
cache->is_truncated= TRUE;
|
||||
}
|
||||
|
||||
/*******************************************************************//**
|
||||
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(
|
||||
/*==================*/
|
||||
trx_i_s_cache_t* cache) /*!< in/out: cache */
|
||||
|
||||
static my_bool fetch_data_into_cache_callback(
|
||||
rw_trx_hash_element_t *element, trx_i_s_cache_t *cache)
|
||||
{
|
||||
ut_ad(lock_mutex_own());
|
||||
ut_ad(trx_sys_mutex_own());
|
||||
|
||||
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 */
|
||||
fetch_data_into_cache_low(cache, true, &trx_sys->rw_trx_list);
|
||||
|
||||
/* Capture the state of the read-only active transactions */
|
||||
fetch_data_into_cache_low(cache, false, &trx_sys->mysql_trx_list);
|
||||
|
||||
cache->is_truncated = FALSE;
|
||||
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.
|
||||
*/
|
||||
|
||||
static void fetch_data_into_cache(trx_i_s_cache_t *cache)
|
||||
{
|
||||
ut_ad(lock_mutex_own());
|
||||
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
|
||||
*/
|
||||
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 */
|
||||
trx_sys_mutex_enter();
|
||||
for (const trx_t *trx= UT_LIST_GET_FIRST(trx_sys->mysql_trx_list);
|
||||
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.
|
||||
Called from handler/i_s.cc.
|
||||
|
@ -1358,13 +1346,7 @@ trx_i_s_possibly_fetch_data_into_cache(
|
|||
/* We need to read trx_sys and record/table lock queues */
|
||||
|
||||
lock_mutex_enter();
|
||||
|
||||
trx_sys_mutex_enter();
|
||||
|
||||
fetch_data_into_cache(cache);
|
||||
|
||||
trx_sys_mutex_exit();
|
||||
|
||||
lock_mutex_exit();
|
||||
|
||||
/* update cache last read time */
|
||||
|
|
|
@ -791,6 +791,33 @@ fake_prepared:
|
|||
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.
|
||||
@return whether the rollback should be aborted due to pending shutdown */
|
||||
bool
|
||||
|
@ -808,31 +835,24 @@ trx_roll_must_shutdown()
|
|||
}
|
||||
|
||||
ib_time_t time = ut_time();
|
||||
mutex_enter(&trx_sys->mutex);
|
||||
mutex_enter(&recv_sys->mutex);
|
||||
|
||||
if (recv_sys->report(time)) {
|
||||
ulint n_trx = 0;
|
||||
ulonglong n_rows = 0;
|
||||
for (const trx_t* t = UT_LIST_GET_FIRST(trx_sys->rw_trx_list);
|
||||
t != NULL;
|
||||
t = UT_LIST_GET_NEXT(trx_list, t)) {
|
||||
trx_roll_count_callback_arg arg;
|
||||
|
||||
assert_trx_in_rw_list(t);
|
||||
if (t->is_recovered
|
||||
&& trx_state_eq(t, TRX_STATE_ACTIVE)) {
|
||||
n_trx++;
|
||||
n_rows += t->undo_no;
|
||||
}
|
||||
}
|
||||
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);
|
||||
/* Get number of recovered active transactions and number of
|
||||
rows they modified. Numbers must be accurate, because only this
|
||||
thread is allowed to touch recovered transactions. */
|
||||
trx_sys->rw_trx_hash.iterate_no_dups(
|
||||
reinterpret_cast<my_hash_walk_action>
|
||||
(trx_roll_count_callback), &arg);
|
||||
ib::info() << "To roll back: " << arg.n_trx
|
||||
<< " transactions, " << arg.n_rows << " rows";
|
||||
sd_notifyf(0, "STATUS=To roll back: " UINT32PF " transactions,"
|
||||
" " UINT64PF " rows", arg.n_trx, arg.n_rows);
|
||||
}
|
||||
|
||||
mutex_exit(&recv_sys->mutex);
|
||||
mutex_exit(&trx_sys->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -2668,141 +2668,121 @@ trx_prepare_for_mysql(trx_t* trx)
|
|||
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 */
|
||||
|
||||
struct trx_recover_for_mysql_callback_arg
|
||||
{
|
||||
const trx_t* trx;
|
||||
ulint count = 0;
|
||||
XID *xid_list;
|
||||
uint len;
|
||||
uint count;
|
||||
};
|
||||
|
||||
ut_ad(xid_list);
|
||||
ut_ad(len);
|
||||
|
||||
/* We should set those transactions which are in the prepared state
|
||||
to the xid_list */
|
||||
static my_bool trx_recover_for_mysql_callback(rw_trx_hash_element_t *element,
|
||||
trx_recover_for_mysql_callback_arg *arg)
|
||||
{
|
||||
mutex_enter(&element->mutex);
|
||||
if (trx_t *trx= element->trx)
|
||||
{
|
||||
assert_trx_in_rw_list(element->trx);
|
||||
|
||||
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);
|
||||
|
||||
/* 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) {
|
||||
ib::info() << "Starting recovery for"
|
||||
" XA transactions...";
|
||||
}
|
||||
|
||||
ib::info() << "Transaction "
|
||||
<< trx_get_id_for_print(trx)
|
||||
<< " in prepared state after recovery";
|
||||
|
||||
ib::info() << "Transaction contains changes to "
|
||||
<< trx->undo_no << " rows";
|
||||
|
||||
count++;
|
||||
|
||||
if (count == len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trx_sys_mutex_exit();
|
||||
|
||||
if (count > 0){
|
||||
ib::info() << count << " transactions in prepared state"
|
||||
" after recovery";
|
||||
}
|
||||
|
||||
return(int (count));
|
||||
/*
|
||||
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";
|
||||
ib::info() << "Transaction contains changes to " << trx->undo_no
|
||||
<< " rows";
|
||||
arg->xid_list[arg->count++]= *trx->xid;
|
||||
}
|
||||
}
|
||||
mutex_exit(&element->mutex);
|
||||
return arg->count == arg->len;
|
||||
}
|
||||
|
||||
/*******************************************************************//**
|
||||
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;
|
||||
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 */
|
||||
|
||||
/**
|
||||
Find prepared transaction objects for recovery.
|
||||
|
||||
@param[out] xid_list prepared transactions
|
||||
@param[in] len number of slots in xid_list
|
||||
|
||||
@return number of prepared transactions stored in xid_list
|
||||
*/
|
||||
|
||||
int trx_recover_for_mysql(XID *xid_list, uint len)
|
||||
{
|
||||
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);
|
||||
trx != NULL;
|
||||
trx = UT_LIST_GET_NEXT(trx_list, trx)) {
|
||||
|
||||
assert_trx_in_rw_list(trx);
|
||||
|
||||
/* Compare two X/Open XA transaction id's: their
|
||||
length should be the same and binary comparison
|
||||
of gtrid_length+bqual_length bytes should be
|
||||
the same */
|
||||
|
||||
if (trx->is_recovered
|
||||
&& trx_state_eq(trx, TRX_STATE_PREPARED)
|
||||
&& xid->eq((XID*)trx->xid)) {
|
||||
|
||||
/* Invalidate the XID, so that subsequent calls
|
||||
will not find it. */
|
||||
trx->xid->null();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(trx);
|
||||
/* Fill xid_list with PREPARED transactions. */
|
||||
trx_sys->rw_trx_hash.iterate_no_dups(reinterpret_cast<my_hash_walk_action>
|
||||
(trx_recover_for_mysql_callback), &arg);
|
||||
if (arg.count)
|
||||
ib::info() << arg.count
|
||||
<< " transactions in prepared state after recovery";
|
||||
return(arg.count);
|
||||
}
|
||||
|
||||
/*******************************************************************//**
|
||||
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 */
|
||||
|
||||
struct trx_get_trx_by_xid_callback_arg
|
||||
{
|
||||
trx_t* trx;
|
||||
XID *xid;
|
||||
trx_t *trx;
|
||||
};
|
||||
|
||||
if (xid == NULL) {
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
trx_sys_mutex_enter();
|
||||
|
||||
/* Recovered/Resurrected transactions are always only on the
|
||||
trx_sys_t::rw_trx_list. */
|
||||
trx = trx_get_trx_by_xid_low((XID*)xid);
|
||||
|
||||
trx_sys_mutex_exit();
|
||||
|
||||
return(trx);
|
||||
static my_bool trx_get_trx_by_xid_callback(rw_trx_hash_element_t *element,
|
||||
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();
|
||||
arg->trx= trx;
|
||||
found= 1;
|
||||
}
|
||||
}
|
||||
mutex_exit(&element->mutex);
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Finds PREPARED XA transaction by xid.
|
||||
|
||||
trx may have been committed, unless the caller is holding lock_sys->mutex.
|
||||
|
||||
@param[in] xid X/Open XA transaction identifier
|
||||
|
||||
@return trx or NULL; on match, the trx->xid will be invalidated;
|
||||
*/
|
||||
|
||||
trx_t *trx_get_trx_by_xid(XID *xid)
|
||||
{
|
||||
trx_get_trx_by_xid_callback_arg arg= { xid, 0 };
|
||||
|
||||
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. */
|
||||
void
|
||||
|
|
Loading…
Reference in a new issue