Skip to content

Commit 309302a

Browse files
committed
MDEV-23475 InnoDB performance regression for write-heavy workloads
In commit fe39d02 (MDEV-20638) we removed some wake-up signaling of the master thread that should have been there, to ensure a steady log checkpointing workload. Common sense suggests that the commit omitted some necessary calls to srv_inc_activity_count(). But, an attempt to add the call to trx_flush_log_if_needed_low() as well as to reinstate the function innobase_active_small() did not restore the performance for the case where sync_binlog=1 is set. Therefore, we will revert the entire commit in MariaDB Server 10.2. In MariaDB Server 10.5, adding a srv_inc_activity_count() call to trx_flush_log_if_needed_low() did restore the performance, so we will not revert MDEV-20638 across all versions.
1 parent 1509363 commit 309302a

File tree

11 files changed

+170
-35
lines changed

11 files changed

+170
-35
lines changed

extra/mariabackup/xtrabackup.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,12 @@ my_bool innobase_locks_unsafe_for_binlog;
254254
my_bool innobase_rollback_on_timeout;
255255
my_bool innobase_create_status_file;
256256

257+
/* The following counter is used to convey information to InnoDB
258+
about server activity: in selects it is not sensible to call
259+
srv_active_wake_master_thread after each fetch or search, we only do
260+
it every INNOBASE_WAKE_INTERVAL'th step. */
261+
262+
#define INNOBASE_WAKE_INTERVAL32
257263
ulong innobase_active_counter = 0;
258264

259265

storage/innobase/buf/buf0flu.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3141,7 +3141,7 @@ DECLARE_THREAD(buf_flush_page_cleaner_coordinator)(void*)
31413141
/* The page_cleaner skips sleep if the server is
31423142
idle and there are no pending IOs in the buffer pool
31433143
and there is work to do. */
3144-
if (srv_check_activity(&last_activity)
3144+
if (srv_check_activity(last_activity)
31453145
|| buf_get_n_pending_read_ios()
31463146
|| n_flushed == 0) {
31473147

@@ -3233,7 +3233,7 @@ DECLARE_THREAD(buf_flush_page_cleaner_coordinator)(void*)
32333233

32343234
n_flushed = n_flushed_lru + n_flushed_list;
32353235

3236-
} else if (srv_check_activity(&last_activity)) {
3236+
} else if (srv_check_activity(last_activity)) {
32373237
ulint n_to_flush;
32383238
lsn_tlsn_limit = 0;
32393239

storage/innobase/handler/ha_innodb.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,14 @@ static TYPELIB innodb_lock_schedule_algorithm_typelib = {
446446
NULL
447447
};
448448

449+
/* The following counter is used to convey information to InnoDB
450+
about server activity: in case of normal DML ops it is not
451+
sensible to call srv_active_wake_master_thread after each
452+
operation, we only do it every INNOBASE_WAKE_INTERVAL'th step. */
453+
454+
#define INNOBASE_WAKE_INTERVAL 32
455+
static ulong innobase_active_counter = 0;
456+
449457
/** Allowed values of innodb_change_buffering */
450458
static const char* innobase_change_buffering_values[IBUF_USE_COUNT] = {
451459
"none", /* IBUF_USE_NONE */
@@ -1885,6 +1893,23 @@ thd_to_trx_id(
18851893
}
18861894
#endif /* WITH_WSREP */
18871895

1896+
/********************************************************************//**
1897+
Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth
1898+
time calls srv_active_wake_master_thread. This function should be used
1899+
when a single database operation may introduce a small need for
1900+
server utility activity, like checkpointing. */
1901+
inline
1902+
void
1903+
innobase_active_small(void)
1904+
/*=======================*/
1905+
{
1906+
innobase_active_counter++;
1907+
1908+
if ((innobase_active_counter % INNOBASE_WAKE_INTERVAL) == 0) {
1909+
srv_active_wake_master_thread();
1910+
}
1911+
}
1912+
18881913
/********************************************************************//**
18891914
Converts an InnoDB error code to a MySQL error code and also tells to MySQL
18901915
about a possible transaction rollback inside InnoDB caused by a lock wait
@@ -6607,6 +6632,11 @@ ha_innobase::close()
66076632

66086633
MONITOR_INC(MONITOR_TABLE_CLOSE);
66096634

6635+
/* Tell InnoDB server that there might be work for
6636+
utility threads: */
6637+
6638+
srv_active_wake_master_thread();
6639+
66106640
DBUG_RETURN(0);
66116641
}
66126642

@@ -8321,6 +8351,8 @@ ha_innobase::write_row(
83218351
}
83228352

83238353
func_exit:
8354+
innobase_active_small();
8355+
83248356
DBUG_RETURN(error_result);
83258357
}
83268358

@@ -8991,6 +9023,11 @@ ha_innobase::update_row(
89919023
error, m_prebuilt->table->flags, m_user_thd);
89929024
}
89939025

