summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKrunal Bauskar <mysqlonarm@gmail.com>2021-02-23 16:21:30 +0800
committerMarko Mäkelä <marko.makela@mariadb.com>2021-03-11 08:14:26 +0200
commitf11b60879beba19798a76e17af206a95fd8fd204 (patch)
tree204d1a927e7ef6d5fb98c9f77668bc1ddf294075
parent1799caa3a1305d21acaa37169e6b14307b4b5f08 (diff)
downloadmariadb-git-f11b60879beba19798a76e17af206a95fd8fd204.tar.gz
MDEV-24949: Enabling idle flushing (possible regression from MDEV-23855)
- Currently page cleaner thread will stop flushing if dirty_pct < innodb_max_dirty_pages_pct_lwm. - If the server is not performing any activity then said resources/time could be used to flush the pending dirty pages and keep buffer pool clean for the next burst of the cycle. This flushing is called idle flushing. - flushing logic underwent a complete revamp in 10.5.7/8 and as part of the revamp idle flushing logic got removed. - New proposed logic of idle flushing is based on updated logic of the page cleaner that will enable idle flushing if - buf page cleaner is idle - there are dirty pages (< innodb_max_dirty_pages_pct_lwm) - server is not performing any activity Logic will kickstart the idle flushing bounded by innodb_io_capacity. (Thanks to Marko Makela for reviewing the patch and idea right from the its inception).
-rw-r--r--storage/innobase/buf/buf0buf.cc2
-rw-r--r--storage/innobase/buf/buf0flu.cc65
-rw-r--r--storage/innobase/include/buf0buf.h9
3 files changed, 71 insertions, 5 deletions
diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc
index d531be43a8b..2f40ea28e0c 100644
--- a/storage/innobase/buf/buf0buf.cc
+++ b/storage/innobase/buf/buf0buf.cc
@@ -1480,6 +1480,8 @@ bool buf_pool_t::create()
srv_buf_pool_old_size= srv_buf_pool_size;
srv_buf_pool_base_size= srv_buf_pool_size;
+ last_activity_count= srv_get_activity_count();
+
chunk_t::map_ref= chunk_t::map_reg;
buf_LRU_old_ratio_update(100 * 3 / 8, false);
btr_search_sys_create();
diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc
index 0acf9334c2c..9ece21ed3a4 100644
--- a/storage/innobase/buf/buf0flu.cc
+++ b/storage/innobase/buf/buf0flu.cc
@@ -134,7 +134,34 @@ inline void buf_pool_t::page_cleaner_wakeup()
double dirty_pct= 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));
double pct_lwm= srv_max_dirty_pages_pct_lwm;
+
+ /* if pct_lwm != 0.0 means adpative flushing is enabled.
+ signal buf page cleaner thread
+ - if pct_lwm <= dirty_pct then it will invoke apdative flushing flow
+ - if pct_lwm > dirty_pct then it will invoke idle flushing flow.
+
+ idle_flushing:
+ dirty_pct < innodb_max_dirty_pages_pct_lwm so it could be an
+ idle flushing use-case.
+
+ Why is last_activity_count not updated always?
+ - let's first understand when is server activity count updated.
+ - it is updated on commit of a transaction trx_t::commit() and not
+ on adding a page to the flush list.
+ - page_cleaner_wakeup is called when a page is added to the flush list.
+
+ - now let's say the first user thread, updates the count from X -> Y but
+ is yet to commit the transaction (so activity count is still Y).
+ followup user threads will see the updated count as (Y) that is matching
+ the universal server activity count (Y), giving a false impression that
+ the server is idle.
+
+ How to avoid this?
+ - by allowing last_activity_count to updated when page-cleaner is made
+ active and has work to do. This ensures that the last_activity signal
+ is consumed by the page-cleaner before the next one is generated. */
if ((pct_lwm != 0.0 && pct_lwm <= dirty_pct) ||
+ (pct_lwm != 0.0 && last_activity_count == srv_get_activity_count()) ||
srv_max_buf_pool_modified_pct <= dirty_pct)
{
page_cleaner_is_idle= false;
@@ -2076,6 +2103,7 @@ static os_thread_ret_t DECLARE_THREAD(buf_flush_page_cleaner)(void*)
mysql_mutex_lock(&buf_pool.flush_list_mutex);
lsn_t lsn_limit;
+ ulint last_activity_count= srv_get_activity_count();
for (;;)
{
@@ -2095,9 +2123,14 @@ furious_flush:
else if (srv_shutdown_state > SRV_SHUTDOWN_INITIATED)
break;
- if (buf_pool.page_cleaner_idle())
- my_cond_wait(&buf_pool.do_flush_list,
- &buf_pool.flush_list_mutex.m_mutex);
+
+ /* If buf pager cleaner is idle and there is no work
+ (either dirty pages are all flushed or adaptive flushing
+ is not enabled) then opt for non-timed wait */
+ if (buf_pool.page_cleaner_idle() &&
+ (!UT_LIST_GET_LEN(buf_pool.flush_list) ||
+ srv_max_dirty_pages_pct_lwm == 0.0))
+ my_cond_wait(&buf_pool.do_flush_list, &buf_pool.flush_list_mutex.m_mutex);
else
my_cond_timedwait(&buf_pool.do_flush_list,
&buf_pool.flush_list_mutex.m_mutex, &abstime);
@@ -2135,10 +2168,26 @@ unemployed:
const double dirty_pct= double(dirty_blocks) * 100.0 /
double(UT_LIST_GET_LEN(buf_pool.LRU) + UT_LIST_GET_LEN(buf_pool.free));
+ bool idle_flush= false;
+
if (lsn_limit);
else if (srv_max_dirty_pages_pct_lwm != 0.0)
{
- if (dirty_pct < srv_max_dirty_pages_pct_lwm)
+ const ulint activity_count= srv_get_activity_count();
+ if (activity_count != last_activity_count)
+ last_activity_count= activity_count;
+ else if (buf_pool.page_cleaner_idle() && buf_pool.n_pend_reads == 0)
+ {
+ /* reaching here means 3 things:
+ - last_activity_count == activity_count: suggesting server is idle
+ (no trx_t::commit activity)
+ - page cleaner is idle (dirty_pct < srv_max_dirty_pages_pct_lwm)
+ - there are no pending reads but there are dirty pages to flush */
+ idle_flush= true;
+ buf_pool.update_last_activity_count(activity_count);
+ }
+
+ if (!idle_flush && dirty_pct < srv_max_dirty_pages_pct_lwm)
goto unemployed;
}
else if (dirty_pct < srv_max_buf_pool_modified_pct)
@@ -2163,7 +2212,7 @@ unemployed:
pthread_cond_broadcast(&buf_pool.done_flush_list);
goto try_checkpoint;
}
- else if (!srv_adaptive_flushing)
+ else if (idle_flush || !srv_adaptive_flushing)
{
n_flushed= buf_flush_lists(srv_io_capacity, LSN_MAX);
try_checkpoint:
@@ -2220,6 +2269,12 @@ do_checkpoint:
next:
#endif /* !DBUG_OFF */
mysql_mutex_lock(&buf_pool.flush_list_mutex);
+
+ /* when idle flushing kicks in page_cleaner is marked active.
+ reset it back to idle since the it was made active as part of
+ idle flushing stage. */
+ if (idle_flush)
+ buf_pool.page_cleaner_set_idle(true);
}
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h
index 12fb139f1bf..deff8054362 100644
--- a/storage/innobase/include/buf0buf.h
+++ b/storage/innobase/include/buf0buf.h
@@ -1946,6 +1946,8 @@ public:
private:
/** whether the page cleaner needs wakeup from indefinite sleep */
bool page_cleaner_is_idle;
+ /** track server activity count for signaling idle flushing */
+ ulint last_activity_count;
public:
/** signalled to wake up the page_cleaner; protected by flush_list_mutex */
pthread_cond_t do_flush_list;
@@ -1966,6 +1968,13 @@ public:
page_cleaner_is_idle= deep_sleep;
}
+ /** Update server last activity count */
+ void update_last_activity_count(ulint activity_count)
+ {
+ mysql_mutex_assert_owner(&flush_list_mutex);
+ last_activity_count= activity_count;
+ }
+
// n_flush_LRU + n_flush_list is approximately COUNT(io_fix()==BUF_IO_WRITE)
// in flush_list