diff options
34 files changed, 552 insertions, 903 deletions
diff --git a/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result b/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result deleted file mode 100644 index df36fa82e0f..00000000000 --- a/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result +++ /dev/null @@ -1,32 +0,0 @@ -include/master-slave.inc -[connection master] -create table t1 (a int) engine=innodb; -create table t2 (b longblob) engine=innodb; -create table t3 (c int) engine=innodb; -insert into t2 values (repeat('b',1024*1024)); -insert into t2 select * from t2; -insert into t2 select * from t2; -insert into t2 select * from t2; -insert into t2 select * from t2; -set debug_sync='rm_table_no_locks_before_delete_table SIGNAL nogo WAIT_FOR go EXECUTE 2'; -drop table t1, t2, t3; -connect foo,localhost,root; -set debug_sync='now SIGNAL go'; -kill query CONNECTION_ID; -connection master; -ERROR 70100: Query execution was interrupted -"Tables t2 and t3 should be listed" -SHOW TABLES; -Tables_in_test -t2 -t3 -include/show_binlog_events.inc -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ -connection slave; -drop table t2, t3; -connection master; -set debug_sync='RESET'; -drop table t2, t3; -include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test b/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test deleted file mode 100644 index 281e2a2ab47..00000000000 --- a/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test +++ /dev/null @@ -1,64 +0,0 @@ -# ==== Purpose ==== -# -# Check that when the execution of a DROP TABLE command with single table -# fails it should not be written to the binary log. Also test that when the -# execution of DROP TABLE command with multiple tables fails the command -# should be written into the binary log. -# -# ==== Implementation ==== -# -# Steps: -# 0 - Create tables named t1, t2, t3 -# 1 - Execute DROP TABLE t1,t2,t3 command. -# 2 - Kill the DROP TABLE command while it is trying to drop table 't2'. -# 3 - Verify that tables t2,t3 are present after the DROP command execution -# was interrupted. -# 4 - Check that table 't1' is present in binary log as part of DROP -# command. -# -# ==== References ==== -# -# MDEV-20348: DROP TABLE IF EXISTS killed on master but was replicated. -# - ---source include/have_innodb.inc ---source include/have_debug_sync.inc ---source include/have_binlog_format_statement.inc ---source include/master-slave.inc - -create table t1 (a int) engine=innodb; -create table t2 (b longblob) engine=innodb; -create table t3 (c int) engine=innodb; -insert into t2 values (repeat('b',1024*1024)); -insert into t2 select * from t2; -insert into t2 select * from t2; -insert into t2 select * from t2; -insert into t2 select * from t2; -let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); - -let $id=`select connection_id()`; -set debug_sync='rm_table_no_locks_before_delete_table SIGNAL nogo WAIT_FOR go EXECUTE 2'; -send drop table t1, t2, t3; - -connect foo,localhost,root; -set debug_sync='now SIGNAL go'; -let $wait_condition=select 1 from information_schema.processlist where state like 'debug sync point:%'; -source include/wait_condition.inc; ---replace_result $id CONNECTION_ID -eval kill query $id; - -connection master; -error ER_QUERY_INTERRUPTED; -reap; - ---echo "Tables t2 and t3 should be listed" -SHOW TABLES; ---source include/show_binlog_events.inc ---sync_slave_with_master -drop table t2, t3; - -connection master; -set debug_sync='RESET'; -drop table t2, t3; - -source include/rpl_end.inc; diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index 2049bbd26fa..5f38e623985 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -2,7 +2,7 @@ Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2014, 2019, MariaDB Corporation. +Copyright (c) 2014, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -723,8 +723,10 @@ void btr_page_free(dict_index_t* index, buf_block_t* block, mtr_t* mtr, { ut_ad(mtr_is_block_fix(mtr, block, MTR_MEMO_PAGE_X_FIX, index->table)); #ifdef BTR_CUR_HASH_ADAPT - ut_ad(!block->index || !blob); - ut_ad(!block->index || page_is_leaf(block->frame)); + if (block->index && !block->index->freed()) { + ut_ad(!blob); + ut_ad(page_is_leaf(block->frame)); + } #endif ut_ad(index->space == block->page.id.space()); /* The root page is freed by btr_free_root(). */ @@ -751,7 +753,7 @@ void btr_page_free(dict_index_t* index, buf_block_t* block, mtr_t* mtr, fseg_free_page(seg_header, block->page.id.space(), block->page.id.page_no(), - block->index != NULL, mtr); + mtr); /* The page was marked free in the allocation bitmap, but it should remain exclusively latched until mtr_t::commit() or until it @@ -876,7 +878,7 @@ btr_page_get_father_node_ptr_func( err = btr_cur_search_to_nth_level( index, level + 1, tuple, PAGE_CUR_LE, latch_mode, cursor, 0, - file, line, mtr); + file, line, mtr, 0); if (err != DB_SUCCESS) { ib::warn() << " Error code: " << err @@ -1005,7 +1007,7 @@ static void btr_free_root(buf_block_t* block, mtr_t* mtr, bool invalidate) BTR_FREED_INDEX_ID, mtr); } - while (!fseg_free_step(header, true, mtr)) { + while (!fseg_free_step(header, mtr)) { /* Free the entire segment in small steps. */ } } @@ -1254,7 +1256,7 @@ leaf_loop: fsp0fsp. */ finished = fseg_free_step(root + PAGE_HEADER + PAGE_BTR_SEG_LEAF, - true, &mtr); + &mtr); mtr_commit(&mtr); if (!finished) { @@ -1274,7 +1276,7 @@ top_loop: #endif /* UNIV_BTR_DEBUG */ finished = fseg_free_step_not_header( - root + PAGE_HEADER + PAGE_BTR_SEG_TOP, true, &mtr); + root + PAGE_HEADER + PAGE_BTR_SEG_TOP, &mtr); mtr_commit(&mtr); if (!finished) { @@ -2342,7 +2344,7 @@ btr_insert_on_non_leaf_level_func( dberr_t err = btr_cur_search_to_nth_level( index, level, tuple, PAGE_CUR_LE, BTR_CONT_MODIFY_TREE, - &cursor, 0, file, line, mtr); + &cursor, 0, file, line, mtr, 0); if (err != DB_SUCCESS) { ib::warn() << " Error code: " << err @@ -2363,7 +2365,7 @@ btr_insert_on_non_leaf_level_func( btr_cur_search_to_nth_level(index, level, tuple, PAGE_CUR_RTREE_INSERT, BTR_CONT_MODIFY_TREE, - &cursor, 0, file, line, mtr); + &cursor, 0, file, line, mtr, 0); } ut_ad(cursor.flag == BTR_CUR_BINARY); diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index 2bf657f037f..37ba133cb0f 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -864,8 +864,7 @@ search tuple should be performed in the B-tree. InnoDB does an insert immediately after the cursor. Thus, the cursor may end up on a user record, or on a page infimum record. */ dberr_t -btr_cur_search_to_nth_level( -/*========================*/ +btr_cur_search_to_nth_level_func( dict_index_t* index, /*!< in: index */ ulint level, /*!< in: the tree level of search */ const dtuple_t* tuple, /*!< in: data tuple; NOTE: n_fields_cmp in @@ -887,10 +886,12 @@ btr_cur_search_to_nth_level( to protect the record! */ btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is s- or x-latched, but see also above! */ +#ifdef BTR_CUR_HASH_ADAPT ulint has_search_latch, /*!< in: info on the latch mode the caller currently has on search system: RW_S_LATCH, or 0 */ +#endif /* BTR_CUR_HASH_ADAPT */ const char* file, /*!< in: file name */ unsigned line, /*!< in: line where called */ mtr_t* mtr, /*!< in: mtr */ @@ -1053,6 +1054,7 @@ btr_cur_search_to_nth_level( } #ifdef BTR_CUR_HASH_ADAPT + rw_lock_t* const search_latch = btr_get_search_latch(index); # ifdef UNIV_SEARCH_PERF_STAT info->n_searches++; @@ -1073,8 +1075,7 @@ btr_cur_search_to_nth_level( will have to check it again. */ && btr_search_enabled && !modify_external - && rw_lock_get_writer(btr_get_search_latch(index)) - == RW_LOCK_NOT_LOCKED + && rw_lock_get_writer(search_latch) == RW_LOCK_NOT_LOCKED && btr_search_guess_on_hash(index, info, tuple, mode, latch_mode, cursor, has_search_latch, mtr)) { @@ -1098,10 +1099,12 @@ btr_cur_search_to_nth_level( /* If the hash search did not succeed, do binary search down the tree */ +#ifdef BTR_CUR_HASH_ADAPT if (has_search_latch) { /* Release possible search latch to obey latching order */ - btr_search_s_unlock(index); + rw_lock_s_unlock(search_latch); } +#endif /* BTR_CUR_HASH_ADAPT */ /* Store the position of the tree latch we push to mtr so that we know how to release it when we have latched leaf node(s) */ @@ -2155,9 +2158,11 @@ func_exit: ut_free(prev_tree_savepoints); } +#ifdef BTR_CUR_HASH_ADAPT if (has_search_latch) { - btr_search_s_lock(index); + rw_lock_s_lock(search_latch); } +#endif /* BTR_CUR_HASH_ADAPT */ if (mbr_adj) { /* remember that we will need to adjust parent MBR */ @@ -5746,7 +5751,7 @@ btr_estimate_n_rows_in_range_low( btr_cur_search_to_nth_level(index, 0, tuple1, mode1, BTR_SEARCH_LEAF | BTR_ESTIMATE, &cursor, 0, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, 0); ut_ad(!page_rec_is_infimum(btr_cur_get_rec(&cursor))); @@ -5800,7 +5805,7 @@ btr_estimate_n_rows_in_range_low( btr_cur_search_to_nth_level(index, 0, tuple2, mode2, BTR_SEARCH_LEAF | BTR_ESTIMATE, &cursor, 0, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, 0); const rec_t* rec = btr_cur_get_rec(&cursor); diff --git a/storage/innobase/btr/btr0pcur.cc b/storage/innobase/btr/btr0pcur.cc index 7a62e4c128a..4340c2f32b0 100644 --- a/storage/innobase/btr/btr0pcur.cc +++ b/storage/innobase/btr/btr0pcur.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2019, MariaDB Corporation. +Copyright (c) 2016, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -353,8 +353,11 @@ btr_pcur_restore_position_func( mode = PAGE_CUR_UNSUPP; } - btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode, - cursor, 0, file, line, mtr); + btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode, cursor, +#ifdef BTR_CUR_HASH_ADAPT + 0, +#endif /* BTR_CUR_HASH_ADAPT */ + file, line, mtr); /* Restore the old search mode */ cursor->search_mode = old_mode; diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc index 7671895953e..f1075358ad2 100644 --- a/storage/innobase/btr/btr0sea.cc +++ b/storage/innobase/btr/btr0sea.cc @@ -2,7 +2,7 @@ Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2020, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -141,15 +141,8 @@ static void btr_search_check_free_space_in_heap(dict_index_t* index) { - hash_table_t* table; - mem_heap_t* heap; - - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_S)); - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); - - table = btr_get_search_table(index); - - heap = table->heap; + 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 @@ -157,8 +150,9 @@ btr_search_check_free_space_in_heap(dict_index_t* index) if (heap->free_block == NULL) { buf_block_t* block = buf_block_alloc(NULL); + rw_lock_t* latch = btr_get_search_latch(index); - btr_search_x_lock(index); + rw_lock_x_lock(latch); if (btr_search_enabled && heap->free_block == NULL) { @@ -167,7 +161,7 @@ btr_search_check_free_space_in_heap(dict_index_t* index) buf_block_free(block); } - btr_search_x_unlock(index); + rw_lock_x_unlock(latch); } } @@ -289,18 +283,88 @@ btr_search_disable_ref_count( { dict_index_t* index; - ut_ad(mutex_own(&dict_sys->mutex)); - for (index = dict_table_get_first_index(table); index != NULL; index = dict_table_get_next_index(index)) { - - ut_ad(rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); - index->search_info->ref_count = 0; } } +/** Lazily free detached metadata when removing the last reference. */ +ATTRIBUTE_COLD static void btr_search_lazy_free(dict_index_t *index) +{ + ut_ad(index->freed()); + dict_table_t *table= index->table; + /* Perform the skipped steps of dict_index_remove_from_cache_low(). */ + UT_LIST_REMOVE(table->freed_indexes, index); + rw_lock_free(&index->lock); + dict_mem_index_free(index); + + if (!UT_LIST_GET_LEN(table->freed_indexes) && + !UT_LIST_GET_LEN(table->indexes)) + { + ut_ad(table->id == 0); + dict_mem_table_free(table); + } +} + +/** Clear the adaptive hash index on all pages in the buffer pool. */ +static void buf_pool_clear_hash_index() +{ + ut_ad(btr_search_own_all(RW_LOCK_X)); + ut_ad(!btr_search_enabled); + + std::set<dict_index_t*> garbage; + + for (ulong p = 0; p < srv_buf_pool_instances; p++) + { + buf_pool_t *buf_pool= buf_pool_from_array(p); + buf_chunk_t *chunks= buf_pool->chunks; + buf_chunk_t *chunk= chunks + buf_pool->n_chunks; + + while (--chunk >= chunks) + { + buf_block_t *block= chunk->blocks; + for (ulint i= chunk->size; i--; block++) + { + dict_index_t *index= block->index; + assert_block_ahi_valid(block); + + /* We can clear block->index and block->n_pointers when + btr_search_own_all(RW_LOCK_X); see the comments in buf0buf.h */ + + if (!index) + { +# if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG + ut_a(!block->n_pointers); +# endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ + continue; + } + + ut_d(buf_page_state state= buf_block_get_state(block)); + /* Another thread may have set the state to + BUF_BLOCK_REMOVE_HASH in buf_LRU_block_remove_hashed(). + + The state change in buf_page_realloc() is not observable here, + because in that case we would have !block->index. + + In the end, the entire adaptive hash index will be removed. */ + ut_ad(state == BUF_BLOCK_FILE_PAGE || state == BUF_BLOCK_REMOVE_HASH); +# if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG + block->n_pointers= 0; +# endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ + if (index->freed()) + garbage.insert(index); + block->index= NULL; + } + } + } + + for (std::set<dict_index_t*>::iterator i= garbage.begin(); + i != garbage.end(); i++) + 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 @@ -373,33 +437,6 @@ btr_search_enable() btr_search_x_unlock_all(); } -/** Returns the value of ref_count. The value is protected by latch. -@param[in] info search info -@param[in] index index identifier -@return ref_count value. */ -ulint -btr_search_info_get_ref_count( - btr_search_t* info, - dict_index_t* index) -{ - ulint ret = 0; - - if (!btr_search_enabled) { - return(ret); - } - - ut_ad(info); - - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_S)); - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); - - btr_search_s_lock(index); - ret = info->ref_count; - btr_search_s_unlock(index); - - return(ret); -} - /** Updates the search info of an index about hash successes. NOTE that info is NOT protected by any semaphore, to save CPU time! Do not assume its fields are consistent. @@ -415,8 +452,8 @@ btr_search_info_update_hash( ulint n_unique; int cmp; - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_S)); - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); + ut_ad(!rw_lock_own_flagged(btr_get_search_latch(index), + RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)); if (dict_index_is_ibuf(index)) { /* So many deletes are performed on an insert buffer tree @@ -601,28 +638,25 @@ btr_search_update_hash_ref( buf_block_t* block, const btr_cur_t* cursor) { - dict_index_t* index; - ulint fold; - rec_t* rec; - ut_ad(cursor->flag == BTR_CUR_HASH_FAIL); - ut_ad(rw_lock_own(btr_get_search_latch(cursor->index), RW_LOCK_X)); + ut_ad(rw_lock_own_flagged(&block->lock, RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)); ut_ad(page_align(btr_cur_get_rec(cursor)) == block->frame); ut_ad(page_is_leaf(block->frame)); assert_block_ahi_valid(block); - index = block->index; + dict_index_t* index = block->index; if (!index) { - return; } ut_ad(block->page.id.space() == index->space); ut_a(index == cursor->index); - ut_a(!dict_index_is_ibuf(index)); + ut_ad(!dict_index_is_ibuf(index)); + rw_lock_t* const latch = btr_get_search_latch(index); + rw_lock_x_lock(latch); if ((info->n_hash_potential > 0) && (block->curr_n_fields == info->n_fields) @@ -632,28 +666,30 @@ btr_search_update_hash_ref( rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); - rec = btr_cur_get_rec(cursor); + const rec_t* rec = btr_cur_get_rec(cursor); if (!page_rec_is_user_rec(rec)) { - - return; + goto func_exit; } - fold = rec_fold(rec, - rec_get_offsets(rec, index, offsets_, true, - ULINT_UNDEFINED, &heap), - block->curr_n_fields, - block->curr_n_bytes, index->id); + ulint fold = rec_fold( + rec, + rec_get_offsets(rec, index, offsets_, true, + ULINT_UNDEFINED, &heap), + block->curr_n_fields, + block->curr_n_bytes, index->id); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } - ut_ad(rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); ha_insert_for_fold(btr_get_search_table(index), fold, block, rec); MONITOR_INC(MONITOR_ADAPTIVE_HASH_ROW_ADDED); } + +func_exit: + rw_lock_x_unlock(latch); } /** Updates the search info. @@ -692,12 +728,7 @@ btr_search_info_update_slow( #ifdef UNIV_SEARCH_PERF_STAT btr_search_n_hash_fail++; #endif /* UNIV_SEARCH_PERF_STAT */ - - btr_search_x_lock(cursor->index); - btr_search_update_hash_ref(info, block, cursor); - - btr_search_x_unlock(cursor->index); } if (build_index) { @@ -898,7 +929,6 @@ btr_search_guess_on_hash( ulint has_search_latch, mtr_t* mtr) { - const rec_t* rec; ulint fold; index_id_t index_id; #ifdef notdefined @@ -944,33 +974,28 @@ btr_search_guess_on_hash( cursor->fold = fold; cursor->flag = BTR_CUR_HASH; + rw_lock_t* const latch = btr_get_search_latch(index); + if (!has_search_latch) { - btr_search_s_lock(index); + rw_lock_s_lock(latch); if (!btr_search_enabled) { - btr_search_s_unlock(index); - +fail: + if (!has_search_latch) { + rw_lock_s_unlock(latch); + } btr_search_failure(info, cursor); - return(FALSE); } } - ut_ad(rw_lock_get_writer(btr_get_search_latch(index)) != RW_LOCK_X); - ut_ad(rw_lock_get_reader_count(btr_get_search_latch(index)) > 0); - - rec = (rec_t*) ha_search_and_get_data( - btr_get_search_table(index), fold); + ut_ad(rw_lock_own(latch, RW_LOCK_S)); - if (rec == NULL) { + const rec_t* rec = static_cast<const rec_t*>( + ha_search_and_get_data(btr_get_search_table(index), fold)); - if (!has_search_latch) { - btr_search_s_unlock(index); - } - - btr_search_failure(info, cursor); - - return(FALSE); + if (!rec) { + goto fail; } buf_block_t* block = buf_block_from_ahi(rec); @@ -980,32 +1005,34 @@ btr_search_guess_on_hash( if (!buf_page_get_known_nowait( latch_mode, block, BUF_MAKE_YOUNG, __FILE__, __LINE__, mtr)) { - - if (!has_search_latch) { - btr_search_s_unlock(index); - } - - btr_search_failure(info, cursor); - - return(FALSE); + goto fail; } - btr_search_s_unlock(index); + const bool fail = index != block->index + && index_id == block->index->id; + ut_a(!fail || block->index->freed()); + rw_lock_s_unlock(latch); buf_block_dbg_add_level(block, SYNC_TREE_NODE_FROM_HASH); + if (UNIV_UNLIKELY(fail)) { + btr_search_drop_page_hash_index(block); + goto fail_and_release_page; + } + } else if (UNIV_UNLIKELY(index != block->index + && index_id == block->index->id)) { + ut_a(block->index->freed()); + goto fail_and_release_page; } if (buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE) { ut_ad(buf_block_get_state(block) == BUF_BLOCK_REMOVE_HASH); - +fail_and_release_page: if (!has_search_latch) { - btr_leaf_page_release(block, latch_mode, mtr); } btr_search_failure(info, cursor); - return(FALSE); } @@ -1024,14 +1051,7 @@ btr_search_guess_on_hash( || !btr_search_check_guess(cursor, has_search_latch, tuple, mode, mtr)) { - - if (!has_search_latch) { - btr_leaf_page_release(block, latch_mode, mtr); - } - - btr_search_failure(info, cursor); - - return(FALSE); + goto fail_and_release_page; } if (info->n_hash_potential < BTR_SEARCH_BUILD_LIMIT + 5) { @@ -1115,30 +1135,26 @@ btr_search_drop_page_hash_index(buf_block_t* block) ulint* folds; ulint i; mem_heap_t* heap; - const dict_index_t* index; rec_offs* offsets; rw_lock_t* latch; - btr_search_t* info; retry: - /* Do a dirty check on block->index, return if the block is - not in the adaptive hash index. */ - index = block->index; /* This debug check uses a dirty read that could theoretically cause false positives while buf_pool_clear_hash_index() is executing. */ assert_block_ahi_valid(block); - if (index == NULL) { + if (!block->index) { return; } ut_ad(block->page.buf_fix_count == 0 || buf_block_get_state(block) == BUF_BLOCK_REMOVE_HASH || rw_lock_own_flagged(&block->lock, - RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)); + RW_LOCK_FLAG_X | RW_LOCK_FLAG_S + | RW_LOCK_FLAG_SX)); ut_ad(page_is_leaf(block->frame)); - /* We must not dereference index here, because it could be freed + /* We must not dereference block->index here, because it could be freed if (index->table->n_ref_count == 0 && !mutex_own(&dict_sys->mutex)). Determine the ahi_slot based on the block contents. */ @@ -1156,18 +1172,12 @@ retry: rw_lock_s_lock(latch); assert_block_ahi_valid(block); - if (block->index == NULL) { + if (!block->index) { rw_lock_s_unlock(latch); return; } - /* The index associated with a block must remain the - same, because we are holding block->lock or the block is - not accessible by other threads (BUF_BLOCK_REMOVE_HASH), - or the index is not accessible to other threads - (buf_fix_count == 0 when DROP TABLE or similar is executing - buf_LRU_drop_page_hash_for_tablespace()). */ - ut_a(index == block->index); + dict_index_t* index = block->index; #ifdef MYSQL_INDEX_DISABLE_AHI ut_ad(!index->disable_ahi); #endif @@ -1176,7 +1186,7 @@ retry: ut_ad(index->space == FIL_NULL || block->page.id.space() == index->space); ut_a(index_id == index->id); - ut_a(!dict_index_is_ibuf(index)); + ut_ad(!dict_index_is_ibuf(index)); #ifdef UNIV_DEBUG switch (dict_index_get_online_status(index)) { case ONLINE_INDEX_CREATION: @@ -1281,9 +1291,14 @@ next_rec: folds[i], page); } - info = btr_search_get_info(block->index); - ut_a(info->ref_count > 0); - info->ref_count--; + switch (index->search_info->ref_count--) { + case 0: + ut_error; + case 1: + if (index->freed()) { + btr_search_lazy_free(index); + } + } block->index = NULL; @@ -1383,27 +1398,27 @@ btr_search_build_page_hash_index( rec_offs_init(offsets_); ut_ad(index); ut_ad(block->page.id.space() == index->space); - ut_a(!dict_index_is_ibuf(index)); + ut_ad(!dict_index_is_ibuf(index)); ut_ad(page_is_leaf(block->frame)); - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); ut_ad(rw_lock_own_flagged(&block->lock, RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)); + ut_ad(block->page.id.page_no() >= 3); - btr_search_s_lock(index); + rw_lock_t* const latch = btr_get_search_latch(index); + rw_lock_s_lock(latch); table = btr_get_search_table(index); page = buf_block_get_frame(block); - if (block->index && ((block->curr_n_fields != n_fields) - || (block->curr_n_bytes != n_bytes) - || (block->curr_left_side != left_side))) { - - btr_search_s_unlock(index); + const bool must_drop = block->index + && ((block->curr_n_fields != n_fields) + || (block->curr_n_bytes != n_bytes) + || (block->curr_left_side != left_side)); + rw_lock_s_unlock(latch); + if (must_drop) { btr_search_drop_page_hash_index(block); - } else { - btr_search_s_unlock(index); } /* Check that the values for hash index build are sensible */ @@ -1495,7 +1510,7 @@ btr_search_build_page_hash_index( btr_search_check_free_space_in_heap(index); - btr_search_x_lock(index); + rw_lock_x_lock(latch); if (!btr_search_enabled) { goto exit_func; @@ -1533,7 +1548,7 @@ btr_search_build_page_hash_index( MONITOR_INC_VALUE(MONITOR_ADAPTIVE_HASH_ROW_ADDED, n_cached); exit_func: assert_block_ahi_valid(block); - btr_search_x_unlock(index); + rw_lock_x_unlock(latch); ut_free(folds); ut_free(recs); @@ -1566,21 +1581,19 @@ btr_search_move_or_delete_hash_entries( ut_ad(rw_lock_own(&(block->lock), RW_LOCK_X)); ut_ad(rw_lock_own(&(new_block->lock), RW_LOCK_X)); - btr_search_s_lock(index); + rw_lock_t* const latch = btr_get_search_latch(index); + rw_lock_s_lock(latch); ut_a(!new_block->index || new_block->index == index); ut_a(!block->index || block->index == index); - ut_a(!(new_block->index || block->index) - || !dict_index_is_ibuf(index)); + ut_ad(!(new_block->index || block->index) + || !dict_index_is_ibuf(index)); assert_block_ahi_valid(block); assert_block_ahi_valid(new_block); if (new_block->index) { - - btr_search_s_unlock(index); - + rw_lock_s_unlock(latch); btr_search_drop_page_hash_index(block); - return; } @@ -1593,7 +1606,7 @@ btr_search_move_or_delete_hash_entries( new_block->n_bytes = block->curr_n_bytes; new_block->left_side = left_side; - btr_search_s_unlock(index); + rw_lock_s_unlock(latch); ut_a(n_fields > 0 || n_bytes > 0); @@ -1605,7 +1618,7 @@ btr_search_move_or_delete_hash_entries( return; } - btr_search_s_unlock(index); + rw_lock_s_unlock(latch); } /** Updates the page hash index when a single record is deleted from a page. @@ -1647,7 +1660,7 @@ btr_search_update_hash_on_delete(btr_cur_t* cursor) ut_ad(block->page.id.space() == index->space); ut_a(index == cursor->index); ut_a(block->curr_n_fields > 0 || block->curr_n_bytes > 0); - ut_a(!dict_index_is_ibuf(index)); + ut_ad(!dict_index_is_ibuf(index)); table = btr_get_search_table(index); @@ -1660,7 +1673,8 @@ btr_search_update_hash_on_delete(btr_cur_t* cursor) mem_heap_free(heap); } - btr_search_x_lock(index); + rw_lock_t* const latch = btr_get_search_latch(index); + rw_lock_x_lock(latch); assert_block_ahi_valid(block); if (block->index) { @@ -1676,7 +1690,7 @@ btr_search_update_hash_on_delete(btr_cur_t* cursor) assert_block_ahi_valid(block); } - btr_search_x_unlock(index); + rw_lock_x_unlock(latch); } /** Updates the page hash index when a single record is inserted on a page. @@ -1712,9 +1726,10 @@ btr_search_update_hash_node_on_insert(btr_cur_t* cursor) } ut_a(cursor->index == index); - ut_a(!dict_index_is_ibuf(index)); + ut_ad(!dict_index_is_ibuf(index)); - btr_search_x_lock(index); + rw_lock_t* const latch = btr_get_search_latch(index); + rw_lock_x_lock(latch); if (!block->index) { @@ -1738,10 +1753,9 @@ btr_search_update_hash_node_on_insert(btr_cur_t* cursor) func_exit: assert_block_ahi_valid(block); - btr_search_x_unlock(index); + rw_lock_x_unlock(latch); } else { - btr_search_x_unlock(index); - + rw_lock_x_unlock(latch); btr_search_update_hash_on_insert(cursor); } } @@ -1765,8 +1779,6 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor) ulint next_fold = 0; /* remove warning (??? bug ???) */ ulint n_fields; ulint n_bytes; - ibool left_side; - ibool locked = FALSE; mem_heap_t* heap = NULL; rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs* offsets = offsets_; @@ -1803,11 +1815,11 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor) ut_a(!index->disable_ahi); #endif ut_a(index == cursor->index); - ut_a(!dict_index_is_ibuf(index)); + ut_ad(!dict_index_is_ibuf(index)); n_fields = block->curr_n_fields; n_bytes = block->curr_n_bytes; - left_side = block->curr_left_side; + const bool left_side = block->curr_left_side; ins_rec = page_rec_get_next_const(rec); next_rec = page_rec_get_next_const(ins_rec); @@ -1824,17 +1836,18 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor) n_bytes, index->id); } + rw_lock_t* const latch = btr_get_search_latch(index); + bool locked = false; + if (!page_rec_is_infimum(rec)) { offsets = rec_get_offsets( rec, index, offsets, true, btr_search_get_n_fields(n_fields, n_bytes), &heap); fold = rec_fold(rec, offsets, n_fields, n_bytes, index->id); } else { - if (left_side) { - - btr_search_x_lock(index); - - locked = TRUE; + locked = left_side; + if (locked) { + rw_lock_x_lock(latch); if (!btr_search_enabled) { goto function_exit; @@ -1849,10 +1862,8 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor) if (fold != ins_fold) { if (!locked) { - - btr_search_x_lock(index); - - locked = TRUE; + locked = true; + rw_lock_x_lock(latch); if (!btr_search_enabled) { goto function_exit; @@ -1870,11 +1881,9 @@ check_next_rec: if (page_rec_is_supremum(next_rec)) { if (!left_side) { - if (!locked) { - btr_search_x_lock(index); - - locked = TRUE; + locked = true; + rw_lock_x_lock(latch); if (!btr_search_enabled) { goto function_exit; @@ -1888,12 +1897,9 @@ check_next_rec: } if (ins_fold != next_fold) { - if (!locked) { - - btr_search_x_lock(index); - - locked = TRUE; + locked = true; + rw_lock_x_lock(latch); if (!btr_search_enabled) { goto function_exit; @@ -1912,7 +1918,7 @@ function_exit: mem_heap_free(heap); } if (locked) { - btr_search_x_unlock(index); + rw_lock_x_unlock(latch); } } @@ -2019,7 +2025,7 @@ btr_search_hash_table_validate(ulint hash_table_id) == BUF_BLOCK_REMOVE_HASH); } - ut_a(!dict_index_is_ibuf(block->index)); + ut_ad(!dict_index_is_ibuf(block->index)); ut_ad(block->page.id.space() == block->index->space); page_index_id = btr_page_get_index_id(block->frame); diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 11c2e0e5ad5..3beae8982b6 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -3165,66 +3165,6 @@ DECLARE_THREAD(buf_resize_thread)(void*) OS_THREAD_DUMMY_RETURN; } -#ifdef BTR_CUR_HASH_ADAPT -/** Clear the adaptive hash index on all pages in the buffer pool. */ -void -buf_pool_clear_hash_index() -{ - ulint p; - - ut_ad(btr_search_own_all(RW_LOCK_X)); - ut_ad(!buf_pool_resizing); - ut_ad(!btr_search_enabled); - - for (p = 0; p < srv_buf_pool_instances; p++) { - buf_pool_t* buf_pool = buf_pool_from_array(p); - buf_chunk_t* chunks = buf_pool->chunks; - buf_chunk_t* chunk = chunks + buf_pool->n_chunks; - - while (--chunk >= chunks) { - buf_block_t* block = chunk->blocks; - ulint i = chunk->size; - - for (; i--; block++) { - dict_index_t* index = block->index; - assert_block_ahi_valid(block); - - /* We can set block->index = NULL - and block->n_pointers = 0 - when btr_search_own_all(RW_LOCK_X); - see the comments in buf0buf.h */ - - if (!index) { -# if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG - ut_a(!block->n_pointers); -# endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ - continue; - } - - ut_d(buf_page_state state - = buf_block_get_state(block)); - /* Another thread may have set the - state to BUF_BLOCK_REMOVE_HASH in - buf_LRU_block_remove_hashed(). - - The state change in buf_page_realloc() - is not observable here, because in - that case we would have !block->index. - - In the end, the entire adaptive hash - index will be removed. */ - ut_ad(state == BUF_BLOCK_FILE_PAGE - || state == BUF_BLOCK_REMOVE_HASH); -# if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG - block->n_pointers = 0; -# endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ - block->index = NULL; - } - } - } -} -#endif /* BTR_CUR_HASH_ADAPT */ - /********************************************************************//** Relocate a buffer control block. Relocates the block on the LRU list and in buf_pool->page_hash. Does not relocate bpage->list. @@ -4207,7 +4147,7 @@ static buf_block_t* buf_page_mtr_lock(buf_block_t *block, { case RW_NO_LATCH: fix_type= MTR_MEMO_BUF_FIX; - break; + goto done; case RW_S_LATCH: rw_lock_s_lock_inline(&block->lock, 0, file, line); fix_type= MTR_MEMO_PAGE_S_FIX; @@ -4223,6 +4163,15 @@ static buf_block_t* buf_page_mtr_lock(buf_block_t *block, break; } +#ifdef BTR_CUR_HASH_ADAPT + { + dict_index_t *index= block->index; + if (index && index->freed()) + btr_search_drop_page_hash_index(block); + } +#endif /* BTR_CUR_HASH_ADAPT */ + +done: mtr_memo_push(mtr, block, fix_type); return block; } @@ -4540,6 +4489,7 @@ evict_from_pool: buf_pool_mutex_exit(buf_pool); return(NULL); } + break; case BUF_BLOCK_ZIP_PAGE: @@ -5087,9 +5037,11 @@ buf_page_get_known_nowait( buf_pool = buf_pool_from_block(block); +#ifdef BTR_CUR_HASH_ADAPT if (mode == BUF_MAKE_YOUNG) { buf_page_make_young_if_needed(&block->page); } +#endif /* BTR_CUR_HASH_ADAPT */ ut_ad(!ibuf_inside(mtr) || mode == BUF_KEEP_OLD); @@ -5132,9 +5084,12 @@ buf_page_get_known_nowait( deleting a record from SYS_INDEXES. This check will be skipped in recv_recover_page() as well. */ - buf_page_mutex_enter(block); - ut_a(!block->page.file_page_was_freed); - buf_page_mutex_exit(block); +# ifdef BTR_CUR_HASH_ADAPT + ut_ad(!block->page.file_page_was_freed + || (block->index && block->index->freed())); +# else /* BTR_CUR_HASH_ADAPT */ + ut_ad(!block->page.file_page_was_freed); +# endif /* BTR_CUR_HASH_ADAPT */ } #endif /* UNIV_DEBUG */ @@ -5627,6 +5582,12 @@ buf_page_create( rw_lock_x_unlock(hash_lock); buf_block_free(free_block); +#ifdef BTR_CUR_HASH_ADAPT + if (block->page.state == BUF_BLOCK_FILE_PAGE + && UNIV_LIKELY_NULL(block->index)) { + btr_search_drop_page_hash_index(block); + } +#endif /* BTR_CUR_HASH_ADAPT */ if (!recv_recovery_is_on()) { return buf_page_get_with_no_latch(page_id, page_size, diff --git a/storage/innobase/buf/buf0lru.cc b/storage/innobase/buf/buf0lru.cc index 9feebd9a08e..6e2dba77513 100644 --- a/storage/innobase/buf/buf0lru.cc +++ b/storage/innobase/buf/buf0lru.cc @@ -222,166 +222,6 @@ buf_LRU_evict_from_unzip_LRU( } #ifdef BTR_CUR_HASH_ADAPT -/** Attempts to drop page hash index on a batch of pages belonging to a -particular space id. -@param[in] space_id space id -@param[in] arr array of page_no -@param[in] count number of entries in array */ -static -void -buf_LRU_drop_page_hash_batch(ulint space_id, const ulint* arr, ulint count) -{ - ut_ad(count <= BUF_LRU_DROP_SEARCH_SIZE); - - for (const ulint* const end = arr + count; arr != end; ) { - /* While our only caller - buf_LRU_drop_page_hash_for_tablespace() - is being executed for DROP TABLE or similar, - the table cannot be evicted from the buffer pool. */ - btr_search_drop_page_hash_when_freed( - page_id_t(space_id, *arr++)); - } -} - -/******************************************************************//** -When doing a DROP TABLE/DISCARD TABLESPACE we have to drop all page -hash index entries belonging to that table. This function tries to -do that in batch. Note that this is a 'best effort' attempt and does -not guarantee that ALL hash entries will be removed. */ -static -void -buf_LRU_drop_page_hash_for_tablespace( -/*==================================*/ - buf_pool_t* buf_pool, /*!< in: buffer pool instance */ - ulint id) /*!< in: space id */ -{ - ulint* page_arr = static_cast<ulint*>(ut_malloc_nokey( - sizeof(ulint) * BUF_LRU_DROP_SEARCH_SIZE)); - - ulint num_entries = 0; - - buf_pool_mutex_enter(buf_pool); - -scan_again: - for (buf_page_t* bpage = UT_LIST_GET_LAST(buf_pool->LRU); - bpage != NULL; - /* No op */) { - - buf_page_t* prev_bpage = UT_LIST_GET_PREV(LRU, bpage); - - ut_a(buf_page_in_file(bpage)); - - if (buf_page_get_state(bpage) != BUF_BLOCK_FILE_PAGE - || bpage->id.space() != id - || bpage->io_fix != BUF_IO_NONE) { - /* Compressed pages are never hashed. - Skip blocks of other tablespaces. - Skip I/O-fixed blocks (to be dealt with later). */ -next_page: - bpage = prev_bpage; - continue; - } - - buf_block_t* block = reinterpret_cast<buf_block_t*>(bpage); - - mutex_enter(&block->mutex); - - /* This debug check uses a dirty read that could - theoretically cause false positives while - buf_pool_clear_hash_index() is executing. - (Other conflicting access paths to the adaptive hash - index should not be possible, because when a - tablespace is being discarded or dropped, there must - be no concurrect access to the contained tables.) */ - assert_block_ahi_valid(block); - - bool skip = bpage->buf_fix_count > 0 || !block->index; - - mutex_exit(&block->mutex); - - if (skip) { - /* Skip this block, because there are - no adaptive hash index entries - pointing to it, or because we cannot - drop them due to the buffer-fix. */ - goto next_page; - } - - /* Store the page number so that we can drop the hash - index in a batch later. */ - page_arr[num_entries] = bpage->id.page_no(); - ut_a(num_entries < BUF_LRU_DROP_SEARCH_SIZE); - ++num_entries; - - if (num_entries < BUF_LRU_DROP_SEARCH_SIZE) { - goto next_page; - } - - /* Array full. We release the buf_pool->mutex to obey - the latching order. */ - buf_pool_mutex_exit(buf_pool); - - buf_LRU_drop_page_hash_batch(id, page_arr, num_entries); - - num_entries = 0; - - buf_pool_mutex_enter(buf_pool); - - /* Note that we released the buf_pool mutex above - after reading the prev_bpage during processing of a - page_hash_batch (i.e.: when the array was full). - Because prev_bpage could belong to a compressed-only - block, it may have been relocated, and thus the - pointer cannot be trusted. Because bpage is of type - buf_block_t, it is safe to dereference. - - bpage can change in the LRU list. This is OK because - this function is a 'best effort' to drop as many - search hash entries as possible and it does not - guarantee that ALL such entries will be dropped. */ - - /* If, however, bpage has been removed from LRU list - to the free list then we should restart the scan. - bpage->state is protected by buf_pool mutex. */ - if (bpage != NULL - && buf_page_get_state(bpage) != BUF_BLOCK_FILE_PAGE) { - - goto scan_again; - } - } - - buf_pool_mutex_exit(buf_pool); - - /* Drop any remaining batch of search hashed pages. */ - buf_LRU_drop_page_hash_batch(id, page_arr, num_entries); - ut_free(page_arr); -} - -/** Try to drop the adaptive hash index for a tablespace. -@param[in,out] table table -@return whether anything was dropped */ -bool buf_LRU_drop_page_hash_for_tablespace(dict_table_t* table) -{ - for (dict_index_t* index = dict_table_get_first_index(table); - index != NULL; - index = dict_table_get_next_index(index)) { - if (btr_search_info_get_ref_count(btr_search_get_info(index), - index)) { - goto drop_ahi; - } - } - - return false; -drop_ahi: - ulint id = table->space; - for (ulint i = 0; i < srv_buf_pool_instances; i++) { - buf_LRU_drop_page_hash_for_tablespace(buf_pool_from_array(i), - id); - } - - return true; -} - /******************************************************************//** While flushing (or removing dirty) pages from a tablespace we don't want to hog the CPU and resources. Release the buffer pool and block diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc index 54d2eca245f..1a3d6c51d98 100644 --- a/storage/innobase/dict/dict0crea.cc +++ b/storage/innobase/dict/dict0crea.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -26,7 +26,9 @@ Created 1/8/1996 Heikki Tuuri #include "dict0crea.h" #include "btr0pcur.h" -#include "btr0btr.h" +#ifdef BTR_CUR_HASH_ADAPT +# include "btr0sea.h" +#endif /* BTR_CUR_HASH_ADAPT */ #include "page0page.h" #include "mach0data.h" #include "dict0boot.h" @@ -1528,6 +1530,9 @@ dict_create_index_step( &node->table->fts->cache->init_lock); } +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!node->index->search_info->ref_count); +#endif /* BTR_CUR_HASH_ADAPT */ dict_index_remove_from_cache(node->table, node->index); node->index = NULL; diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 8f94ccb4c63..cff213dea3d 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -1361,25 +1361,12 @@ dict_table_can_be_evicted( } #ifdef BTR_CUR_HASH_ADAPT + /* We cannot really evict the table if adaptive hash + index entries are pointing to any of its indexes. */ for (dict_index_t* index = dict_table_get_first_index(table); index != NULL; index = dict_table_get_next_index(index)) { - - btr_search_t* info = btr_search_get_info(index); - - /* We are not allowed to free the in-memory index - struct dict_index_t until all entries in the adaptive - hash index that point to any of the page belonging to - his b-tree index are dropped. This is so because - dropping of these entries require access to - dict_index_t struct. To avoid such scenario we keep - a count of number of such pages in the search_info and - only free the dict_index_t struct when this count - drops to zero. - - See also: dict_index_remove_from_cache_low() */ - - if (btr_search_info_get_ref_count(info, index) > 0) { + if (index->n_ahi_pages()) { return(FALSE); } } @@ -1391,6 +1378,71 @@ dict_table_can_be_evicted( return(FALSE); } +#ifdef BTR_CUR_HASH_ADAPT +/** @return a clone of this */ +dict_index_t *dict_index_t::clone() const +{ + ut_ad(n_fields); + ut_ad(!(type & (DICT_IBUF | DICT_SPATIAL | DICT_FTS))); + ut_ad(online_status == ONLINE_INDEX_COMPLETE); + ut_ad(is_committed()); + ut_ad(!is_dummy); + ut_ad(!parser); + ut_ad(!index_fts_syncing); + ut_ad(!online_log); + ut_ad(!rtr_track); + + const size_t size= sizeof *this + n_fields * sizeof(*fields) + +#ifdef BTR_CUR_ADAPT + sizeof *search_info + +#endif + 1 + strlen(name) + + n_uniq * (sizeof *stat_n_diff_key_vals + + sizeof *stat_n_sample_sizes + + sizeof *stat_n_non_null_key_vals); + + mem_heap_t* heap= mem_heap_create(size); + dict_index_t *index= static_cast<dict_index_t*>(mem_heap_dup(heap, this, + sizeof *this)); + *index= *this; + rw_lock_create(index_tree_rw_lock_key, &index->lock, SYNC_INDEX_TREE); + index->heap= heap; + index->name= mem_heap_strdup(heap, name); + index->fields= static_cast<dict_field_t*> + (mem_heap_dup(heap, fields, n_fields * sizeof *fields)); +#ifdef BTR_CUR_ADAPT + index->search_info= btr_search_info_create(index->heap); +#endif /* BTR_CUR_ADAPT */ + index->stat_n_diff_key_vals= static_cast<ib_uint64_t*> + (mem_heap_zalloc(heap, n_uniq * sizeof *stat_n_diff_key_vals)); + index->stat_n_sample_sizes= static_cast<ib_uint64_t*> + (mem_heap_zalloc(heap, n_uniq * sizeof *stat_n_sample_sizes)); + index->stat_n_non_null_key_vals= static_cast<ib_uint64_t*> + (mem_heap_zalloc(heap, n_uniq * sizeof *stat_n_non_null_key_vals)); + memset(&index->zip_pad, 0, sizeof index->zip_pad); + return index; +} + +/** Clone this index for lazy dropping of the adaptive hash. +@return this or a clone */ +dict_index_t *dict_index_t::clone_if_needed() +{ + if (!search_info->ref_count) + return this; + dict_index_t *prev= UT_LIST_GET_PREV(indexes, this); + + UT_LIST_REMOVE(table->indexes, this); + UT_LIST_ADD_LAST(table->freed_indexes, this); + dict_index_t *index= clone(); + set_freed(); + if (prev) + UT_LIST_INSERT_AFTER(table->indexes, prev, index); + else + UT_LIST_ADD_FIRST(table->indexes, index); + return index; +} +#endif /* BTR_CUR_HASH_ADAPT */ + /**********************************************************************//** Make room in the table cache by evicting an unused table. The unused table should not be part of FK relationship and currently not used in any user @@ -2081,6 +2133,14 @@ dict_table_remove_from_cache_low( UT_DELETE(table->vc_templ); } +#ifdef BTR_CUR_HASH_ADAPT + if (UNIV_UNLIKELY(UT_LIST_GET_LEN(table->freed_indexes) != 0)) { + table->vc_templ = NULL; + table->id = 0; + return; + } +#endif /* BTR_CUR_HASH_ADAPT */ + dict_mem_table_free(table); } @@ -2310,6 +2370,8 @@ dict_index_remove_from_cache_low( ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); ut_ad(mutex_own(&dict_sys->mutex)); + ut_ad(table->id); + ut_ad(!index->freed()); /* No need to acquire the dict_index_t::lock here because there can't be any active operations on this index (or table). */ @@ -2319,13 +2381,22 @@ dict_index_remove_from_cache_low( row_log_free(index->online_log); } + /* Remove the index from the list of indexes of the table */ + UT_LIST_REMOVE(table->indexes, index); + + /* The index is being dropped, remove any compression stats for it. */ + if (!lru_evict && DICT_TF_GET_ZIP_SSIZE(index->table->flags)) { + mutex_enter(&page_zip_stat_per_index_mutex); + page_zip_stat_per_index.erase(index->id); + mutex_exit(&page_zip_stat_per_index_mutex); + } + + /* Remove the index from affected virtual column index list */ + index->detach_columns(); + #ifdef BTR_CUR_HASH_ADAPT /* We always create search info whether or not adaptive hash index is enabled or not. */ - btr_search_t* info = btr_search_get_info(index); - ulint retries = 0; - ut_ad(info); - /* We are not allowed to free the in-memory index struct dict_index_t until all entries in the adaptive hash index that point to any of the page belonging to his b-tree index @@ -2335,31 +2406,15 @@ dict_index_remove_from_cache_low( only free the dict_index_t struct when this count drops to zero. See also: dict_table_can_be_evicted() */ - do { - if (!btr_search_info_get_ref_count(info, index) - || !buf_LRU_drop_page_hash_for_tablespace(table)) { - break; - } - - ut_a(++retries < 10000); - } while (srv_shutdown_state == SRV_SHUTDOWN_NONE || !lru_evict); + if (index->n_ahi_pages()) { + index->set_freed(); + UT_LIST_ADD_LAST(table->freed_indexes, index); + return; + } #endif /* BTR_CUR_HASH_ADAPT */ rw_lock_free(&index->lock); - /* The index is being dropped, remove any compression stats for it. */ - if (!lru_evict && DICT_TF_GET_ZIP_SSIZE(index->table->flags)) { - mutex_enter(&page_zip_stat_per_index_mutex); - page_zip_stat_per_index.erase(index->id); - mutex_exit(&page_zip_stat_per_index_mutex); - } - - /* Remove the index from the list of indexes of the table */ - UT_LIST_REMOVE(table->indexes, index); - - /* Remove the index from affected virtual column index list */ - index->detach_columns(); - dict_mem_index_free(index); } @@ -5698,7 +5753,7 @@ dict_set_corrupted( btr_cur_search_to_nth_level(sys_index, 0, tuple, PAGE_CUR_LE, BTR_MODIFY_LEAF, - &cursor, 0, __FILE__, __LINE__, &mtr); + &cursor, 0, __FILE__, __LINE__, &mtr, 0); if (cursor.low_match == dtuple_get_n_fields(tuple)) { /* UPDATE SYS_INDEXES SET TYPE=index->type @@ -5800,7 +5855,7 @@ dict_index_set_merge_threshold( btr_cur_search_to_nth_level(sys_index, 0, tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, - &cursor, 0, __FILE__, __LINE__, &mtr); + &cursor, 0, __FILE__, __LINE__, &mtr, 0); if (cursor.up_match == dtuple_get_n_fields(tuple) && rec_get_n_fields_old(btr_cur_get_rec(&cursor)) diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index 21a3c743f52..8327318e46c 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -126,6 +126,9 @@ dict_mem_table_create( lock_table_lock_list_init(&table->locks); UT_LIST_INIT(table->indexes, &dict_index_t::indexes); +#ifdef BTR_CUR_HASH_ADAPT + UT_LIST_INIT(table->freed_indexes, &dict_index_t::indexes); +#endif /* BTR_CUR_HASH_ADAPT */ table->heap = heap; @@ -181,6 +184,10 @@ dict_mem_table_free( { ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + ut_ad(UT_LIST_GET_LEN(table->indexes) == 0); +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(UT_LIST_GET_LEN(table->freed_indexes) == 0); +#endif /* BTR_CUR_HASH_ADAPT */ ut_d(table->cached = FALSE); if (dict_table_has_fts_index(table) diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index dab30a218e6..0da77dfdff7 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -424,6 +424,9 @@ dict_stats_table_clone_create( dict_table_stats_latch_create(t, false); UT_LIST_INIT(t->indexes, &dict_index_t::indexes); +#ifdef BTR_CUR_HASH_ADAPT + UT_LIST_INIT(t->freed_indexes, &dict_index_t::indexes); +#endif /* BTR_CUR_HASH_ADAPT */ for (index = dict_table_get_first_index(table); index != NULL; @@ -4005,6 +4008,9 @@ test_dict_stats_save() table.stat_clustered_index_size = TEST_CLUSTERED_INDEX_SIZE; table.stat_sum_of_other_index_sizes = TEST_SUM_OF_OTHER_INDEX_SIZES; UT_LIST_INIT(table.indexes, &dict_index_t::indexes); +#ifdef BTR_CUR_HASH_ADAPT + UT_LIST_INIT(table.freed_indexes, &dict_index_t::indexes); +#endif /* BTR_CUR_HASH_ADAPT */ UT_LIST_ADD_LAST(table.indexes, &index1); UT_LIST_ADD_LAST(table.indexes, &index2); ut_d(table.magic_n = DICT_TABLE_MAGIC_N); @@ -4154,6 +4160,9 @@ test_dict_stats_fetch_from_ps() /* craft a dummy dict_table_t */ table.name.m_name = (char*) (TEST_DATABASE_NAME "/" TEST_TABLE_NAME); UT_LIST_INIT(table.indexes, &dict_index_t::indexes); +#ifdef BTR_CUR_HASH_ADAPT + UT_LIST_INIT(table.freed_indexes, &dict_index_t::indexes); +#endif /* BTR_CUR_HASH_ADAPT */ UT_LIST_ADD_LAST(table.indexes, &index1); UT_LIST_ADD_LAST(table.indexes, &index2); ut_d(table.magic_n = DICT_TABLE_MAGIC_N); diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index d308ff9de71..974a514e637 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -2976,8 +2976,6 @@ fseg_mark_page_used( @param[in,out] space tablespace @param[in] offset page number @param[in] page_size page size -@param[in] ahi whether we may need to drop the adaptive -hash index @param[in,out] mtr mini-transaction */ static void @@ -2986,9 +2984,6 @@ fseg_free_page_low( fil_space_t* space, page_no_t offset, const page_size_t& page_size, -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) { xdes_t* descr; @@ -3003,15 +2998,6 @@ fseg_free_page_low( == FSEG_MAGIC_N_VALUE); ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE)); ut_d(space->modify_check(*mtr)); -#ifdef BTR_CUR_HASH_ADAPT - /* Drop search system page hash index if the page is found in - the pool and is hashed */ - - if (ahi) { - btr_search_drop_page_hash_when_freed( - page_id_t(space->id, offset)); - } -#endif /* BTR_CUR_HASH_ADAPT */ descr = xdes_get_descriptor(space, offset, page_size, mtr); @@ -3097,22 +3083,13 @@ fseg_free_page_low( } } -#ifndef BTR_CUR_HASH_ADAPT -# define fseg_free_page_low(inode, space, offset, page_size, ahi, mtr) \ - fseg_free_page_low(inode, space, offset, page_size, mtr) -#endif /* !BTR_CUR_HASH_ADAPT */ - /**********************************************************************//** Frees a single page of a segment. */ void -fseg_free_page_func( +fseg_free_page( fseg_header_t* seg_header, /*!< in: segment header */ ulint space_id,/*!< in: space id */ ulint page, /*!< in: page offset */ -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, /*!< in: whether we may need to drop - the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) /*!< in/out: mini-transaction */ { DBUG_ENTER("fseg_free_page"); @@ -3128,7 +3105,7 @@ fseg_free_page_func( &iblock); fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr); - fseg_free_page_low(seg_inode, space, page, page_size, ahi, mtr); + fseg_free_page_low(seg_inode, space, page, page_size, mtr); ut_d(buf_page_set_file_page_was_freed(page_id_t(space_id, page))); @@ -3169,8 +3146,6 @@ fseg_page_is_free(fil_space_t* space, unsigned page) @param[in,out] space tablespace @param[in] page_size page size @param[in] page page number in the extent -@param[in] ahi whether we may need to drop - the adaptive hash index @param[in,out] mtr mini-transaction */ MY_ATTRIBUTE((nonnull)) static @@ -3180,9 +3155,6 @@ fseg_free_extent( fil_space_t* space, const page_size_t& page_size, ulint page, -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) { xdes_t* descr; @@ -3203,23 +3175,6 @@ fseg_free_extent( const ulint first_page_in_extent = page - (page % FSP_EXTENT_SIZE); #endif /* BTR_CUR_HASH_ADAPT || UNIV_DEBUG */ -#ifdef BTR_CUR_HASH_ADAPT - if (ahi) { - for (ulint i = 0; i < FSP_EXTENT_SIZE; i++) { - if (!xdes_mtr_get_bit(descr, XDES_FREE_BIT, i, mtr)) { - - /* Drop search system page hash index - if the page is found in the pool and - is hashed */ - - btr_search_drop_page_hash_when_freed( - page_id_t(space->id, - first_page_in_extent + i)); - } - } - } -#endif /* BTR_CUR_HASH_ADAPT */ - if (xdes_is_full(descr, mtr)) { flst_remove(seg_inode + FSEG_FULL, descr + XDES_FLST_NODE, mtr); @@ -3251,27 +3206,18 @@ fseg_free_extent( #endif /* UNIV_DEBUG */ } -#ifndef BTR_CUR_HASH_ADAPT -# define fseg_free_extent(inode, space, page_size, page, ahi, mtr) \ - fseg_free_extent(inode, space, page_size, page, mtr) -#endif /* !BTR_CUR_HASH_ADAPT */ - /**********************************************************************//** Frees part of a segment. This function can be used to free a segment by repeatedly calling this function in different mini-transactions. Doing the freeing in a single mini-transaction might result in too big a mini-transaction. -@return TRUE if freeing completed */ -ibool -fseg_free_step_func( +@return whether the freeing was completed */ +bool +fseg_free_step( fseg_header_t* header, /*!< in, own: segment header; NOTE: if the header resides on the first page of the frag list of the segment, this pointer becomes obsolete after the last freeing step */ -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, /*!< in: whether we may need to drop - the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) /*!< in/out: mini-transaction */ { ulint n; @@ -3303,7 +3249,7 @@ fseg_free_step_func( if (inode == NULL) { ib::info() << "Double free of inode from " << page_id_t(space_id, header_page); - DBUG_RETURN(TRUE); + DBUG_RETURN(true); } fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr); @@ -3313,9 +3259,9 @@ fseg_free_step_func( /* Free the extent held by the segment */ page = xdes_get_offset(descr); - fseg_free_extent(inode, space, page_size, page, ahi, mtr); + fseg_free_extent(inode, space, page_size, page, mtr); - DBUG_RETURN(FALSE); + DBUG_RETURN(false); } /* Free a frag page */ @@ -3325,13 +3271,13 @@ fseg_free_step_func( /* Freeing completed: free the segment inode */ fsp_free_seg_inode(space, page_size, inode, mtr); - DBUG_RETURN(TRUE); + DBUG_RETURN(true); } fseg_free_page_low( inode, space, fseg_get_nth_frag_page_no(inode, n, mtr), - page_size, ahi, mtr); + page_size, mtr); n = fseg_find_last_used_frag_page_slot(inode, mtr); @@ -3339,24 +3285,20 @@ fseg_free_step_func( /* Freeing completed: free the segment inode */ fsp_free_seg_inode(space, page_size, inode, mtr); - DBUG_RETURN(TRUE); + DBUG_RETURN(true); } - DBUG_RETURN(FALSE); + DBUG_RETURN(false); } /**********************************************************************//** Frees part of a segment. Differs from fseg_free_step because this function leaves the header page unfreed. -@return TRUE if freeing completed, except the header page */ -ibool -fseg_free_step_not_header_func( +@return whether the freeing was completed, except for the header page */ +bool +fseg_free_step_not_header( fseg_header_t* header, /*!< in: segment header which must reside on the first fragment page of the segment */ -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, /*!< in: whether we may need to drop - the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) /*!< in/out: mini-transaction */ { ulint n; @@ -3382,29 +3324,27 @@ fseg_free_step_not_header_func( /* Free the extent held by the segment */ page = xdes_get_offset(descr); - fseg_free_extent(inode, space, page_size, page, ahi, mtr); + fseg_free_extent(inode, space, page_size, page, mtr); - return(FALSE); + return false; } /* Free a frag page */ n = fseg_find_last_used_frag_page_slot(inode, mtr); - if (n == ULINT_UNDEFINED) { - ut_error; - } + ut_a(n != ULINT_UNDEFINED); page_no = fseg_get_nth_frag_page_no(inode, n, mtr); if (page_no == page_get_page_no(page_align(header))) { - return(TRUE); + return true; } - fseg_free_page_low(inode, space, page_no, page_size, ahi, mtr); + fseg_free_page_low(inode, space, page_no, page_size, mtr); - return(FALSE); + return false; } /** Returns the first extent descriptor for a segment. diff --git a/storage/innobase/gis/gis0sea.cc b/storage/innobase/gis/gis0sea.cc index 3631ff35874..c372a3f1cd4 100644 --- a/storage/innobase/gis/gis0sea.cc +++ b/storage/innobase/gis/gis0sea.cc @@ -590,7 +590,7 @@ rtr_pcur_open_low( } btr_cur_search_to_nth_level(index, level, tuple, mode, latch_mode, - btr_cursor, 0, file, line, mtr); + btr_cursor, 0, file, line, mtr, 0); cursor->pos_state = BTR_PCUR_IS_POSITIONED; cursor->trx_if_known = NULL; @@ -759,7 +759,7 @@ static void rtr_get_father_node( btr_cur_search_to_nth_level( index, level, tuple, PAGE_CUR_RTREE_LOCATE, BTR_CONT_MODIFY_TREE, btr_cur, 0, - __FILE__, __LINE__, mtr); + __FILE__, __LINE__, mtr, 0); } else { /* btr_validate */ @@ -769,7 +769,7 @@ static void rtr_get_father_node( btr_cur_search_to_nth_level( index, level, tuple, PAGE_CUR_RTREE_LOCATE, BTR_CONT_MODIFY_TREE, btr_cur, 0, - __FILE__, __LINE__, mtr); + __FILE__, __LINE__, mtr, 0); rec = btr_cur_get_rec(btr_cur); n_fields = dtuple_get_n_fields_cmp(tuple); diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index edc7478f7de..9b1d99d3730 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -254,6 +254,7 @@ struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx for (ulint i = 0; i < num_to_add_index; i++) { if (!add_index[i]->is_committed()) { add_index[i]->detach_columns(); + add_index[i]->n_fields = 0; } } } @@ -8689,21 +8690,14 @@ foreign_fail: } if (ctx0->num_to_drop_vcol || ctx0->num_to_add_vcol) { + /* FIXME: this workaround does not seem to work with + partitioned tables */ DBUG_ASSERT(ctx0->old_table->get_ref_count() == 1); trx_commit_for_mysql(m_prebuilt->trx); -#ifdef BTR_CUR_HASH_ADAPT - if (btr_search_enabled) { - btr_search_disable(false); - btr_search_enable(); - } -#endif /* BTR_CUR_HASH_ADAPT */ - - char tb_name[FN_REFLEN]; - ut_strcpy(tb_name, m_prebuilt->table->name.m_name); - - tb_name[strlen(m_prebuilt->table->name.m_name)] = 0; + char tb_name[NAME_LEN * 2 + 1 + 1]; + strcpy(tb_name, m_prebuilt->table->name.m_name); dict_table_close(m_prebuilt->table, true, false); dict_table_remove_from_cache(m_prebuilt->table); m_prebuilt->table = dict_table_open_on_name( diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index e02958b4ac3..1ee1624ca8b 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -2109,7 +2109,7 @@ ibuf_remove_free_page(void) page from it. */ fseg_free_page(header_page + IBUF_HEADER + IBUF_TREE_SEG_HEADER, - IBUF_SPACE_ID, page_no, false, &mtr); + IBUF_SPACE_ID, page_no, &mtr); const page_id_t page_id(IBUF_SPACE_ID, page_no); diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index 19d0fb397be..f70fe687182 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -154,8 +154,7 @@ Note that if mode is PAGE_CUR_LE, which is used in inserts, then cursor->up_match and cursor->low_match both will have sensible values. If mode is PAGE_CUR_GE, then up_match will a have a sensible value. */ dberr_t -btr_cur_search_to_nth_level( -/*========================*/ +btr_cur_search_to_nth_level_func( dict_index_t* index, /*!< in: index */ ulint level, /*!< in: the tree level of search */ const dtuple_t* tuple, /*!< in: data tuple; NOTE: n_fields_cmp in @@ -181,16 +180,24 @@ btr_cur_search_to_nth_level( to protect the record! */ btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is s- or x-latched, but see also above! */ +#ifdef BTR_CUR_HASH_ADAPT ulint has_search_latch, /*!< in: latch mode the caller currently has on search system: RW_S_LATCH, or 0 */ +#endif /* BTR_CUR_HASH_ADAPT */ const char* file, /*!< in: file name */ unsigned line, /*!< in: line where called */ mtr_t* mtr, /*!< in/out: mini-transaction */ - ib_uint64_t autoinc = 0); + ib_uint64_t autoinc); /*!< in: PAGE_ROOT_AUTO_INC to be written (0 if none) */ +#ifdef BTR_CUR_HASH_ADAPT +# define btr_cur_search_to_nth_level btr_cur_search_to_nth_level_func +#else /* BTR_CUR_HASH_ADAPT */ +# define btr_cur_search_to_nth_level(ix,lv,t,md,l,cur,has,file,line,m,ai) \ + btr_cur_search_to_nth_level_func(ix,lv,t,md,l,cur,file,line,m,ai) +#endif /* BTR_CUR_HASH_ADAPT */ /*****************************************************************//** Opens a cursor at either end of an index. diff --git a/storage/innobase/include/btr0pcur.h b/storage/innobase/include/btr0pcur.h index b79260e5ab6..26c7ebcee6d 100644 --- a/storage/innobase/include/btr0pcur.h +++ b/storage/innobase/include/btr0pcur.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -136,15 +136,22 @@ btr_pcur_open_with_no_init_func( page, but assume that the caller uses his btr search latch to protect the record! */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ +#ifdef BTR_CUR_HASH_ADAPT ulint has_search_latch, /*!< in: latch mode the caller currently has on search system: RW_S_LATCH, or 0 */ +#endif /* BTR_CUR_HASH_ADAPT */ const char* file, /*!< in: file name */ unsigned line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ -#define btr_pcur_open_with_no_init(ix,t,md,l,cur,has,m) \ +#ifdef BTR_CUR_HASH_ADAPT +# define btr_pcur_open_with_no_init(ix,t,md,l,cur,has,m) \ btr_pcur_open_with_no_init_func(ix,t,md,l,cur,has,__FILE__,__LINE__,m) +#else /* BTR_CUR_HASH_ADAPT */ +# define btr_pcur_open_with_no_init(ix,t,md,l,cur,has,m) \ + btr_pcur_open_with_no_init_func(ix,t,md,l,cur,__FILE__,__LINE__,m) +#endif /* BTR_CUR_HASH_ADAPT */ /*****************************************************************//** Opens a persistent cursor at either end of an index. */ diff --git a/storage/innobase/include/btr0pcur.ic b/storage/innobase/include/btr0pcur.ic index 8b0da666250..b5b57050832 100644 --- a/storage/innobase/include/btr0pcur.ic +++ b/storage/innobase/include/btr0pcur.ic @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2015, 2019, MariaDB Corporation. +Copyright (c) 2015, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -496,10 +496,12 @@ btr_pcur_open_with_no_init_func( page, but assume that the caller uses his btr search latch to protect the record! */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ +#ifdef BTR_CUR_HASH_ADAPT ulint has_search_latch, /*!< in: latch mode the caller currently has on search system: RW_S_LATCH, or 0 */ +#endif /* BTR_CUR_HASH_ADAPT */ const char* file, /*!< in: file name */ unsigned line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ @@ -516,7 +518,7 @@ btr_pcur_open_with_no_init_func( err = btr_cur_search_to_nth_level( index, 0, tuple, mode, latch_mode, btr_cursor, - has_search_latch, file, line, mtr); + has_search_latch, file, line, mtr, 0); cursor->pos_state = BTR_PCUR_IS_POSITIONED; diff --git a/storage/innobase/include/btr0sea.h b/storage/innobase/include/btr0sea.h index 645b3689ff6..b808ba036db 100644 --- a/storage/innobase/include/btr0sea.h +++ b/storage/innobase/include/btr0sea.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2018, MariaDB Corporation. +Copyright (c) 2018, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -54,15 +54,6 @@ btr_search_disable( void btr_search_enable(); -/** Returns the value of ref_count. The value is protected by latch. -@param[in] info search info -@param[in] index index identifier -@return ref_count value. */ -ulint -btr_search_info_get_ref_count( - btr_search_t* info, - dict_index_t* index); - /*********************************************************************//** Updates the search info. */ UNIV_INLINE @@ -156,18 +147,6 @@ btr_search_update_hash_on_delete(btr_cur_t* cursor); bool btr_search_validate(); -/** X-Lock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_x_lock(const dict_index_t* index); - -/** X-Unlock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_x_unlock(const dict_index_t* index); - /** Lock all search latches in exclusive mode. */ UNIV_INLINE void @@ -178,18 +157,6 @@ UNIV_INLINE void btr_search_x_unlock_all(); -/** S-Lock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_s_lock(const dict_index_t* index); - -/** S-Unlock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_s_unlock(const dict_index_t* index); - /** Lock all search latches in shared mode. */ UNIV_INLINE void @@ -236,12 +203,8 @@ btr_get_search_table(const dict_index_t* index); #else /* BTR_CUR_HASH_ADAPT */ # define btr_search_sys_create(size) # define btr_search_drop_page_hash_index(block) -# define btr_search_s_lock(index) -# define btr_search_s_unlock(index) # define btr_search_s_lock_all(index) # define btr_search_s_unlock_all(index) -# define btr_search_x_lock(index) -# define btr_search_x_unlock(index) # define btr_search_info_update(index, cursor) # define btr_search_move_or_delete_hash_entries(new_block, block, index) # define btr_search_update_hash_on_insert(cursor) @@ -327,6 +290,18 @@ struct btr_search_t{ }; #ifdef BTR_CUR_HASH_ADAPT +/** @return number of leaf pages pointed to by the adaptive hash index */ +inline ulint dict_index_t::n_ahi_pages() const +{ + if (!btr_search_enabled) + return 0; + rw_lock_t *latch = btr_get_search_latch(this); + rw_lock_s_lock(latch); + ulint ref_count= search_info->ref_count; + rw_lock_s_unlock(latch); + return ref_count; +} + /** The hash index system */ struct btr_search_sys_t{ hash_table_t** hash_tables; /*!< the adaptive hash tables, diff --git a/storage/innobase/include/btr0sea.ic b/storage/innobase/include/btr0sea.ic index 4972de16064..90877d23192 100644 --- a/storage/innobase/include/btr0sea.ic +++ b/storage/innobase/include/btr0sea.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -62,8 +63,8 @@ btr_search_info_update( dict_index_t* index, /*!< in: index of the cursor */ btr_cur_t* cursor) /*!< in: cursor which was just positioned */ { - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_S)); - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); + ut_ad(!rw_lock_own_flagged(btr_get_search_latch(index), + RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)); if (dict_index_is_spatial(index) || !btr_search_enabled) { return; @@ -87,24 +88,6 @@ btr_search_info_update( btr_search_info_update_slow(info, cursor); } -/** X-Lock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_x_lock(const dict_index_t* index) -{ - rw_lock_x_lock(btr_get_search_latch(index)); -} - -/** X-Unlock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_x_unlock(const dict_index_t* index) -{ - rw_lock_x_unlock(btr_get_search_latch(index)); -} - /** Lock all search latches in exclusive mode. */ UNIV_INLINE void @@ -125,24 +108,6 @@ btr_search_x_unlock_all() } } -/** S-Lock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_s_lock(const dict_index_t* index) -{ - rw_lock_s_lock(btr_get_search_latch(index)); -} - -/** S-Unlock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_s_unlock(const dict_index_t* index) -{ - rw_lock_s_unlock(btr_get_search_latch(index)); -} - /** Lock all search latches in shared mode. */ UNIV_INLINE void diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index 0536e2f8ac6..a9b8ef94bab 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -71,11 +71,13 @@ struct fil_addr_t; /* @} */ /** @name Modes for buf_page_get_known_nowait */ /* @{ */ -#define BUF_MAKE_YOUNG 51 /*!< Move the block to the +#ifdef BTR_CUR_HASH_ADAPT +# define BUF_MAKE_YOUNG 51 /*!< Move the block to the start of the LRU list if there is a danger that the block would drift out of the buffer pool*/ +#endif /* BTR_CUR_HASH_ADAPT */ #define BUF_KEEP_OLD 52 /*!< Preserve the current LRU position of the block. */ /* @} */ @@ -282,12 +284,6 @@ extern "C" os_thread_ret_t DECLARE_THREAD(buf_resize_thread)(void*); -#ifdef BTR_CUR_HASH_ADAPT -/** Clear the adaptive hash index on all pages in the buffer pool. */ -void -buf_pool_clear_hash_index(); -#endif /* BTR_CUR_HASH_ADAPT */ - /*********************************************************************//** Gets the current size of buffer buf_pool in bytes. @return size in bytes */ diff --git a/storage/innobase/include/buf0lru.h b/storage/innobase/include/buf0lru.h index 1efbb1f03ef..a72d98395af 100644 --- a/storage/innobase/include/buf0lru.h +++ b/storage/innobase/include/buf0lru.h @@ -49,17 +49,6 @@ These are low-level functions /** Minimum LRU list length for which the LRU_old pointer is defined */ #define BUF_LRU_OLD_MIN_LEN 512 /* 8 megabytes of 16k pages */ -#ifdef BTR_CUR_HASH_ADAPT -struct dict_table_t; -/** Try to drop the adaptive hash index for a tablespace. -@param[in,out] table table -@return whether anything was dropped */ -bool buf_LRU_drop_page_hash_for_tablespace(dict_table_t* table) - MY_ATTRIBUTE((warn_unused_result,nonnull)); -#else -# define buf_LRU_drop_page_hash_for_tablespace(table) -#endif /* BTR_CUR_HASH_ADAPT */ - /** Empty the flush list for all pages belonging to a tablespace. @param[in] id tablespace identifier @param[in,out] observer flush observer, diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index b259d2fb2ad..80d18dcbb2a 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -831,6 +831,9 @@ struct dict_index_t{ dict_table_t* table; /*!< back pointer to table */ unsigned space:32; /*!< space where the index tree is placed */ + /** root page number, or FIL_NULL if the index has been detached + from storage (DISCARD TABLESPACE or similar), + or 1 if the index is in table->freed_indexes */ unsigned page:32;/*!< index tree root page number */ unsigned merge_threshold:6; /*!< In the pessimistic delete, if the page @@ -1021,11 +1024,23 @@ struct dict_index_t{ for (unsigned i = 0; i < n_fields; i++) { fields[i].col->detach(*this); } - - n_fields = 0; } } +#ifdef BTR_CUR_HASH_ADAPT + /** @return a clone of this */ + dict_index_t* clone() const; + /** Clone this index for lazy dropping of the adaptive hash index. + @return this or a clone */ + dict_index_t* clone_if_needed(); + /** @return number of leaf pages pointed to by the adaptive hash index */ + inline ulint n_ahi_pages() const; + /** @return whether mark_freed() had been invoked */ + bool freed() const { return UNIV_UNLIKELY(page == 1); } + /** Note that the index is waiting for btr_search_lazy_free() */ + void set_freed() { ut_ad(!freed()); page= 1; } +#endif /* BTR_CUR_HASH_ADAPT */ + /** This ad-hoc class is used by record_size_info only. */ class record_size_info_t { public: @@ -1605,6 +1620,11 @@ struct dict_table_t { /** List of indexes of the table. */ UT_LIST_BASE_NODE_T(dict_index_t) indexes; +#ifdef BTR_CUR_HASH_ADAPT + /** List of detached indexes that are waiting to be freed along with + the last adaptive hash index entry */ + UT_LIST_BASE_NODE_T(dict_index_t) freed_indexes; +#endif /* BTR_CUR_HASH_ADAPT */ /** List of foreign key constraints in the table. These refer to columns in other tables. */ diff --git a/storage/innobase/include/fsp0fsp.h b/storage/innobase/include/fsp0fsp.h index a4e3b84b55e..8debdb00013 100644 --- a/storage/innobase/include/fsp0fsp.h +++ b/storage/innobase/include/fsp0fsp.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2019, MariaDB Corporation. +Copyright (c) 2013, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -568,22 +568,11 @@ fsp_get_available_space_in_free_extents( /**********************************************************************//** Frees a single page of a segment. */ void -fseg_free_page_func( +fseg_free_page( fseg_header_t* seg_header, /*!< in: segment header */ ulint space_id, /*!< in: space id */ ulint page, /*!< in: page offset */ -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, /*!< in: whether we may need to drop - the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr); /*!< in/out: mini-transaction */ -#ifdef BTR_CUR_HASH_ADAPT -# define fseg_free_page(header, space_id, page, ahi, mtr) \ - fseg_free_page_func(header, space_id, page, ahi, mtr) -#else /* BTR_CUR_HASH_ADAPT */ -# define fseg_free_page(header, space_id, page, ahi, mtr) \ - fseg_free_page_func(header, space_id, page, mtr) -#endif /* BTR_CUR_HASH_ADAPT */ /** Determine whether a page is free. @param[in,out] space tablespace @param[in] page page number @@ -596,45 +585,25 @@ Frees part of a segment. This function can be used to free a segment by repeatedly calling this function in different mini-transactions. Doing the freeing in a single mini-transaction might result in too big a mini-transaction. -@return TRUE if freeing completed */ -ibool -fseg_free_step_func( +@return whether the freeing was completed */ +bool +fseg_free_step( fseg_header_t* header, /*!< in, own: segment header; NOTE: if the header resides on the first page of the frag list of the segment, this pointer becomes obsolete after the last freeing step */ -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, /*!< in: whether we may need to drop - the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) /*!< in/out: mini-transaction */ MY_ATTRIBUTE((warn_unused_result)); -#ifdef BTR_CUR_HASH_ADAPT -# define fseg_free_step(header, ahi, mtr) fseg_free_step_func(header, ahi, mtr) -#else /* BTR_CUR_HASH_ADAPT */ -# define fseg_free_step(header, ahi, mtr) fseg_free_step_func(header, mtr) -#endif /* BTR_CUR_HASH_ADAPT */ /**********************************************************************//** Frees part of a segment. Differs from fseg_free_step because this function leaves the header page unfreed. -@return TRUE if freeing completed, except the header page */ -ibool -fseg_free_step_not_header_func( +@return whether the freeing was completed, except for the header page */ +bool +fseg_free_step_not_header( fseg_header_t* header, /*!< in: segment header which must reside on the first fragment page of the segment */ -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, /*!< in: whether we may need to drop - the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) /*!< in/out: mini-transaction */ MY_ATTRIBUTE((warn_unused_result)); -#ifdef BTR_CUR_HASH_ADAPT -# define fseg_free_step_not_header(header, ahi, mtr) \ - fseg_free_step_not_header_func(header, ahi, mtr) -#else /* BTR_CUR_HASH_ADAPT */ -# define fseg_free_step_not_header(header, ahi, mtr) \ - fseg_free_step_not_header_func(header, mtr) -#endif /* BTR_CUR_HASH_ADAPT */ /** Reset the page type. Data files created before MySQL 5.1.48 may contain garbage in FIL_PAGE_TYPE. diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index b0fcecf0e39..d92bcb761e4 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -26,6 +26,9 @@ Created 2012-02-08 by Sunny Bains. #include "row0import.h" #include "btr0pcur.h" +#ifdef BTR_CUR_HASH_ADAPT +# include "btr0sea.h" +#endif #include "que0que.h" #include "dict0boot.h" #include "dict0load.h" @@ -4017,15 +4020,12 @@ row_import_for_mysql( index entries that point to cached garbage pages in the buffer pool, because PageConverter::operator() only evicted those pages that were replaced by the imported pages. We must - discard all remaining adaptive hash index entries, because the + detach any remaining adaptive hash index entries, because the adaptive hash index must be a subset of the table contents; false positives are not tolerated. */ - while (buf_LRU_drop_page_hash_for_tablespace(table)) { - if (trx_is_interrupted(trx) - || srv_shutdown_state != SRV_SHUTDOWN_NONE) { - err = DB_INTERRUPTED; - break; - } + for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes); index; + index = UT_LIST_GET_NEXT(indexes, index)) { + index = index->clone_if_needed(); } #endif /* BTR_CUR_HASH_ADAPT */ diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 4c11c241cd6..123566132f4 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -2907,7 +2907,7 @@ row_ins_sec_index_entry_low( err = btr_cur_search_to_nth_level( index, 0, entry, PAGE_CUR_RTREE_INSERT, search_mode, - &cursor, 0, __FILE__, __LINE__, &mtr); + &cursor, 0, __FILE__, __LINE__, &mtr, 0); if (mode == BTR_MODIFY_LEAF && rtr_info.mbr_adj) { mtr_commit(&mtr); @@ -2922,7 +2922,7 @@ row_ins_sec_index_entry_low( err = btr_cur_search_to_nth_level( index, 0, entry, PAGE_CUR_RTREE_INSERT, search_mode, - &cursor, 0, __FILE__, __LINE__, &mtr); + &cursor, 0, __FILE__, __LINE__, &mtr, 0); mode = BTR_MODIFY_TREE; } @@ -2934,7 +2934,7 @@ row_ins_sec_index_entry_low( err = btr_cur_search_to_nth_level( index, 0, entry, PAGE_CUR_LE, search_mode, - &cursor, 0, __FILE__, __LINE__, &mtr); + &cursor, 0, __FILE__, __LINE__, &mtr, 0); } if (err != DB_SUCCESS) { @@ -3028,7 +3028,7 @@ row_ins_sec_index_entry_low( index, 0, entry, PAGE_CUR_LE, (search_mode & ~(BTR_INSERT | BTR_IGNORE_SEC_UNIQUE)), - &cursor, 0, __FILE__, __LINE__, &mtr); + &cursor, 0, __FILE__, __LINE__, &mtr, 0); } if (row_ins_must_modify_rec(&cursor)) { diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index e7b06e5a5b2..e46ee2c4f18 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -3102,7 +3102,7 @@ row_log_apply_op_low( ? BTR_MODIFY_TREE : BTR_MODIFY_LEAF, &cursor, 0, __FILE__, __LINE__, - &mtr); + &mtr, 0); ut_ad(dict_index_get_n_unique(index) > 0); /* This test is somewhat similar to row_ins_must_modify_rec(), @@ -3151,7 +3151,7 @@ row_log_apply_op_low( btr_cur_search_to_nth_level( index, 0, entry, PAGE_CUR_LE, BTR_MODIFY_TREE, &cursor, 0, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, 0); /* No other thread than the current one is allowed to modify the index tree. @@ -3254,7 +3254,7 @@ insert_the_rec: btr_cur_search_to_nth_level( index, 0, entry, PAGE_CUR_LE, BTR_MODIFY_TREE, &cursor, 0, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, 0); } /* We already determined that the diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 1677a47fa45..8c8f1674374 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -46,6 +46,9 @@ Completed by Sunny Bains and Marko Makela #include "row0vers.h" #include "handler0alter.h" #include "btr0bulk.h" +#ifdef BTR_CUR_ADAPT +# include "btr0sea.h" +#endif /* BTR_CUR_ADAPT */ #include "ut0stage.h" #include "fil0crypt.h" @@ -162,7 +165,7 @@ public: PAGE_CUR_RTREE_INSERT, BTR_MODIFY_LEAF, &ins_cur, 0, __FILE__, __LINE__, - &mtr); + &mtr, 0); /* It need to update MBR in parent entry, so change search mode to BTR_MODIFY_TREE */ @@ -178,7 +181,7 @@ public: m_index, 0, dtuple, PAGE_CUR_RTREE_INSERT, BTR_MODIFY_TREE, &ins_cur, 0, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, 0); } error = btr_cur_optimistic_insert( @@ -201,8 +204,7 @@ public: PAGE_CUR_RTREE_INSERT, BTR_MODIFY_TREE, &ins_cur, 0, - __FILE__, __LINE__, &mtr); - + __FILE__, __LINE__, &mtr, 0); error = btr_cur_pessimistic_insert( flag, &ins_cur, &ins_offsets, @@ -3770,6 +3772,9 @@ row_merge_drop_indexes( we should exclude FTS entries from prebuilt->ins_node->entry_list in ins_node_create_entry_list(). */ +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!index->search_info->ref_count); +#endif /* BTR_CUR_HASH_ADAPT */ dict_index_remove_from_cache( table, index); index = prev; diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 45586a1e977..9bbd531cb9a 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -2459,6 +2459,9 @@ row_create_index_for_mysql( index->table = table; err = dict_create_index_tree_in_mem(index, trx); +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!index->search_info->ref_count); +#endif /* BTR_CUR_HASH_ADAPT */ if (err != DB_SUCCESS) { dict_index_remove_from_cache(table, index); @@ -3372,35 +3375,6 @@ row_drop_table_for_mysql( if (!dict_table_is_temporary(table)) { if (table->space != TRX_SYS_SPACE) { -#ifdef BTR_CUR_HASH_ADAPT - /* On DISCARD TABLESPACE, we would not drop the - adaptive hash index entries. If the tablespace is - missing here, delete-marking the record in SYS_INDEXES - would not free any pages in the buffer pool. Thus, - dict_index_remove_from_cache() would hang due to - adaptive hash index entries existing in the buffer - pool. To prevent this hang, and also to guarantee - that btr_search_drop_page_hash_when_freed() will avoid - calling btr_search_drop_page_hash_index() while we - hold the InnoDB dictionary lock, we will drop any - adaptive hash index entries upfront. */ - const bool immune = is_temp_name - || create_failed - || sqlcom == SQLCOM_CREATE_TABLE - || strstr(table->name.m_name, "/FTS"); - - while (buf_LRU_drop_page_hash_for_tablespace(table)) { - if ((!immune && trx_is_interrupted(trx)) - || srv_shutdown_state - != SRV_SHUTDOWN_NONE) { - err = DB_INTERRUPTED; - table->to_be_dropped = false; - dict_table_close(table, true, false); - goto funct_exit; - } - } -#endif /* BTR_CUR_HASH_ADAPT */ - /* Delete the link file if used. */ if (DICT_TF_HAS_DATA_DIR(table->flags)) { RemoteDatafile::delete_link_file(name); diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 6420bf8a707..15486500b37 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -2,7 +2,7 @@ Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. -Copyright (c) 2015, 2019, MariaDB Corporation. +Copyright (c) 2015, 2020, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -1289,23 +1289,17 @@ void row_sel_open_pcur( /*==============*/ plan_t* plan, /*!< in: table plan */ - ibool search_latch_locked, - /*!< in: TRUE if the thread currently - has the search latch locked in - s-mode */ +#ifdef BTR_CUR_HASH_ADAPT + ulint has_search_latch, +#endif mtr_t* mtr) /*!< in: mtr */ { dict_index_t* index; func_node_t* cond; que_node_t* exp; ulint n_fields; - ulint has_search_latch = 0; /* RW_S_LATCH or 0 */ ulint i; - if (search_latch_locked) { - has_search_latch = RW_S_LATCH; - } - index = plan->index; /* Calculate the value of the search tuple: the exact match columns @@ -1357,6 +1351,11 @@ row_sel_open_pcur( plan->pcur_is_open = TRUE; } +#ifndef BTR_CUR_HASH_ADAPT +# define row_sel_open_pcur(plan, has_search_latch, mtr) \ + row_sel_open_pcur(plan, mtr) +#endif /* !BTR_CUR_HASH_ADAPT */ + /*********************************************************************//** Restores a stored pcur position to a table index. @return TRUE if the cursor should be moved to the next record after we @@ -1618,12 +1617,6 @@ row_sel( ut_ad(thr->run_node == node); -#ifdef BTR_CUR_HASH_ADAPT - ibool search_latch_locked = FALSE; -#else /* BTR_CUR_HASH_ADAPT */ -# define search_latch_locked false -#endif /* BTR_CUR_HASH_ADAPT */ - if (node->read_view) { /* In consistent reads, we try to do with the hash index and not to use the buffer page get. This is to reduce memory bus @@ -1648,6 +1641,10 @@ table_loop: plan = sel_node_get_nth_plan(node, node->fetch_table); index = plan->index; +#ifdef BTR_CUR_HASH_ADAPT + ulint has_search_latch = 0; + rw_lock_t* const latch = btr_get_search_latch(index); +#endif /* BTR_CUR_HASH_ADAPT */ if (plan->n_rows_prefetched > 0) { sel_dequeue_prefetched_row(plan); @@ -1672,26 +1669,22 @@ table_loop: #ifdef BTR_CUR_HASH_ADAPT if (consistent_read && plan->unique_search && !plan->pcur_is_open && !plan->must_get_clust) { - if (!search_latch_locked) { - btr_search_s_lock(index); - - search_latch_locked = TRUE; - } else if (rw_lock_get_writer(btr_get_search_latch(index)) - == RW_LOCK_X_WAIT) { - + if (!has_search_latch) { + has_search_latch = RW_S_LATCH; + rw_lock_s_lock(latch); + } else if (rw_lock_get_writer(latch) == RW_LOCK_X_WAIT) { /* There is an x-latch request waiting: release the s-latch for a moment; as an s-latch here is often kept for some 10 searches before being released, a waiting x-latch request would block other threads from acquiring an s-latch for a long time, lowering performance significantly in multiprocessors. */ - - btr_search_s_unlock(index); - btr_search_s_lock(index); + rw_lock_s_unlock(latch); + rw_lock_s_lock(latch); } switch (row_sel_try_search_shortcut(node, plan, - search_latch_locked, + has_search_latch, &mtr)) { case SEL_FOUND: goto next_table; @@ -1709,10 +1702,9 @@ table_loop: mtr.start(); } - if (search_latch_locked) { - btr_search_s_unlock(index); - - search_latch_locked = FALSE; + if (has_search_latch) { + has_search_latch = 0; + rw_lock_s_unlock(latch); } #endif /* BTR_CUR_HASH_ADAPT */ @@ -1720,7 +1712,7 @@ table_loop: /* Evaluate the expressions to build the search tuple and open the cursor */ - row_sel_open_pcur(plan, search_latch_locked, &mtr); + row_sel_open_pcur(plan, has_search_latch, &mtr); cursor_just_opened = TRUE; @@ -2117,7 +2109,9 @@ skip_lock: } next_rec: - ut_ad(!search_latch_locked); +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!has_search_latch); +#endif /* BTR_CUR_HASH_ADAPT */ if (mtr_has_extra_clust_latch) { @@ -2156,8 +2150,9 @@ next_table: plan->cursor_at_end = TRUE; } else { - ut_ad(!search_latch_locked); - +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!has_search_latch); +#endif /* BTR_CUR_HASH_ADAPT */ plan->stored_cursor_rec_processed = TRUE; btr_pcur_store_position(&(plan->pcur), &mtr); @@ -2248,8 +2243,9 @@ stop_for_a_while: inserted new records which should have appeared in the result set, which would result in the phantom problem. */ - ut_ad(!search_latch_locked); - +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!has_search_latch); +#endif /* BTR_CUR_HASH_ADAPT */ plan->stored_cursor_rec_processed = FALSE; btr_pcur_store_position(&(plan->pcur), &mtr); @@ -2266,7 +2262,9 @@ commit_mtr_for_a_while: plan->stored_cursor_rec_processed = TRUE; - ut_ad(!search_latch_locked); +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!has_search_latch); +#endif /* BTR_CUR_HASH_ADAPT */ btr_pcur_store_position(&(plan->pcur), &mtr); mtr.commit(); @@ -2280,7 +2278,9 @@ lock_wait_or_error: /* See the note at stop_for_a_while: the same holds for this case */ ut_ad(!btr_pcur_is_before_first_on_page(&plan->pcur) || !node->asc); - ut_ad(!search_latch_locked); +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!has_search_latch); +#endif /* BTR_CUR_HASH_ADAPT */ plan->stored_cursor_rec_processed = FALSE; btr_pcur_store_position(&(plan->pcur), &mtr); @@ -2289,8 +2289,8 @@ lock_wait_or_error: func_exit: #ifdef BTR_CUR_HASH_ADAPT - if (search_latch_locked) { - btr_search_s_unlock(index); + if (has_search_latch) { + rw_lock_s_unlock(latch); } #endif /* BTR_CUR_HASH_ADAPT */ ut_ad(!sync_check_iterate(dict_sync_check())); @@ -4460,7 +4460,6 @@ row_search_mvcc( && !prebuilt->templ_contains_blob && !prebuilt->used_in_HANDLER && (prebuilt->mysql_row_len < UNIV_PAGE_SIZE / 8)) { - mode = PAGE_CUR_GE; if (trx->mysql_n_tables_locked == 0 @@ -4480,7 +4479,8 @@ row_search_mvcc( and if we try that, we can deadlock on the adaptive hash index semaphore! */ - rw_lock_s_lock(btr_get_search_latch(index)); + rw_lock_t* const latch = btr_get_search_latch(index); + rw_lock_s_lock(latch); switch (row_sel_try_search_shortcut_for_mysql( &rec, prebuilt, &offsets, &heap, @@ -4534,7 +4534,7 @@ row_search_mvcc( err = DB_SUCCESS; - rw_lock_s_unlock(btr_get_search_latch(index)); + rw_lock_s_unlock(latch); goto func_exit; @@ -4544,7 +4544,7 @@ row_search_mvcc( err = DB_RECORD_NOT_FOUND; - rw_lock_s_unlock(btr_get_search_latch(index)); + rw_lock_s_unlock(latch); /* NOTE that we do NOT store the cursor position */ @@ -4561,7 +4561,7 @@ row_search_mvcc( mtr.commit(); mtr.start(); - rw_lock_s_unlock(btr_get_search_latch(index)); + rw_lock_s_unlock(latch); } } #endif /* BTR_CUR_HASH_ADAPT */ diff --git a/storage/innobase/row/row0trunc.cc b/storage/innobase/row/row0trunc.cc index 5e512c602e6..618e161bee4 100644 --- a/storage/innobase/row/row0trunc.cc +++ b/storage/innobase/row/row0trunc.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2013, 2018, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -39,6 +39,7 @@ Created 2013-04-12 Sunny Bains #include "os0file.h" #include "que0que.h" #include "trx0undo.h" +#include "btr0sea.h" /* FIXME: For temporary tables, use a simple approach of btr_free() and btr_create() of each index tree. */ @@ -1953,7 +1954,6 @@ dberr_t row_truncate_table_for_mysql(dict_table_t* table, trx_t* trx) for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes); index != NULL; index = UT_LIST_GET_NEXT(indexes, index)) { - err = dict_truncate_index_tree_in_mem(index); if (err != DB_SUCCESS) { @@ -2003,6 +2003,15 @@ dberr_t row_truncate_table_for_mysql(dict_table_t* table, trx_t* trx) os_thread_sleep(2000000); DBUG_SUICIDE();); +#ifdef BTR_CUR_HASH_ADAPT + dict_table_x_unlock_indexes(table); + for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes); index; + index = UT_LIST_GET_NEXT(indexes, index)) { + index = index->clone_if_needed(); + } + dict_table_x_lock_indexes(table); +#endif /* BTR_CUR_HASH_ADAPT */ + /* Step-10: Re-create new indexes. */ if (!dict_table_is_temporary(table)) { diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index 732435ccefb..81ba1b731bf 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -383,7 +383,7 @@ trx_purge_free_segment(trx_rseg_t* rseg, fil_addr_t hdr_addr) } if (fseg_free_step_not_header( - seg_hdr + TRX_UNDO_FSEG_HEADER, false, &mtr)) { + seg_hdr + TRX_UNDO_FSEG_HEADER, &mtr)) { break; } @@ -413,7 +413,7 @@ trx_purge_free_segment(trx_rseg_t* rseg, fil_addr_t hdr_addr) is not flooded with bufferfixed pages: see the note in fsp0fsp.cc. */ - } while (!fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER, false, &mtr)); + } while (!fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER, &mtr)); hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, &mtr); diff --git a/storage/innobase/trx/trx0undo.cc b/storage/innobase/trx/trx0undo.cc index 336506c7b65..f9e76e5d772 100644 --- a/storage/innobase/trx/trx0undo.cc +++ b/storage/innobase/trx/trx0undo.cc @@ -869,7 +869,7 @@ trx_undo_free_page( undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE, mtr); fseg_free_page(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER, - space, page_no, false, mtr); + space, page_no, mtr); last_addr = flst_get_last(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST, mtr); @@ -1092,7 +1092,7 @@ trx_undo_seg_free( file_seg = seg_header + TRX_UNDO_FSEG_HEADER; - finished = fseg_free_step(file_seg, false, &mtr); + finished = fseg_free_step(file_seg, &mtr); if (finished) { /* Update the rseg header */ |