MDEV-22646 Assertion `table2->cached' failed in dict_table_t::add_to_cache

Problem:
========
  During buffer pool resizing, InnoDB recreates the dictionary hash
tables. Dictionary hash table reuses the heap of AHI hash tables.
It leads to memory corruption.

Fix:
====
- While disabling AHI, free the heap and AHI hash tables. Recreate the
AHI hash tables and assign new heap when AHI is enabled.

- btr_blob_free() access invalid page if page was reallocated during
buffer poolresizing. So btr_blob_free() should get the page from
buf_pool instead of using existing block.

- btr_search_enabled and block->index should be checked after
acquiring the btr_search_sys latch

- Moved the buffer_pool_scan debug sync to earlier before accessing the
btr_search_sys latches to avoid the hang of truncate_purge_debug
test case

- srv_printf_innodb_monitor() should acquire btr_search_sys latches
before AHI hash tables.
parent ca3aa679
......@@ -6631,29 +6631,19 @@ btr_blob_free(
mtr_t* mtr) /*!< in: mini-transaction to commit */
{
buf_pool_t* buf_pool = buf_pool_from_block(block);
ulint space = block->page.id.space();
ulint page_no = block->page.id.page_no();
const page_id_t page_id = block->page.id;
ut_ad(mtr_is_block_fix(mtr, block, MTR_MEMO_PAGE_X_FIX, index->table));
mtr_commit(mtr);
buf_pool_mutex_enter(buf_pool);
/* Only free the block if it is still allocated to
the same file page. */
if (buf_block_get_state(block)
== BUF_BLOCK_FILE_PAGE
&& block->page.id.space() == space
&& block->page.id.page_no() == page_no) {
if (!buf_LRU_free_page(&block->page, all)
&& all && block->page.zip.data) {
if (buf_page_t* bpage = buf_page_hash_get(buf_pool, page_id)) {
if (!buf_LRU_free_page(bpage, all)
&& all && bpage->zip.data) {
/* Attempt to deallocate the uncompressed page
if the whole block cannot be deallocted. */
buf_LRU_free_page(&block->page, false);
buf_LRU_free_page(bpage, false);
}
}
......
......@@ -141,28 +141,32 @@ static
void
btr_search_check_free_space_in_heap(dict_index_t* index)
{
hash_table_t* table = btr_get_search_table(index);
mem_heap_t* heap = table->heap;
/* Note that we peek the value of heap->free_block without reserving
the latch: this is ok, because we will not guarantee that there will
be enough free space in the hash table. */
if (heap->free_block == NULL) {
buf_block_t* block = buf_block_alloc(NULL);
rw_lock_t* latch = btr_get_search_latch(index);
hash_table_t* table;
mem_heap_t* heap;
rw_lock_x_lock(latch);
if (btr_search_enabled
&& heap->free_block == NULL) {
if (!btr_search_enabled) {
goto func_exit;
}
table = btr_get_search_table(index);
heap = table->heap;
if (heap->free_block == NULL) {
heap->free_block = block;
} else {
func_exit:
buf_block_free(block);
}
rw_lock_x_unlock(latch);
}
}
/** Creates and initializes the adaptive search system at a database start.
......@@ -191,58 +195,11 @@ btr_search_sys_create(ulint hash_size)
btr_search_sys = reinterpret_cast<btr_search_sys_t*>(
ut_malloc(sizeof(btr_search_sys_t), mem_key_ahi));
btr_search_sys->hash_tables = reinterpret_cast<hash_table_t**>(
ut_malloc(sizeof(hash_table_t*) * btr_ahi_parts, mem_key_ahi));
for (ulint i = 0; i < btr_ahi_parts; ++i) {
btr_search_sys->hash_tables[i] =
ib_create((hash_size / btr_ahi_parts),
LATCH_ID_HASH_TABLE_MUTEX,
0, MEM_HEAP_FOR_BTR_SEARCH);
#if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
btr_search_sys->hash_tables[i]->adaptive = TRUE;
#endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */
}
}
/** Resize hash index hash table.
@param[in] hash_size hash index hash table size */
void
btr_search_sys_resize(ulint hash_size)
{
/* Step-1: Lock all search latches in exclusive mode. */
btr_search_x_lock_all();
btr_search_sys->hash_tables = NULL;
if (btr_search_enabled) {
btr_search_x_unlock_all();
ib::error() << "btr_search_sys_resize failed because"
" hash index hash table is not empty.";
ut_ad(0);
return;
}
/* Step-2: Recreate hash tables with new size. */
for (ulint i = 0; i < btr_ahi_parts; ++i) {
mem_heap_free(btr_search_sys->hash_tables[i]->heap);
hash_table_free(btr_search_sys->hash_tables[i]);
btr_search_sys->hash_tables[i] =
ib_create((hash_size / btr_ahi_parts),
LATCH_ID_HASH_TABLE_MUTEX,
0, MEM_HEAP_FOR_BTR_SEARCH);
#if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
btr_search_sys->hash_tables[i]->adaptive = TRUE;
#endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */
btr_search_enable();
}
/* Step-3: Unlock all search latches from exclusive mode. */
btr_search_x_unlock_all();
}
/** Frees the adaptive search system at a database shutdown. */
......@@ -251,19 +208,10 @@ btr_search_sys_free()
{
ut_ad(btr_search_sys != NULL && btr_search_latches != NULL);
/* Step-1: Release the hash tables. */
for (ulint i = 0; i < btr_ahi_parts; ++i) {
mem_heap_free(btr_search_sys->hash_tables[i]->heap);
hash_table_free(btr_search_sys->hash_tables[i]);
}
ut_free(btr_search_sys->hash_tables);
ut_free(btr_search_sys);
btr_search_sys = NULL;
/* Step-2: Release all allocates latches. */
/* Free all latches. */
for (ulint i = 0; i < btr_ahi_parts; ++i) {
rw_lock_free(btr_search_latches[i]);
......@@ -365,26 +313,17 @@ static void buf_pool_clear_hash_index()
btr_search_lazy_free(*i);
}
/** Disable the adaptive hash search system and empty the index.
@param[in] need_mutex need to acquire dict_sys->mutex */
void
btr_search_disable(
bool need_mutex)
/** Disable the adaptive hash search system and empty the index. */
void btr_search_disable()
{
dict_table_t* table;
if (need_mutex) {
mutex_enter(&dict_sys->mutex);
}
ut_ad(mutex_own(&dict_sys->mutex));
btr_search_x_lock_all();
if (!btr_search_enabled) {
if (need_mutex) {
mutex_exit(&dict_sys->mutex);
}
btr_search_x_unlock_all();
return;
}
......@@ -405,34 +344,57 @@ btr_search_disable(
btr_search_disable_ref_count(table);
}
if (need_mutex) {
mutex_exit(&dict_sys->mutex);
}
/* Set all block->index = NULL. */
buf_pool_clear_hash_index();
/* Clear the adaptive hash index. */
for (ulint i = 0; i < btr_ahi_parts; ++i) {
hash_table_clear(btr_search_sys->hash_tables[i]);
mem_heap_empty(btr_search_sys->hash_tables[i]->heap);
mem_heap_free(btr_search_sys->hash_tables[i]->heap);
hash_table_free(btr_search_sys->hash_tables[i]);
}
ut_free(btr_search_sys->hash_tables);
btr_search_sys->hash_tables = NULL;
btr_search_x_unlock_all();
}
/** Enable the adaptive hash search system. */
/** Enable the adaptive hash search system.
@param[in] resize Flag to indicate call during buf_pool_resize() */
void
btr_search_enable()
btr_search_enable(bool resize)
{
if (!resize) {
buf_pool_mutex_enter_all();
if (srv_buf_pool_old_size != srv_buf_pool_size) {
buf_pool_mutex_exit_all();
return;
}
buf_pool_mutex_exit_all();
}
ulint hash_size = buf_pool_get_curr_size() / sizeof(void *) / 64;
btr_search_x_lock_all();
if (btr_search_sys->hash_tables) {
ut_ad(btr_search_enabled);
btr_search_x_unlock_all();
return;
}
btr_search_sys->hash_tables = reinterpret_cast<hash_table_t**>(
ut_malloc(sizeof(hash_table_t*) * btr_ahi_parts, mem_key_ahi));
for (ulint i = 0; i < btr_ahi_parts; ++i) {
btr_search_sys->hash_tables[i] =
ib_create((hash_size / btr_ahi_parts),
LATCH_ID_HASH_TABLE_MUTEX,
0, MEM_HEAP_FOR_BTR_SEARCH);
#if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
btr_search_sys->hash_tables[i]->adaptive = TRUE;
#endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */
}
btr_search_enabled = true;
btr_search_x_unlock_all();
}
......@@ -662,7 +624,8 @@ btr_search_update_hash_ref(
if (block->index
&& (block->curr_n_fields == info->n_fields)
&& (block->curr_n_bytes == info->n_bytes)
&& (block->curr_left_side == info->left_side)) {
&& (block->curr_left_side == info->left_side)
&& btr_search_enabled) {
mem_heap_t* heap = NULL;
rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs_init(offsets_);
......@@ -1173,7 +1136,7 @@ btr_search_drop_page_hash_index(buf_block_t* block)
rw_lock_s_lock(latch);
assert_block_ahi_valid(block);
if (!block->index) {
if (!block->index || !btr_search_enabled) {
rw_lock_s_unlock(latch);
return;
}
......@@ -1409,6 +1372,11 @@ btr_search_build_page_hash_index(
rw_lock_t* const latch = btr_get_search_latch(index);
rw_lock_s_lock(latch);
if (!btr_search_enabled) {
rw_lock_s_unlock(latch);
return;
}
table = btr_get_search_table(index);
page = buf_block_get_frame(block);
......@@ -1517,6 +1485,7 @@ btr_search_build_page_hash_index(
goto exit_func;
}
table = btr_get_search_table(index);
if (block->index && ((block->curr_n_fields != n_fields)
|| (block->curr_n_bytes != n_bytes)
|| (block->curr_left_side != left_side))) {
......@@ -1663,8 +1632,6 @@ btr_search_update_hash_on_delete(btr_cur_t* cursor)
ut_a(block->curr_n_fields > 0 || block->curr_n_bytes > 0);
ut_ad(!dict_index_is_ibuf(index));
table = btr_get_search_table(index);
rec = btr_cur_get_rec(cursor);
fold = rec_fold(rec, rec_get_offsets(rec, index, offsets_, true,
......@@ -1678,6 +1645,12 @@ btr_search_update_hash_on_delete(btr_cur_t* cursor)
rw_lock_x_lock(latch);
assert_block_ahi_valid(block);
if (!btr_search_enabled) {
rw_lock_x_unlock(latch);
return;
}
table = btr_get_search_table(index);
if (block->index) {
ut_a(block->index == index);
......@@ -1732,7 +1705,7 @@ btr_search_update_hash_node_on_insert(btr_cur_t* cursor)
rw_lock_t* const latch = btr_get_search_latch(index);
rw_lock_x_lock(latch);
if (!block->index) {
if (!block->index || !btr_search_enabled) {
goto func_exit;
}
......@@ -1808,8 +1781,6 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor)
ut_ad(block->page.id.space() == index->space);
btr_search_check_free_space_in_heap(index);
table = btr_get_search_table(index);
rec = btr_cur_get_rec(cursor);
#ifdef MYSQL_INDEX_DISABLE_AHI
......@@ -1850,10 +1821,11 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor)
if (locked) {
rw_lock_x_lock(latch);
if (!btr_search_enabled) {
if (!btr_search_enabled || !block->index) {
goto function_exit;
}
table = btr_get_search_table(index);
ha_insert_for_fold(table, ins_fold, block, ins_rec);
}
......@@ -1866,9 +1838,10 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor)
locked = true;
rw_lock_x_lock(latch);
if (!btr_search_enabled) {
if (!btr_search_enabled || !block->index) {
goto function_exit;
}
table = btr_get_search_table(index);
}
if (!left_side) {
......@@ -1886,9 +1859,10 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor)
locked = true;
rw_lock_x_lock(latch);
if (!btr_search_enabled) {
if (!btr_search_enabled || !block->index) {
goto function_exit;
}
table = btr_get_search_table(index);
}
ha_insert_for_fold(table, ins_fold, block, ins_rec);
......@@ -1902,9 +1876,10 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor)
locked = true;
rw_lock_x_lock(latch);
if (!btr_search_enabled) {
if (!btr_search_enabled || !block->index) {
goto function_exit;
}
table = btr_get_search_table(index);
}
if (!left_side) {
......@@ -1940,7 +1915,9 @@ btr_search_hash_table_validate(ulint hash_table_id)
rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs* offsets = offsets_;
btr_search_x_lock_all();
if (!btr_search_enabled) {
btr_search_x_unlock_all();
return(TRUE);
}
......@@ -1950,7 +1927,6 @@ btr_search_hash_table_validate(ulint hash_table_id)
rec_offs_init(offsets_);
btr_search_x_lock_all();
buf_pool_mutex_enter_all();
cell_count = hash_get_n_cells(
......@@ -1967,6 +1943,12 @@ btr_search_hash_table_validate(ulint hash_table_id)
os_thread_yield();
btr_search_x_lock_all();
if (!btr_search_enabled) {
ok = true;
goto func_exit;
}
buf_pool_mutex_enter_all();
ulint curr_cell_count = hash_get_n_cells(
......@@ -2084,6 +2066,12 @@ btr_search_hash_table_validate(ulint hash_table_id)
os_thread_yield();
btr_search_x_lock_all();
if (!btr_search_enabled) {
ok = true;
goto func_exit;
}
buf_pool_mutex_enter_all();
ulint curr_cell_count = hash_get_n_cells(
......@@ -2108,6 +2096,7 @@ btr_search_hash_table_validate(ulint hash_table_id)
}
buf_pool_mutex_exit_all();
func_exit:
btr_search_x_unlock_all();
if (UNIV_LIKELY_NULL(heap)) {
......
......@@ -2698,7 +2698,7 @@ buf_pool_resize()
btr_search_s_unlock_all();
}
btr_search_disable(true);
btr_search_disable();
if (btr_search_disabled) {
ib::info() << "disabled adaptive hash index.";
......@@ -3072,10 +3072,6 @@ buf_pool_resize()
srv_lock_table_size = 5 * (srv_buf_pool_size / UNIV_PAGE_SIZE);
lock_sys_resize(srv_lock_table_size);
/* normalize btr_search_sys */
btr_search_sys_resize(
buf_pool_get_curr_size() / sizeof(void*) / 64);
/* normalize dict_sys */
dict_resize();
......@@ -3100,7 +3096,7 @@ buf_pool_resize()
#ifdef BTR_CUR_HASH_ADAPT
/* enable AHI if needed */
if (btr_search_disabled) {
btr_search_enable();
btr_search_enable(true);
ib::info() << "Re-enabled adaptive hash index.";
}
#endif /* BTR_CUR_HASH_ADAPT */
......
......@@ -3124,10 +3124,10 @@ fil_reinit_space_header_for_table(
dict_table_x_unlock_indexes(table);
row_mysql_unlock_data_dictionary(trx);
DEBUG_SYNC_C("buffer_pool_scan");
/* Lock the search latch in shared mode to prevent user
from disabling AHI during the scan */
btr_search_s_lock_all();
DEBUG_SYNC_C("buffer_pool_scan");
buf_LRU_flush_or_remove_pages(id, NULL);
btr_search_s_unlock_all();
......
......@@ -18134,7 +18134,7 @@ innodb_adaptive_hash_index_update(
if (*(my_bool*) save) {
btr_search_enable();
} else {
btr_search_disable(true);
btr_search_disable();
}
mysql_mutex_lock(&LOCK_global_system_variables);
}
......
......@@ -36,23 +36,17 @@ Created 2/17/1996 Heikki Tuuri
void
btr_search_sys_create(ulint hash_size);
/** Resize hash index hash table.
@param[in] hash_size hash index hash table size */
void
btr_search_sys_resize(ulint hash_size);
/** Frees the adaptive search system at a database shutdown. */
void
btr_search_sys_free();
/** Disable the adaptive hash search system and empty the index.
@param need_mutex need to acquire dict_sys->mutex */
void
btr_search_disable(
bool need_mutex);
/** Enable the adaptive hash search system. */
/** Disable the adaptive hash search system and empty the index. */
void btr_search_disable();
/** Enable the adaptive hash search system.
@param[in] resize Flag to indicate call during buf_pool_resize() */
void
btr_search_enable();
btr_search_enable(bool resize=false);
/*********************************************************************//**
Updates the search info. */
......@@ -209,7 +203,6 @@ btr_get_search_table(const dict_index_t* index);
# define btr_search_move_or_delete_hash_entries(new_block, block, index)
# define btr_search_update_hash_on_insert(cursor)
# define btr_search_update_hash_on_delete(cursor)
# define btr_search_sys_resize(hash_size)
#endif /* BTR_CUR_HASH_ADAPT */
#ifdef BTR_CUR_ADAPT
......
......@@ -1333,7 +1333,8 @@ srv_printf_innodb_monitor(
ibuf_print(file);
#ifdef BTR_CUR_HASH_ADAPT
for (ulint i = 0; i < btr_ahi_parts; ++i) {
btr_search_x_lock_all();
for (ulint i = 0; i < btr_ahi_parts && btr_search_enabled; ++i) {
const hash_table_t* table = btr_search_sys->hash_tables[i];
ut_ad(table->magic_n == HASH_TABLE_MAGIC_N);
......@@ -1357,6 +1358,7 @@ srv_printf_innodb_monitor(
", node heap has " ULINTPF " buffer(s)\n",
table->n_cells, heap->base.count - !heap->free_block);
}
btr_search_x_unlock_all();
fprintf(file,
"%.2f hash searches/s, %.2f non-hash searches/s\n",
......
......@@ -2847,7 +2847,7 @@ innodb_shutdown()
#ifdef BTR_CUR_HASH_ADAPT
if (dict_sys) {
btr_search_disable(true);
btr_search_disable();
}
#endif /* BTR_CUR_HASH_ADAPT */
if (ibuf) {
......
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