Skip to content

Commit ae744e4

Browse files
committed
MDEV-39344: trx_disconnect_prepared() uses wrong mutex
trx_t::disconnect_prepared(): Replaces trx_disconnect_prepared(). Protect the data members with trx_t::mutex. trx_sys.trx_list: Use a plain ilist<trx_t> that is protected by lock_sys.latch, which was already protecting some traversal of trx_sys.trx_list. All traversal will now use range for, instead of callback functions. fetch_data_into_cache_low(): Return whether we ran out of memory. fetch_data_into_cache(): Reduce the hold time of lock_sys.latch and do not wrongly reset the cache->is_truncated flag at the end. trx_sys_t::clone_oldest_view(): Protect the list traversal with shared lock_sys.latch. trx_sys_t::register_trx(), trx_sys_t::deregister_trx(): Protect trx_sys.trx_list with an exclusive lock_sys.latch.
1 parent 61d251c commit ae744e4

10 files changed

Lines changed: 126 additions & 187 deletions

File tree

storage/innobase/buf/buf0buf.cc

Lines changed: 26 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,42 +1534,6 @@ inline void buf_pool_t::page_hash_table::write_unlock_all() noexcept
15341534
}
15351535

15361536

1537-
namespace
1538-
{
1539-
1540-
struct find_interesting_trx
1541-
{
1542-
void operator()(const trx_t &trx)
1543-
{
1544-
if (!trx.is_started())
1545-
return;
1546-
if (trx.mysql_thd == nullptr)
1547-
return;
1548-
if (withdraw_started <= trx.start_time_micro)
1549-
return;
1550-
1551-
if (!found)
1552-
{
1553-
sql_print_warning("InnoDB: The following trx might hold "
1554-
"the blocks in buffer pool to "
1555-
"be withdrawn. Buffer pool "
1556-
"resizing can complete only "
1557-
"after all the transactions "
1558-
"below release the blocks.");
1559-
found= true;
1560-
}
1561-
1562-
lock_trx_print_wait_and_mvcc_state(stderr, &trx, current_time);
1563-
}
1564-
1565-
bool &found;
1566-
/** microsecond_interval_timer() */
1567-
const ulonglong withdraw_started;
1568-
const my_hrtime_t current_time;
1569-
};
1570-
1571-
} // namespace
1572-
15731537
/** Resize from srv_buf_pool_old_size to srv_buf_pool_size. */
15741538
inline void buf_pool_t::resize()
15751539
{
@@ -1656,15 +1620,37 @@ inline void buf_pool_t::resize()
16561620
message_interval *= 2;
16571621
}
16581622

1659-
bool found= false;
1660-
find_interesting_trx f
1661-
{found, withdraw_started, my_hrtime_coarse()};
16621623
withdraw_started = current_time;
1624+
const my_hrtime_t current_hrtime{my_hrtime_coarse()};
1625+
bool found{false};
16631626

16641627
/* This is going to exceed the maximum size of a
16651628
memory transaction. */
16661629
LockMutexGuard g{SRW_LOCK_CALL};
1667-
trx_sys.trx_list.for_each(f);
1630+
for (const trx_t& trx: trx_sys.trx_list) {
1631+
if (!trx.is_started() || !trx.mysql_thd
1632+
|| withdraw_started <= trx.start_time_micro) {
1633+
continue;
1634+
}
1635+
1636+
if (!found) {
1637+
sql_print_warning("InnoDB: The following "
1638+
"trx might hold "
1639+
"the blocks in "
1640+
"buffer pool to "
1641+
"be withdrawn. "
1642+
"Buffer pool "
1643+
"resizing can "
1644+
"complete only "
1645+
"after all "
1646+
"the transactions "
1647+
"below release the blocks.");
1648+
found = true;
1649+
}
1650+
1651+
lock_trx_print_wait_and_mvcc_state(stderr, &trx,
1652+
current_hrtime);
1653+
}
16681654
}
16691655

16701656
if (should_retry_withdraw) {

storage/innobase/handler/ha_innodb.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2953,7 +2953,7 @@ static int innobase_close_connection(handlerton *, THD *thd) noexcept
29532953
case TRX_STATE_PREPARED:
29542954
if (trx->has_logged_persistent())
29552955
{
2956-
trx_disconnect_prepared(trx);
2956+
trx->disconnect_prepared();
29572957
return 0;
29582958
}
29592959
/* fall through */

storage/innobase/include/trx0sys.h

Lines changed: 7 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -781,58 +781,6 @@ class rw_trx_hash_t
781781
}
782782
};
783783

