mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
MDEV-18429: Simpler implementation
This reverts commit61f370a3c9
and implements a simpler fix that is straightforward to merge to 10.3. lock_print_info: Renamed from PrintNotStarted. Dump the entire contents of trx_sys->mysql_trx_list. lock_print_info_rw_recovered: Like lock_print_info, but dump only recovered transactions in trx_sys->rw_trx_list. lock_print_info_all_transactions(): Dump both trx_sys->mysql_trx_list and trx_sys->rw_trx_list. TrxLockIterator, TrxListIterator, lock_rec_fetch_page(): Remove. This is a partial backport of the 10.3 commita447980ff3
which removed the race-condition-prone ability of the InnoDB monitor to read relevant pages into the buffer pool for some record locks.
This commit is contained in:
parent
092602ac9b
commit
810f014ca7
1 changed files with 51 additions and 307 deletions
|
@ -4973,137 +4973,6 @@ lock_print_info_summary(
|
|||
return(TRUE);
|
||||
}
|
||||
|
||||
/** Prints not started transaction or puts it into set.
|
||||
@param[in] trx transaction
|
||||
@param[in,out] file fd where to print
|
||||
@param[in,out] started put transaction here if is started */
|
||||
static void
|
||||
print_not_started(const trx_t* trx, FILE* file, std::set<const trx_t*>& started)
|
||||
{
|
||||
ut_ad(trx->in_mysql_trx_list);
|
||||
ut_ad(mutex_own(&trx_sys->mutex));
|
||||
|
||||
/* See state transitions and locking rules in trx0trx.h */
|
||||
|
||||
if (trx_state_eq(trx, TRX_STATE_NOT_STARTED)) {
|
||||
|
||||
fputs("---", file);
|
||||
trx_print_latched(file, trx, 600);
|
||||
} else {
|
||||
started.insert(trx);
|
||||
}
|
||||
}
|
||||
|
||||
/** Iterate over a transaction's locks. Keeping track of the
|
||||
iterator using an ordinal value. */
|
||||
|
||||
class TrxLockIterator {
|
||||
public:
|
||||
TrxLockIterator() { rewind(); }
|
||||
|
||||
/** Get the m_index(th) lock of a transaction.
|
||||
@return current lock or 0 */
|
||||
const lock_t* current(const trx_t* trx) const
|
||||
{
|
||||
lock_t* lock;
|
||||
ulint i = 0;
|
||||
|
||||
for (lock = UT_LIST_GET_FIRST(trx->lock.trx_locks);
|
||||
lock != NULL && i < m_index;
|
||||
lock = UT_LIST_GET_NEXT(trx_locks, lock), ++i) {
|
||||
|
||||
/* No op */
|
||||
}
|
||||
|
||||
return(lock);
|
||||
}
|
||||
|
||||
/** Set the ordinal value to 0 */
|
||||
void rewind()
|
||||
{
|
||||
m_index = 0;
|
||||
}
|
||||
|
||||
/** Increment the ordinal value.
|
||||
@retun the current index value */
|
||||
ulint next()
|
||||
{
|
||||
return(++m_index);
|
||||
}
|
||||
|
||||
private:
|
||||
/** Current iterator position */
|
||||
ulint m_index;
|
||||
};
|
||||
|
||||
/** This iterates over both the RW and RO trx_sys lists. We need to keep
|
||||
track where the iterator was up to and we do that using an ordinal value. */
|
||||
|
||||
class TrxListIterator {
|
||||
public:
|
||||
TrxListIterator() : m_index()
|
||||
{
|
||||
/* We iterate over the RW trx list first. */
|
||||
|
||||
m_trx_list = &trx_sys->rw_trx_list;
|
||||
}
|
||||
|
||||
/** Get the current transaction whose ordinality is m_index.
|
||||
@return current transaction or 0 */
|
||||
|
||||
const trx_t* current()
|
||||
{
|
||||
return(reposition());
|
||||
}
|
||||
|
||||
/** Advance the transaction current ordinal value and reset the
|
||||
transaction lock ordinal value */
|
||||
|
||||
void next()
|
||||
{
|
||||
++m_index;
|
||||
m_lock_iter.rewind();
|
||||
}
|
||||
|
||||
TrxLockIterator& lock_iter()
|
||||
{
|
||||
return(m_lock_iter);
|
||||
}
|
||||
|
||||
private:
|
||||
/** Reposition the "cursor" on the current transaction. If it
|
||||
is the first time then the "cursor" will be positioned on the
|
||||
first transaction.
|
||||
|
||||
@return transaction instance or 0 */
|
||||
const trx_t* reposition() const
|
||||
{
|
||||
ulint i;
|
||||
trx_t* trx;
|
||||
|
||||
/* Make the transaction at the ordinal value of m_index
|
||||
the current transaction. ie. reposition/restore */
|
||||
|
||||
for (i = 0, trx = UT_LIST_GET_FIRST(*m_trx_list);
|
||||
trx != NULL && (i < m_index);
|
||||
trx = UT_LIST_GET_NEXT(trx_list, trx), ++i) {
|
||||
|
||||
check_trx_state(trx);
|
||||
}
|
||||
|
||||
return(trx);
|
||||
}
|
||||
|
||||
/** Ordinal value of the transaction in the current transaction list */
|
||||
ulint m_index;
|
||||
|
||||
/** Current transaction list */
|
||||
trx_ut_list_t* m_trx_list;
|
||||
|
||||
/** For iterating over a transaction's locks */
|
||||
TrxLockIterator m_lock_iter;
|
||||
};
|
||||
|
||||
/** Prints transaction lock wait and MVCC state.
|
||||
@param[in,out] file file where to print
|
||||
@param[in] trx transaction */
|
||||
|
@ -5140,118 +5009,29 @@ lock_trx_print_wait_and_mvcc_state(
|
|||
}
|
||||
|
||||
/*********************************************************************//**
|
||||
Prints info of locks for a transaction. This function will release the
|
||||
lock mutex and the trx_sys_t::mutex if the page was read from disk.
|
||||
@return true if page was read from the tablespace */
|
||||
Prints info of locks for a transaction. */
|
||||
static
|
||||
bool
|
||||
lock_rec_fetch_page(
|
||||
/*================*/
|
||||
const lock_t* lock) /*!< in: record lock */
|
||||
{
|
||||
ut_ad(lock_get_type_low(lock) == LOCK_REC);
|
||||
|
||||
ulint space_id = lock->un_member.rec_lock.space;
|
||||
fil_space_t* space;
|
||||
bool found;
|
||||
const page_size_t& page_size = fil_space_get_page_size(space_id,
|
||||
&found);
|
||||
ulint page_no = lock->un_member.rec_lock.page_no;
|
||||
|
||||
/* Check if the .ibd file exists. */
|
||||
if (found) {
|
||||
mtr_t mtr;
|
||||
|
||||
lock_mutex_exit();
|
||||
|
||||
mutex_exit(&trx_sys->mutex);
|
||||
|
||||
DEBUG_SYNC_C("innodb_monitor_before_lock_page_read");
|
||||
|
||||
/* Check if the space is exists or not. only
|
||||
when the space is valid, try to get the page. */
|
||||
space = fil_space_acquire(space_id);
|
||||
if (space) {
|
||||
dberr_t err = DB_SUCCESS;
|
||||
mtr_start(&mtr);
|
||||
buf_page_get_gen(
|
||||
page_id_t(space_id, page_no), page_size,
|
||||
RW_NO_LATCH, NULL,
|
||||
BUF_GET_POSSIBLY_FREED,
|
||||
__FILE__, __LINE__, &mtr, &err);
|
||||
mtr_commit(&mtr);
|
||||
fil_space_release(space);
|
||||
}
|
||||
|
||||
lock_mutex_enter();
|
||||
|
||||
mutex_enter(&trx_sys->mutex);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*********************************************************************//**
|
||||
Prints info of locks for a transaction.
|
||||
@return true if all printed, false if latches were released. */
|
||||
static
|
||||
bool
|
||||
void
|
||||
lock_trx_print_locks(
|
||||
/*=================*/
|
||||
FILE* file, /*!< in/out: File to write */
|
||||
const trx_t* trx, /*!< in: current transaction */
|
||||
TrxLockIterator&iter, /*!< in: transaction lock iterator */
|
||||
bool load_block) /*!< in: if true then read block
|
||||
from disk */
|
||||
const trx_t* trx) /*!< in: current transaction */
|
||||
{
|
||||
const lock_t* lock;
|
||||
|
||||
uint32_t i= 0;
|
||||
/* Iterate over the transaction's locks. */
|
||||
while ((lock = iter.current(trx)) != 0) {
|
||||
|
||||
for (lock_t *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_REC) {
|
||||
|
||||
if (load_block) {
|
||||
|
||||
/* Note: lock_rec_fetch_page() will
|
||||
release both the lock mutex and the
|
||||
trx_sys_t::mutex if it does a read
|
||||
from disk. */
|
||||
|
||||
if (lock_rec_fetch_page(lock)) {
|
||||
/* We need to resync the
|
||||
current transaction. */
|
||||
return(false);
|
||||
}
|
||||
|
||||
/* It is a single table tablespace
|
||||
and the .ibd file is missing:
|
||||
just print the lock without
|
||||
attempting to load the page in the
|
||||
buffer pool. */
|
||||
|
||||
fprintf(file,
|
||||
"RECORD LOCKS on non-existing"
|
||||
" space %u\n",
|
||||
lock->un_member.rec_lock.space);
|
||||
}
|
||||
|
||||
/* Print all the record locks on the page from
|
||||
the record lock bitmap */
|
||||
|
||||
lock_rec_print(file, lock);
|
||||
|
||||
load_block = true;
|
||||
|
||||
} else {
|
||||
ut_ad(lock_get_type_low(lock) & LOCK_TABLE);
|
||||
|
||||
lock_table_print(file, lock);
|
||||
}
|
||||
|
||||
if (iter.next() >= 10) {
|
||||
if (++i == 10) {
|
||||
|
||||
fprintf(file,
|
||||
"10 LOCKS PRINTED FOR THIS TRX:"
|
||||
|
@ -5260,10 +5040,48 @@ lock_trx_print_locks(
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
/** Functor to display all transactions (except recovered ones) */
|
||||
struct lock_print_info
|
||||
{
|
||||
lock_print_info(FILE* file) : file(file) {}
|
||||
|
||||
void operator()(const trx_t* trx) const
|
||||
{
|
||||
ut_ad(mutex_own(&trx_sys->mutex));
|
||||
ut_ad(trx->in_mysql_trx_list);
|
||||
lock_trx_print_wait_and_mvcc_state(file, trx);
|
||||
|
||||
if (trx->will_lock && srv_print_innodb_lock_monitor)
|
||||
lock_trx_print_locks(file, trx);
|
||||
}
|
||||
|
||||
FILE* const file;
|
||||
};
|
||||
|
||||
/** Functor to display recovered read-write transactions */
|
||||
struct lock_print_info_rw_recovered
|
||||
{
|
||||
lock_print_info_rw_recovered(FILE* file) : file(file) {}
|
||||
|
||||
void operator()(const trx_t* trx) const
|
||||
{
|
||||
ut_ad(mutex_own(&trx_sys->mutex));
|
||||
ut_ad(trx->in_rw_trx_list);
|
||||
if (trx->mysql_thd)
|
||||
return;
|
||||
ut_ad(!trx->in_mysql_trx_list);
|
||||
|
||||
lock_trx_print_wait_and_mvcc_state(file, trx);
|
||||
|
||||
if (trx->will_lock && srv_print_innodb_lock_monitor)
|
||||
lock_trx_print_locks(file, trx);
|
||||
}
|
||||
|
||||
FILE* const file;
|
||||
};
|
||||
|
||||
/*********************************************************************//**
|
||||
Prints info of locks for each transaction. This function assumes that the
|
||||
caller holds the lock mutex and more importantly it will release the lock
|
||||
|
@ -5278,84 +5096,10 @@ lock_print_info_all_transactions(
|
|||
fprintf(file, "LIST OF TRANSACTIONS FOR EACH SESSION:\n");
|
||||
|
||||
mutex_enter(&trx_sys->mutex);
|
||||
|
||||
/* First print info on non-active transactions */
|
||||
|
||||
/* NOTE: information of auto-commit non-locking read-only
|
||||
transactions will be omitted here. The information will be
|
||||
available from INFORMATION_SCHEMA.INNODB_TRX. */
|
||||
|
||||
std::set<const trx_t*> not_printed_transactions;
|
||||
for (const trx_t* trx = UT_LIST_GET_FIRST(trx_sys->mysql_trx_list);
|
||||
trx; trx = UT_LIST_GET_NEXT(mysql_trx_list, trx)) {
|
||||
print_not_started(trx, file, not_printed_transactions);
|
||||
}
|
||||
|
||||
const trx_t* trx;
|
||||
TrxListIterator trx_iter;
|
||||
const trx_t* prev_trx = 0;
|
||||
|
||||
/* Control whether a block should be fetched from the buffer pool. */
|
||||
bool load_block = true;
|
||||
bool monitor = srv_print_innodb_lock_monitor;
|
||||
|
||||
while ((trx = trx_iter.current()) != 0) {
|
||||
|
||||
check_trx_state(trx);
|
||||
|
||||
not_printed_transactions.erase(trx);
|
||||
|
||||
if (trx != prev_trx) {
|
||||
lock_trx_print_wait_and_mvcc_state(file, trx);
|
||||
prev_trx = trx;
|
||||
|
||||
/* The transaction that read in the page is no
|
||||
longer the one that read the page in. We need to
|
||||
force a page read. */
|
||||
load_block = true;
|
||||
}
|
||||
|
||||
/* If we need to print the locked record contents then we
|
||||
need to fetch the containing block from the buffer pool. */
|
||||
if (monitor) {
|
||||
|
||||
/* Print the locks owned by the current transaction. */
|
||||
TrxLockIterator& lock_iter = trx_iter.lock_iter();
|
||||
|
||||
if (!lock_trx_print_locks(
|
||||
file, trx, lock_iter, load_block)) {
|
||||
|
||||
/* Resync trx_iter, the trx_sys->mutex and
|
||||
the lock mutex were released. A page was
|
||||
successfully read in. We need to print its
|
||||
contents on the next call to
|
||||
lock_trx_print_locks(). On the next call to
|
||||
lock_trx_print_locks() we should simply print
|
||||
the contents of the page just read in.*/
|
||||
load_block = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
load_block = true;
|
||||
|
||||
/* All record lock details were printed without fetching
|
||||
a page from disk, or we didn't need to print the detail. */
|
||||
trx_iter.next();
|
||||
}
|
||||
|
||||
for (std::set<const trx_t*>::const_iterator it
|
||||
= not_printed_transactions.begin(),
|
||||
end = not_printed_transactions.end();
|
||||
it != end; ++it) {
|
||||
fputs("---", file);
|
||||
trx_print_latched(file, *it, 600);
|
||||
}
|
||||
|
||||
lock_mutex_exit();
|
||||
ut_list_map(trx_sys->mysql_trx_list, lock_print_info(file));
|
||||
ut_list_map(trx_sys->rw_trx_list, lock_print_info_rw_recovered(file));
|
||||
mutex_exit(&trx_sys->mutex);
|
||||
|
||||
lock_mutex_exit();
|
||||
ut_ad(lock_validate());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue