Commit 7b1252c0 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-24278 InnoDB page cleaner keeps waking up on idle server

The purpose of the InnoDB page cleaner subsystem is to write out
modified pages from the buffer pool to data files. When the
innodb_max_dirty_pages_pct_lwm is not exceeded or
innodb_adaptive_flushing=ON decides not to write out anything,
the page cleaner should keep sleeping indefinitely until the state
of the system changes: a dirty page is added to the buffer pool such
that the page cleaner would no longer be idle.

buf_flush_page_cleaner(): Explicitly note when the page cleaner is idle.
When that happens, use mysql_cond_wait() instead of mysql_cond_timedwait().

buf_flush_insert_into_flush_list(): Wake up the page cleaner if needed.

innodb_max_dirty_pages_pct_update(),
innodb_max_dirty_pages_pct_lwm_update():
Wake up the page cleaner just in case.

Note: buf_flush_ahead(), buf_flush_wait_flushed() and shutdown are
already waking up the page cleaner thread.
parent f693b725
......@@ -126,6 +126,20 @@ static void buf_flush_validate_skip()
}
#endif /* UNIV_DEBUG */
/** Wake up the page cleaner if needed */
inline void buf_pool_t::page_cleaner_wakeup()
{
if (page_cleaner_idle() &&
(srv_max_dirty_pages_pct_lwm == 0.0 ||
srv_max_dirty_pages_pct_lwm <=
double(UT_LIST_GET_LEN(buf_pool.flush_list)) * 100.0 /
double(UT_LIST_GET_LEN(buf_pool.LRU) + UT_LIST_GET_LEN(buf_pool.free))))
{
page_cleaner_is_idle= false;
mysql_cond_signal(&do_flush_list);
}
}
/** Insert a modified block into the flush list.
@param[in,out] block modified block
@param[in] lsn oldest modification */
......@@ -145,6 +159,7 @@ void buf_flush_insert_into_flush_list(buf_block_t* block, lsn_t lsn)
UT_LIST_ADD_FIRST(buf_pool.flush_list, &block->page);
ut_d(buf_flush_validate_skip());
buf_pool.page_cleaner_wakeup();
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
}
......@@ -2067,8 +2082,12 @@ static os_thread_ret_t DECLARE_THREAD(buf_flush_page_cleaner)(void*)
else if (srv_shutdown_state > SRV_SHUTDOWN_INITIATED)
break;
if (buf_pool.page_cleaner_idle())
mysql_cond_wait(&buf_pool.do_flush_list, &buf_pool.flush_list_mutex);
else
mysql_cond_timedwait(&buf_pool.do_flush_list, &buf_pool.flush_list_mutex,
&abstime);
set_timespec(abstime, 1);
lsn_limit= buf_flush_sync_lsn;
......@@ -2091,6 +2110,8 @@ static os_thread_ret_t DECLARE_THREAD(buf_flush_page_cleaner)(void*)
/* wake up buf_flush_wait_flushed() */
mysql_cond_broadcast(&buf_pool.done_flush_list);
}
unemployed:
buf_pool.page_cleaner_set_idle(true);
continue;
}
......@@ -2101,13 +2122,14 @@ static os_thread_ret_t DECLARE_THREAD(buf_flush_page_cleaner)(void*)
double(UT_LIST_GET_LEN(buf_pool.LRU) + UT_LIST_GET_LEN(buf_pool.free));
if (dirty_pct < srv_max_dirty_pages_pct_lwm && !lsn_limit)
continue;
goto unemployed;
const lsn_t oldest_lsn= buf_pool.get_oldest_modification(0);
if (UNIV_UNLIKELY(lsn_limit != 0) && oldest_lsn >= lsn_limit)
buf_flush_sync_lsn= 0;
buf_pool.page_cleaner_set_idle(false);
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
ulint n_flushed;
......@@ -2159,6 +2181,11 @@ static os_thread_ret_t DECLARE_THREAD(buf_flush_page_cleaner)(void*)
goto do_checkpoint;
}
}
else
{
mysql_mutex_lock(&buf_pool.flush_list_mutex);
goto unemployed;
}
#ifdef UNIV_DEBUG
while (innodb_page_cleaner_disabled_debug && !buf_flush_sync_lsn &&
......
......@@ -17207,6 +17207,7 @@ innodb_max_dirty_pages_pct_update(
in_val);
srv_max_dirty_pages_pct_lwm = in_val;
mysql_cond_signal(&buf_pool.do_flush_list);
}
srv_max_buf_pool_modified_pct = in_val;
......@@ -17240,6 +17241,7 @@ innodb_max_dirty_pages_pct_lwm_update(
}
srv_max_dirty_pages_pct_lwm = in_val;
mysql_cond_signal(&buf_pool.do_flush_list);
}
/*************************************************************//**
......
......@@ -1937,10 +1937,29 @@ class buf_pool_t
FlushHp flush_hp;
/** modified blocks (a subset of LRU) */
UT_LIST_BASE_NODE_T(buf_page_t) flush_list;
private:
/** whether the page cleaner needs wakeup from indefinite sleep */
bool page_cleaner_is_idle;
public:
/** signalled to wake up the page_cleaner; protected by flush_list_mutex */
mysql_cond_t do_flush_list;
/** @return whether the page cleaner must sleep due to being idle */
bool page_cleaner_idle() const
{
mysql_mutex_assert_owner(&flush_list_mutex);
return page_cleaner_is_idle;
}
/** Wake up the page cleaner if needed */
inline void page_cleaner_wakeup();
/** Register whether an explicit wakeup of the page cleaner is needed */
void page_cleaner_set_idle(bool deep_sleep)
{
mysql_mutex_assert_owner(&flush_list_mutex);
page_cleaner_is_idle= deep_sleep;
}
// n_flush_LRU + n_flush_list is approximately COUNT(io_fix()==BUF_IO_WRITE)
// in flush_list
......
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