9026+
/* Tell InnoDB server that there might be work for
9027+
utility threads: */
9028+
9029+
innobase_active_small();
9030+
89949031
#ifdef WITH_WSREP
89959032
if (error == DB_SUCCESS && trx->is_wsrep() &&
89969033
wsrep_thd_exec_mode(m_user_thd) == LOCAL_STATE &&
@@ -9046,6 +9083,11 @@ ha_innobase::delete_row(
90469083

90479084
innobase_srv_conc_exit_innodb(m_prebuilt);
90489085

9086+
/* Tell the InnoDB server that there might be work for
9087+
utility threads: */
9088+
9089+
innobase_active_small();
9090+
90499091
#ifdef WITH_WSREP
90509092
if (error == DB_SUCCESS && trx->is_wsrep()
90519093
&& wsrep_thd_exec_mode(m_user_thd) == LOCAL_STATE
@@ -12933,6 +12975,7 @@ create_table_info_t::create_table_update_dict()
1293312975
if (m_flags2 & DICT_TF2_FTS) {
1293412976
if (!innobase_fts_load_stopword(innobase_table, NULL, m_thd)) {
1293512977
dict_table_close(innobase_table, FALSE, FALSE);
12978+
srv_active_wake_master_thread();
1293612979
trx_free_for_mysql(m_trx);
1293712980
DBUG_RETURN(-1);
1293812981
}
@@ -13078,6 +13121,11 @@ ha_innobase::create(
1307813121

1307913122
error = info.create_table_update_dict();
1308013123

13124+
/* Tell the InnoDB server that there might be work for
13125+
utility threads: */
13126+
13127+
srv_active_wake_master_thread();
13128+
1308113129
DBUG_RETURN(error);
1308213130
}
1308313131

storage/innobase/handler/handler0alter.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8641,6 +8641,11 @@ ha_innobase::commit_inplace_alter_table(
86418641

86428642
log_append_on_checkpoint(NULL);
86438643

8644+
/* Tell the InnoDB server that there might be work for
8645+
utility threads: */
8646+
8647+
srv_active_wake_master_thread();
8648+
86448649
if (fail) {
86458650
for (inplace_alter_handler_ctx** pctx = ctx_array;
86468651
*pctx; pctx++) {

storage/innobase/include/srv0srv.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,19 @@ srv_reset_io_thread_op_info();
809809
/** Wake up the purge threads if there is work to do. */
810810
void
811811
srv_wake_purge_thread_if_not_active();
812+
/** Wake up the InnoDB master thread if it was suspended (not sleeping). */
813+
void
814+
srv_active_wake_master_thread_low();
815+
816+
#define srv_active_wake_master_thread() \
817+
do { \
818+
if (!srv_read_only_mode) { \
819+
srv_active_wake_master_thread_low(); \
820+
} \
821+
} while (0)
822+
/** Wake up the master thread if it is suspended or being suspended. */
823+
void
824+
srv_wake_master_thread();
812825

813826
/******************************************************************//**
814827
Outputs to a file the output of the InnoDB Monitor.
@@ -837,13 +850,13 @@ reading this value as it is only used in heuristics.
837850
ulint
838851
srv_get_activity_count(void);
839852
/*========================*/
840-
841-
/** Check if there has been any activity.
842-
@param[in,out] activity_count recent activity count to be returned
843-
if there is a change
853+
/*******************************************************************//**
854+
Check if there has been any activity.
844855
@return FALSE if no change in activity counter. */
845-
bool srv_check_activity(ulint *activity_count);
846-
856+
ibool
857+
srv_check_activity(
858+
/*===============*/
859+
ulintold_activity_count);/*!< old activity count */
847860
/******************************************************************//**
848861
Increment the server activity counter. */
849862
void

storage/innobase/row/row0mysql.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3820,7 +3820,7 @@ row_drop_table_for_mysql(
38203820

38213821
trx->op_info = "";
38223822

3823-
srv_inc_activity_count();
3823+
srv_wake_master_thread();
38243824

38253825
DBUG_RETURN(err);
38263826
}

storage/innobase/row/row0trunc.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1249,7 +1249,7 @@ row_truncate_complete(
12491249

12501250
ut_ad(!trx_is_started(trx));
12511251

1252-
srv_inc_activity_count();
1252+
srv_wake_master_thread();
12531253

12541254
DBUG_EXECUTE_IF("ib_trunc_crash_after_truncate_done",
12551255
DBUG_SUICIDE(););

storage/innobase/srv/srv0srv.cc

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,6 +1982,33 @@ srv_get_active_thread_type(void)
19821982
return(ret);
19831983
}
19841984

1985+
/** Wake up the InnoDB master thread if it was suspended (not sleeping). */
1986+
void
1987+
srv_active_wake_master_thread_low()
1988+
{
1989+
ut_ad(!srv_read_only_mode);
1990+
ut_ad(!srv_sys_mutex_own());
1991+
1992+
srv_inc_activity_count();
1993+
1994+
if (my_atomic_loadlint(&srv_sys.n_threads_active[SRV_MASTER]) == 0) {
1995+
srv_slot_t* slot;
1996+
1997+
srv_sys_mutex_enter();
1998+
1999+
slot = &srv_sys.sys_threads[SRV_MASTER_SLOT];
2000+
2001+
/* Only if the master thread has been started. */
2002+
2003+
if (slot->in_use) {
2004+
ut_a(srv_slot_get_type(slot) == SRV_MASTER);
2005+
os_event_set(slot->event);
2006+
}
2007+
2008+
srv_sys_mutex_exit();
2009+
}
2010+
}
2011+
19852012
/** Wake up the purge threads if there is work to do. */
19862013
void
19872014
srv_wake_purge_thread_if_not_active()
@@ -1996,6 +2023,14 @@ srv_wake_purge_thread_if_not_active()
19962023
}
19972024
}
19982025

2026+
/** Wake up the master thread if it is suspended or being suspended. */
2027+
void
2028+
srv_wake_master_thread()
2029+
{
2030+
srv_inc_activity_count();
2031+
srv_release_threads(SRV_MASTER, 1);
2032+
}
2033+
19992034
/*******************************************************************//**
20002035
Get current server activity count. We don't hold srv_sys::mutex while
20012036
reading this value as it is only used in heuristics.
@@ -2007,20 +2042,15 @@ srv_get_activity_count(void)
20072042
return(srv_sys.activity_count);
20082043
}
20092044

2010-
/** Check if there has been any activity.
2011-
@param[in,out] activity_count recent activity count to be returned
2012-
if there is a change
2045+
/*******************************************************************//**
2046+
Check if there has been any activity.
20132047
@return FALSE if no change in activity counter. */
2014-
bool srv_check_activity(ulint *activity_count)
2048+
ibool
2049+
srv_check_activity(
2050+
/*===============*/
2051+
ulint old_activity_count)/*!< in: old activity count */
20152052
{
2016-
ulint new_activity_count= srv_sys.activity_count;
2017-
if (new_activity_count != *activity_count)
2018-
{
2019-
*activity_count= new_activity_count;
2020-
return true;
2021-
}
2022-
2023-
return false;
2053+
return(srv_sys.activity_count != old_activity_count);
20242054
}
20252055

20262056
/********************************************************************//**
@@ -2427,30 +2457,52 @@ DECLARE_THREAD(srv_master_thread)(
24272457
slot = srv_reserve_slot(SRV_MASTER);
24282458
ut_a(slot == srv_sys.sys_threads);
24292459

2460+
loop:
24302461
while (srv_shutdown_state <= SRV_SHUTDOWN_INITIATED) {
24312462
srv_master_sleep();
24322463

24332464
MONITOR_INC(MONITOR_MASTER_THREAD_SLEEP);
24342465

2435-
if (srv_check_activity(&old_activity_count)) {
2466+
if (srv_check_activity(old_activity_count)) {
2467+
old_activity_count = srv_get_activity_count();
24362468
srv_master_do_active_tasks();
24372469
} else {
24382470
srv_master_do_idle_tasks();
24392471
}
24402472
}
24412473

2442-
ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS
2443-
|| srv_shutdown_state == SRV_SHUTDOWN_CLEANUP);
2444-
2445-
if (srv_shutdown_state == SRV_SHUTDOWN_CLEANUP
2446-
&& srv_fast_shutdown < 2) {
2447-
srv_shutdown(srv_fast_shutdown == 0);
2474+
switch (srv_shutdown_state) {
2475+
case SRV_SHUTDOWN_NONE:
2476+
case SRV_SHUTDOWN_INITIATED:
2477+
break;
2478+
case SRV_SHUTDOWN_FLUSH_PHASE:
2479+
case SRV_SHUTDOWN_LAST_PHASE:
2480+
ut_ad(0);
2481+
/* fall through */
2482+
case SRV_SHUTDOWN_EXIT_THREADS:
2483+
/* srv_init_abort() must have been invoked */
2484+
case SRV_SHUTDOWN_CLEANUP:
2485+
if (srv_shutdown_state == SRV_SHUTDOWN_CLEANUP
2486+
&& srv_fast_shutdown < 2) {
2487+
srv_shutdown(srv_fast_shutdown == 0);
2488+
}
2489+
srv_suspend_thread(slot);
2490+
my_thread_end();
2491+
os_thread_exit();
24482492
}
24492493

2494+
srv_main_thread_op_info = "suspending";
2495+
24502496
srv_suspend_thread(slot);
2451-
my_thread_end();
2452-
os_thread_exit();
2453-
OS_THREAD_DUMMY_RETURN;
2497+
2498+
/* DO NOT CHANGE THIS STRING. innobase_start_or_create_for_mysql()
2499+
waits for database activity to die down when converting < 4.1.x
2500+
databases, and relies on this string being exactly as it is. InnoDB
2501+
manual also mentions this string in several places. */
2502+
srv_main_thread_op_info = "waiting for server activity";
2503+
2504+
srv_resume_thread(slot);
2505+
goto loop;
24542506
}
24552507

24562508
/** Check if purge should stop.
@@ -2647,13 +2699,15 @@ srv_do_purge(ulint* n_total_purged
26472699
++n_use_threads;
26482700
}
26492701

2650-
} else if (srv_check_activity(&old_activity_count)
2702+
} else if (srv_check_activity(old_activity_count)
26512703
&& n_use_threads > 1) {
26522704

26532705
/* History length same or smaller since last snapshot,
26542706
use fewer threads. */
26552707

26562708
--n_use_threads;
2709+
2710+
old_activity_count = srv_get_activity_count();
26572711
}
26582712

26592713
/* Ensure that the purge threads are less than what

storage/innobase/srv/srv0start.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,7 +1238,7 @@ srv_shutdown_all_bg_threads()
12381238
if (srv_start_state_is_set(SRV_START_STATE_MASTER)) {
12391239
/* c. We wake the master thread so that
12401240
it exits */
1241-
srv_inc_activity_count();
1241+
srv_wake_master_thread();
12421242
}
12431243

12441244
if (srv_start_state_is_set(SRV_START_STATE_PURGE)) {
@@ -2762,7 +2762,7 @@ srv_shutdown_bg_undo_sources()
27622762
fts_optimize_shutdown();
27632763
dict_stats_shutdown();
27642764
while (row_get_background_drop_list_len_low()) {
2765-
srv_inc_activity_count();
2765+
srv_wake_master_thread();
27662766
os_thread_yield();
27672767
}
27682768
srv_undo_sources = false;

storage/innobase/trx/trx0roll.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ trx_rollback_to_savepoint_low(
124124

125125
mem_heap_free(heap);
126126

127+
/* There might be work for utility threads.*/
128+
srv_active_wake_master_thread();
129+
127130
MONITOR_DEC(MONITOR_TRX_ACTIVE);
128131
}
129132

0 commit comments

Comments
 (0)