From 35d477dd1dff464bd80dff9efe83884ce1c38e4c Mon Sep 17 00:00:00 2001 From: mariadb-DebarunBanerjee Date: Wed, 18 Sep 2024 21:56:28 +0530 Subject: [PATCH] MDEV-34453 Trying to read 16384 bytes at 70368744161280 outside the bounds of the file: ./ibdata1 The issue is caused by a race between buf_page_create_low getting the page from buffer pool hash and buf_LRU_free_page evicting it from LRU. The issue is introduced in 10.6 by MDEV-27058 commit aaef2e1d8c843d1e40b1ce0c5199c3abb3c5da28 MDEV-27058: Reduce the size of buf_block_t and buf_page_t The solution is buffer fix the page before releasing buffer pool mutex in buf_page_create_low when x_lock_try fails to acquire the page latch. --- mysql-test/suite/stress/t/ddl_innodb.test | 14 ++++++++++ storage/innobase/buf/buf0buf.cc | 31 ++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/stress/t/ddl_innodb.test b/mysql-test/suite/stress/t/ddl_innodb.test index d5e23124b4b..79414a36a2a 100644 --- a/mysql-test/suite/stress/t/ddl_innodb.test +++ b/mysql-test/suite/stress/t/ddl_innodb.test @@ -1,4 +1,5 @@ --source include/no_valgrind_without_big.inc +--source include/maybe_debug.inc ######## t/ddl_innodb.test ###### # # Stress the storage engine InnoDB with CREATE/DROP TABLE/INDEX @@ -34,6 +35,13 @@ if (!$run) ##### Some preparations needed for the ddl*.inc scripts --source suite/stress/include/ddl.pre +if ($have_debug) { + --disable_query_log + SET @old_debug_dbug = @@global.debug_dbug; + SET DEBUG_DBUG="+d,ib_buf_create_intermittent_wait"; + --enable_query_log +} + --source suite/stress/include/ddl1.inc --source suite/stress/include/ddl2.inc --source suite/stress/include/ddl3.inc @@ -43,5 +51,11 @@ if (!$run) --source suite/stress/include/ddl7.inc --source suite/stress/include/ddl8.inc +if ($have_debug) { + --disable_query_log + SET @@global.debug_dbug = @old_debug_dbug; + --enable_query_log +} + ##### Cleanup --source suite/stress/include/ddl.cln diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 7796d1dfa00..dcebd9414ba 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -3289,22 +3289,47 @@ retry: if (!mtr->have_x_latch(reinterpret_cast(*bpage))) { - const bool got= bpage->lock.x_lock_try(); - if (!got) + /* Buffer-fix the block to prevent the block being concurrently freed + after we release the buffer pool mutex. It should work fine with + concurrent load of the page (free on disk) to buffer pool due to + possible read ahead. After we find a zero filled page during load, we + call buf_pool_t::corrupted_evict, where we try to wait for all buffer + fixes to go away only after resetting the page ID and releasing the + page latch. */ + auto state= bpage->fix(); + DBUG_EXECUTE_IF("ib_buf_create_intermittent_wait", { + static bool need_to_wait = false; + need_to_wait = !need_to_wait; + /* Simulate try lock failure in every alternate call. */ + if (need_to_wait) { + goto must_wait; + } + }); + + if (!bpage->lock.x_lock_try()) + { +#ifndef DBUG_OFF + must_wait: +#endif mysql_mutex_unlock(&buf_pool.mutex); + bpage->lock.x_lock(); const page_id_t id{bpage->id()}; if (UNIV_UNLIKELY(id != page_id)) { ut_ad(id.is_corrupted()); + ut_ad(bpage->is_freed()); + bpage->unfix(); bpage->lock.x_unlock(); goto retry; } mysql_mutex_lock(&buf_pool.mutex); + state= bpage->state(); + ut_ad(!bpage->is_io_fixed(state)); + ut_ad(bpage->buf_fix_count(state)); } - auto state= bpage->fix(); ut_ad(state >= buf_page_t::FREED); ut_ad(state < buf_page_t::READ_FIX);