784-
class thread_safe_trx_ilist_t
785-
{
786-
public:
787-
void create() { mysql_mutex_init(trx_sys_mutex_key, &mutex, nullptr); }
788-
void close() { mysql_mutex_destroy(&mutex); }
789-
790-
bool empty() const
791-
{
792-
mysql_mutex_lock(&mutex);
793-
auto result= trx_list.empty();
794-
mysql_mutex_unlock(&mutex);
795-
return result;
796-
}
797-
798-
void push_front(trx_t &trx)
799-
{
800-
mysql_mutex_lock(&mutex);
801-
trx_list.push_front(trx);
802-
mysql_mutex_unlock(&mutex);
803-
}
804-
805-
void remove(trx_t &trx)
806-
{
807-
mysql_mutex_lock(&mutex);
808-
trx_list.remove(trx);
809-
mysql_mutex_unlock(&mutex);
810-
}
811-
812-
template <typename Callable> void for_each(Callable &&callback) const
813-
{
814-
mysql_mutex_lock(&mutex);
815-
for (const auto &trx : trx_list)
816-
callback(trx);
817-
mysql_mutex_unlock(&mutex);
818-
}
819-
820-
template <typename Callable> void for_each(Callable &&callback)
821-
{
822-
mysql_mutex_lock(&mutex);
823-
for (auto &trx : trx_list)
824-
callback(trx);
825-
mysql_mutex_unlock(&mutex);
826-
}
827-
828-
void freeze() const { mysql_mutex_lock(&mutex); }
829-
void unfreeze() const { mysql_mutex_unlock(&mutex); }
830-
831-
private:
832-
alignas(CPU_LEVEL1_DCACHE_LINESIZE) mutable mysql_mutex_t mutex;
833-
alignas(CPU_LEVEL1_DCACHE_LINESIZE) ilist<trx_t> trx_list;
834-
};
835-
836784
/** The transaction system central memory data structure. */
837785
class trx_sys_t
838786
{
@@ -858,9 +806,10 @@ class trx_sys_t
858806
bool m_initialised;
859807

860808
public:
861-
/** List of all transactions. */
862-
thread_safe_trx_ilist_t trx_list;
809+
/** List of all transactions; protected by lock_sys.latch */
810+
ilist<trx_t> trx_list;
863811

812+
alignas(CPU_LEVEL1_DCACHE_LINESIZE)
864813
/** Temporary rollback segments */
865814
trx_rseg_t temp_rsegs[TRX_SYS_N_RSEGS];
866815

@@ -1102,7 +1051,7 @@ class trx_sys_t
11021051
void close();
11031052

11041053
/** @return total number of active (non-prepared) transactions */
1105-
size_t any_active_transactions(size_t *prepared= nullptr);
1054+
size_t any_active_transactions(size_t *prepared= nullptr) noexcept;
11061055

11071056

11081057
/**
@@ -1179,21 +1128,15 @@ class trx_sys_t
11791128
11801129
@param trx transaction
11811130
*/
1182-
void register_trx(trx_t *trx)
1183-
{
1184-
trx_list.push_front(*trx);
1185-
}
1131+
inline void register_trx(trx_t *trx) noexcept;
11861132

11871133

11881134
/**
11891135
Deregisters transaction in trx_sys.
11901136
11911137
@param trx transaction
11921138
*/
1193-
void deregister_trx(trx_t *trx)
1194-
{
1195-
trx_list.remove(*trx);
1196-
}
1139+
inline void deregister_trx(trx_t *trx) noexcept;
11971140

11981141

11991142
/**
@@ -1207,17 +1150,7 @@ class trx_sys_t
12071150

12081151

12091152
/** @return the number of active views */
1210-
size_t view_count() const
1211-
{
1212-
size_t count= 0;
1213-
1214-
trx_list.for_each([&count](const trx_t &trx) {
1215-
if (trx.read_view.is_open())
1216-
++count;
1217-
});
1218-
1219-
return count;
1220-
}
1153+
size_t view_count() const noexcept;
12211154

12221155
/** Disable further allocation of transactions in a rollback segment
12231156
that are subject to innodb_undo_log_truncate=ON

storage/innobase/include/trx0trx.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,6 @@ trx_t *trx_create();
7373
/** At shutdown, frees a transaction object. */
7474
void trx_free_at_shutdown(trx_t *trx);
7575

76-
/** Disconnect a prepared transaction from MySQL.
77-
@param[in,out] trx transaction */
78-
void trx_disconnect_prepared(trx_t *trx);
79-
8076
/** Initialize (resurrect) transactions at startup. */
8177
dberr_t trx_lists_init_at_db_start();
8278

@@ -730,7 +726,7 @@ struct trx_t : ilist_node<>
730726
This field is accessed by the thread that owns the transaction,
731727
without holding any mutex.
732728
There is only one foreign-thread access in trx_print_low()
733-
and a possible race condition with trx_disconnect_prepared(). */
729+
and a possible race condition with disconnect_prepared(). */
734730
bool is_recovered;
735731
const char* op_info; /*!< English text describing the
736732
current operation, or an empty
@@ -968,6 +964,8 @@ struct trx_t : ilist_node<>
968964
public:
969965
/** Commit the transaction. */
970966
void commit() noexcept;
967+
/** Disconnect a prepared transaction */
968+
void disconnect_prepared() noexcept;
971969

972970
/** Try to drop a persistent table.
973971
@param table persistent table

storage/innobase/lock/lock0lock.cc

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5239,43 +5239,30 @@ lock_trx_print_locks(
52395239
}
52405240
}
52415241

5242-
/** Functor to display all transactions */
5243-
struct lock_print_info
5242+
/*********************************************************************//**
5243+
Prints info of locks for each transaction. This function will release
5244+
lock_sys.latch, which the caller must be holding in exclusive mode.
5245+
@param file output stream */
5246+
void lock_print_info_all_transactions(FILE *file)
52445247
{
5245-
lock_print_info(FILE* file, my_hrtime_t now) :
5246-
file(file), now(now),
5247-
purge_trx(purge_sys.query ? purge_sys.query->trx : nullptr)
5248-
{}
5248+
fprintf(file, "LIST OF TRANSACTIONS FOR EACH SESSION:\n");
52495249

5250-
void operator()(const trx_t &trx) const
5250+
const trx_t *const purge_trx= purge_sys.query
5251+
? purge_sys.query->trx : nullptr;
5252+
const my_hrtime_t now{my_hrtime_coarse()};
5253+
5254+
for (const trx_t &trx : trx_sys.trx_list)
52515255
{
52525256
if (UNIV_UNLIKELY(&trx == purge_trx))
5253-
return;
5257+
continue;
52545258
lock_trx_print_wait_and_mvcc_state(file, &trx, now);
52555259

52565260
if (trx.will_lock && srv_print_innodb_lock_monitor)
52575261
lock_trx_print_locks(file, &trx);
52585262
}
52595263

5260-
FILE* const file;
5261-
const my_hrtime_t now;
5262-
const trx_t* const purge_trx;
5263-
};
5264-
5265-
/*********************************************************************//**
5266-
Prints info of locks for each transaction. This function will release
5267-
lock_sys.latch, which the caller must be holding in exclusive mode. */
5268-
void
5269-
lock_print_info_all_transactions(
5270-
/*=============================*/
5271-
FILE* file) /*!< in/out: file where to print */
5272-
{
5273-
fprintf(file, "LIST OF TRANSACTIONS FOR EACH SESSION:\n");
5274-
5275-
trx_sys.trx_list.for_each(lock_print_info(file, my_hrtime_coarse()));
5276-
lock_sys.wr_unlock();
5277-
5278-
ut_d(lock_validate());
5264+
lock_sys.wr_unlock();
5265+
ut_d(lock_validate());
52795266
}
52805267

52815268
#ifdef UNIV_DEBUG

storage/innobase/read/read0read.cc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Created 2/16/1997 Heikki Tuuri
2929
#include "srv0srv.h"
3030
#include "trx0sys.h"
3131
#include "trx0purge.h"
32+
#include "lock0lock.h"
3233

3334
/*
3435
-------------------------------------------------------------------------------
@@ -259,7 +260,8 @@ void trx_sys_t::clone_oldest_view(ReadViewBase *view) const
259260
{
260261
view->snapshot(nullptr);
261262
/* Find oldest view. */
262-
trx_list.for_each([view](const trx_t &trx) {
263-
trx.read_view.append_to(view);
264-
});
263+
lock_sys.rd_lock(SRW_LOCK_CALL);
264+
for (const trx_t &trx : trx_list)
265+
trx.read_view.append_to(view);
266+
lock_sys.rd_unlock();
265267
}

