Commit 810f014c authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-18429: Simpler implementation

This reverts commit 61f370a3
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
commit a447980f
which removed the race-condition-prone ability of the InnoDB monitor
to read relevant pages into the buffer pool for some record locks.
parent 092602ac
......@@ -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 */
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. */
Prints info of locks for a transaction. */
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());
}
......
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