From bb48d7bc812baf7cbd71c9e41b29fac6288cec97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 13 May 2025 12:27:46 +0300 Subject: [PATCH] MDEV-36781: Assertion i < BUF_BUDDY_SIZES failed in buf_buddy_shrink() buf_buddy_shrink(): Properly cover the case when KEY_BLOCK_SIZE corresponds to the innodb_page_size, that is, the ROW_FORMAT=COMPRESSED page frame is directly allocated from the buffer pool, not via the binary buddy allocator. buf_LRU_check_size_of_non_data_objects(): Avoid a crash when the buffer pool is being shrunk. buf_pool_t::shrink(): Abort if over 95% of the shrunk buffer pool would be occupied by the adaptive hash index or record locks. --- .../suite/innodb/r/innodb_buffer_pool_resize.result | 12 +++++++++++- .../suite/innodb/t/innodb_buffer_pool_resize.test | 11 ++++++++--- storage/innobase/buf/buf0buddy.cc | 13 ++++--------- storage/innobase/buf/buf0buf.cc | 3 +++ storage/innobase/buf/buf0lru.cc | 5 ++++- storage/innobase/include/buf0buddy.h | 2 +- 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb_buffer_pool_resize.result b/mysql-test/suite/innodb/r/innodb_buffer_pool_resize.result index 5db74a71636..66b36f18bc8 100644 --- a/mysql-test/suite/innodb/r/innodb_buffer_pool_resize.result +++ b/mysql-test/suite/innodb/r/innodb_buffer_pool_resize.result @@ -12,12 +12,19 @@ select @@innodb_buffer_pool_size; 10485760 create table t1 (id int primary key, val int not null) ENGINE=InnoDB ROW_FORMAT=COMPRESSED; +create table t2 (id int primary key, val int not null) +ENGINE=InnoDB ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=$kbs; SET STATEMENT foreign_key_checks=0, unique_checks=0 FOR INSERT INTO t1 SELECT seq*4,seq*4 FROM seq_1_to_262144; +SET STATEMENT foreign_key_checks=0, unique_checks=0 FOR +INSERT INTO t2 SELECT seq*4,seq*4 FROM seq_1_to_16384; set global innodb_buffer_pool_size = 7340032; select count(val) from t1; count(val) 262144 +select count(val) from t2; +count(val) +16384 set global innodb_adaptive_hash_index=OFF; set global innodb_buffer_pool_size = 24117248; set global innodb_buffer_pool_size = 26214400; @@ -29,7 +36,10 @@ select @@innodb_buffer_pool_size; select count(val) from t1; count(val) 262144 -drop table t1; +select count(val) from t2; +count(val) +16384 +drop table t1,t2; SET GLOBAL innodb_max_purge_lag_wait = 0; SET @save_pct= @@GLOBAL.innodb_max_dirty_pages_pct; SET @save_pct_lwm= @@GLOBAL.innodb_max_dirty_pages_pct_lwm; diff --git a/mysql-test/suite/innodb/t/innodb_buffer_pool_resize.test b/mysql-test/suite/innodb/t/innodb_buffer_pool_resize.test index 612a0c1be64..4cbbdba9974 100644 --- a/mysql-test/suite/innodb/t/innodb_buffer_pool_resize.test +++ b/mysql-test/suite/innodb/t/innodb_buffer_pool_resize.test @@ -21,6 +21,7 @@ set global innodb_buffer_pool_size = 9437184; set global innodb_buffer_pool_size = 10485760; select @@innodb_buffer_pool_size; +let $kbs=`SELECT CAST(@@innodb_page_size / 1024 AS INT)`; # fill buffer pool --disable_query_log @@ -29,9 +30,13 @@ SET GLOBAL innodb_read_only_compressed=OFF; --enable_query_log create table t1 (id int primary key, val int not null) ENGINE=InnoDB ROW_FORMAT=COMPRESSED; +evalp create table t2 (id int primary key, val int not null) +ENGINE=InnoDB ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=$kbs; SET STATEMENT foreign_key_checks=0, unique_checks=0 FOR INSERT INTO t1 SELECT seq*4,seq*4 FROM seq_1_to_262144; +SET STATEMENT foreign_key_checks=0, unique_checks=0 FOR +INSERT INTO t2 SELECT seq*4,seq*4 FROM seq_1_to_16384; --disable_query_log SET GLOBAL innodb_read_only_compressed=@save_innodb_read_only_compressed; @@ -42,6 +47,7 @@ SET GLOBAL innodb_read_only_compressed=@save_innodb_read_only_compressed; set global innodb_buffer_pool_size = 7340032; select count(val) from t1; +select count(val) from t2; set global innodb_adaptive_hash_index=OFF; @@ -52,8 +58,9 @@ set global innodb_buffer_pool_size = 26214400; select @@innodb_buffer_pool_size; select count(val) from t1; +select count(val) from t2; -drop table t1; +drop table t1,t2; SET GLOBAL innodb_max_purge_lag_wait = 0; SET @save_pct= @@GLOBAL.innodb_max_dirty_pages_pct; @@ -66,8 +73,6 @@ SELECT variable_value = 0 FROM information_schema.global_status WHERE variable_name = 'INNODB_BUFFER_POOL_PAGES_DIRTY'; --source include/wait_condition.inc -# this may occasionally be aborted on a heavily loaded builder ---error 0,ER_WRONG_USAGE SET GLOBAL innodb_buffer_pool_size = @old_innodb_buffer_pool_size; SET GLOBAL innodb_adaptive_hash_index = @old_innodb_adaptive_hash_index; SET GLOBAL innodb_max_dirty_pages_pct = @save_pct; diff --git a/storage/innobase/buf/buf0buddy.cc b/storage/innobase/buf/buf0buddy.cc index 6da47d84307..a56f9047eea 100644 --- a/storage/innobase/buf/buf0buddy.cc +++ b/storage/innobase/buf/buf0buddy.cc @@ -637,7 +637,7 @@ func_exit: buf_buddy_add_to_free(reinterpret_cast(buf), i); } -/** Reallocate a ROW_FORMAT=COMPRESSED page frame during buf_pool_t::resize(). +/** Reallocate a ROW_FORMAT=COMPRESSED page frame during buf_pool_t::shrink(). @param bpage page descriptor covering a ROW_FORMAT=COMPRESSED page @param block uncompressed block for storage @return block @@ -672,10 +672,9 @@ buf_block_t *buf_buddy_shrink(buf_page_t *bpage, buf_block_t *block) noexcept bpage->zip.data= static_cast(dst); buf_pool.buddy_stat[i].relocated++; - for (;;) + while (i < BUF_BUDDY_SIZES) { MEM_UNDEFINED(src, BUF_BUDDY_LOW << i); - ut_ad(i < BUF_BUDDY_SIZES); /* Try to combine adjacent blocks. */ buf_buddy_free_t *buddy= reinterpret_cast (buf_buddy_get(static_cast(src), BUF_BUDDY_LOW << i)); @@ -684,20 +683,16 @@ buf_block_t *buf_buddy_shrink(buf_page_t *bpage, buf_block_t *block) noexcept { ut_ad(!buf_pool.contains_zip(src, BUF_BUDDY_LOW_SHIFT + i)); buf_buddy_add_to_free(static_cast(src), i); - break; + return block; } /* The buddy is free: recombine */ buf_buddy_remove_from_free(buddy, i); i++; src= ut_align_down(src, BUF_BUDDY_LOW << i); - if (i == BUF_BUDDY_SIZES) - { - buf_buddy_block_free(src); - break; - } } + buf_buddy_block_free(src); return block; } diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index b2fde057c43..f9769f3fb18 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -1796,6 +1796,9 @@ ATTRIBUTE_COLD buf_pool_t::shrink_status buf_pool_t::shrink(size_t size) goto next; } + if (UT_LIST_GET_LEN(free) + UT_LIST_GET_LEN(LRU) < usable_size() / 20) + return SHRINK_ABORT; + mysql_mutex_lock(&flush_list_mutex); if (LRU_warned && !UT_LIST_GET_FIRST(free)) diff --git a/storage/innobase/buf/buf0lru.cc b/storage/innobase/buf/buf0lru.cc index 1f5d6eab259..bbf2014608d 100644 --- a/storage/innobase/buf/buf0lru.cc +++ b/storage/innobase/buf/buf0lru.cc @@ -276,7 +276,10 @@ static void buf_LRU_check_size_of_non_data_objects() noexcept auto s= UT_LIST_GET_LEN(buf_pool.free) + UT_LIST_GET_LEN(buf_pool.LRU); - if (s < curr_size / 20) + if (s >= curr_size / 20); + else if (buf_pool.is_shrinking()) + buf_pool.LRU_warn(); + else { sql_print_error("[FATAL] InnoDB: Over 95 percent of the buffer pool is" " occupied by lock heaps" diff --git a/storage/innobase/include/buf0buddy.h b/storage/innobase/include/buf0buddy.h index 9ac26c7d4be..11c42307b47 100644 --- a/storage/innobase/include/buf0buddy.h +++ b/storage/innobase/include/buf0buddy.h @@ -76,7 +76,7 @@ inline void buf_buddy_free(void* buf, ulint size) noexcept } ATTRIBUTE_COLD MY_ATTRIBUTE((nonnull, warn_unused_result)) -/** Reallocate a ROW_FORMAT=COMPRESSED page frame during buf_pool_t::resize(). +/** Reallocate a ROW_FORMAT=COMPRESSED page frame during buf_pool_t::shrink(). @param bpage page descriptor covering a ROW_FORMAT=COMPRESSED page @param block uncompressed block for storage @return block