storage/innobase/srv/srv0srv.cc

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,20 @@ static void srv_refresh_innodb_monitor_stats(time_t current_time)
695695
mysql_mutex_unlock(&srv_innodb_monitor_mutex);
696696
}
697697

698+
size_t trx_sys_t::view_count() const noexcept
699+
{
700+
ut_ad(lock_sys.is_holder());
701+
702+
size_t count{0};
703+
704+
for (const trx_t &trx : trx_list)
705+
if (trx.read_view.is_open())
706+
++count;
707+
708+
return count;
709+
}
710+
711+
698712
/******************************************************************//**
699713
Outputs to a file the output of the InnoDB Monitor.
700714
@return FALSE if not all information printed
@@ -760,6 +774,7 @@ srv_printf_innodb_monitor(
760774
ut_copy_file(file, dict_foreign_err_file);
761775
}
762776

777+
size_t view_count{0};
763778
mysql_mutex_unlock(&dict_foreign_err_mutex);
764779

765780
/* Only if lock_print_info_summary proceeds correctly,
@@ -778,6 +793,8 @@ srv_printf_innodb_monitor(
778793
}
779794
}
780795

796+
view_count = trx_sys.view_count();
797+
781798
/* NOTE: The following function will release the lock_sys.latch
782799
that lock_print_info_summary() acquired. */
783800

@@ -848,11 +865,11 @@ srv_printf_innodb_monitor(
848865

849866
buf_print_io(file);
850867

851-
fputs("--------------\n"
852-
"ROW OPERATIONS\n"
853-
"--------------\n", file);
854-
fprintf(file, ULINTPF " read views open inside InnoDB\n",
855-
trx_sys.view_count());
868+
fprintf(file,
869+
"--------------\n"
870+
"ROW OPERATIONS\n"
871+
"--------------\n"
872+
"%zu read views open inside InnoDB\n", view_count);
856873

857874
if (ulint n_reserved = fil_system.sys_space->n_reserved_extents) {
858875
fprintf(file,

0 commit comments

Comments
 (0)