diff options
author | Thirunarayanan Balathandayuthapani <thiru@mariadb.com> | 2021-02-01 18:35:31 +0530 |
---|---|---|
committer | Thirunarayanan Balathandayuthapani <thiru@mariadb.com> | 2021-02-02 12:48:23 +0530 |
commit | 384b0bc811b5dbe9bf35c96b12d2c88170b9da74 (patch) | |
tree | 6da7d50acc4e48b8717f8a1ea5df0fa146adc31f | |
parent | 8a495d7f90f64566d083d9ccd04cd95023a40931 (diff) | |
download | mariadb-git-bb-10.6-MDEV-24720.tar.gz |
MDEV-24720 AHI removal during rollback of bulk insertbb-10.6-MDEV-24720
InnoDB fails to remove the ahi entries during rollback
of bulk insert operation. InnoDB throws the error when
validates the ahi hash tables. InnoDB should remove
the ahi entries while freeing the segment only during
bulk index rollback operation.
Reviewed-by: Marko Mäkelä
-rw-r--r-- | mysql-test/suite/innodb/r/insert_into_empty.result | 14 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/insert_into_empty.test | 14 | ||||
-rw-r--r-- | storage/innobase/btr/btr0btr.cc | 30 | ||||
-rw-r--r-- | storage/innobase/fsp/fsp0fsp.cc | 103 | ||||
-rw-r--r-- | storage/innobase/include/fsp0fsp.h | 45 |
5 files changed, 160 insertions, 46 deletions
diff --git a/mysql-test/suite/innodb/r/insert_into_empty.result b/mysql-test/suite/innodb/r/insert_into_empty.result index 651cadd50b7..02d9549886d 100644 --- a/mysql-test/suite/innodb/r/insert_into_empty.result +++ b/mysql-test/suite/innodb/r/insert_into_empty.result @@ -13,3 +13,17 @@ SELECT * FROM t; a b 1 3 DROP TEMPORARY TABLE t; +# +# MDEV-24720 AHI removal during bulk index rollback +# +SET @save_ahi = @@global.innodb_adaptive_hash_index; +SET GLOBAL innodb_adaptive_hash_index = 1; +CREATE TABLE t1(f1 INT NOT NULL)ENGINE=InnoDB; +BEGIN; +INSERT INTO t1 SELECT * FROM seq_1_to_65536; +ROLLBACK; +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; +SET GLOBAL innodb_adaptive_hash_index = @save_ahi; diff --git a/mysql-test/suite/innodb/t/insert_into_empty.test b/mysql-test/suite/innodb/t/insert_into_empty.test index 945f0dbd627..af067cbd02d 100644 --- a/mysql-test/suite/innodb/t/insert_into_empty.test +++ b/mysql-test/suite/innodb/t/insert_into_empty.test @@ -1,4 +1,5 @@ --source include/have_innodb.inc +--source include/have_sequence.inc --echo # --echo # MDEV-24715 Assertion !node->table->skip_alter_undo @@ -12,3 +13,16 @@ CREATE TEMPORARY TABLE t (a INT UNIQUE) ENGINE=InnoDB REPLACE SELECT 1 AS a, 2 AS b UNION SELECT 1 AS a, 3 AS c; SELECT * FROM t; DROP TEMPORARY TABLE t; + +--echo # +--echo # MDEV-24720 AHI removal during bulk index rollback +--echo # +SET @save_ahi = @@global.innodb_adaptive_hash_index; +SET GLOBAL innodb_adaptive_hash_index = 1; +CREATE TABLE t1(f1 INT NOT NULL)ENGINE=InnoDB; +BEGIN; +INSERT INTO t1 SELECT * FROM seq_1_to_65536; +ROLLBACK; +CHECK TABLE t1; +DROP TABLE t1; +SET GLOBAL innodb_adaptive_hash_index = @save_ahi; diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index 4d85ce16df1..96fe35362d5 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -1118,7 +1118,11 @@ static void btr_free_but_not_root( buf_block_t* block, - mtr_log_t log_mode) + mtr_log_t log_mode +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi=false +#endif + ) { mtr_t mtr; @@ -1147,7 +1151,11 @@ leaf_loop: fsp0fsp. */ bool finished = fseg_free_step(root + PAGE_HEADER + PAGE_BTR_SEG_LEAF, - &mtr); + &mtr +#ifdef BTR_CUR_HASH_ADAPT + , ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ); mtr_commit(&mtr); if (!finished) { @@ -1167,7 +1175,11 @@ top_loop: #endif /* UNIV_BTR_DEBUG */ finished = fseg_free_step_not_header( - root + PAGE_HEADER + PAGE_BTR_SEG_TOP, &mtr); + root + PAGE_HEADER + PAGE_BTR_SEG_TOP, &mtr +#ifdef BTR_CUR_HASH_ADAPT + ,ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ); mtr_commit(&mtr); if (!finished) { @@ -1191,12 +1203,22 @@ void dict_index_t::clear(que_thr_t *thr) table->space->zip_size(), RW_X_LATCH, &mtr)) { - btr_free_but_not_root(root_block, mtr.get_log_mode()); + btr_free_but_not_root(root_block, mtr.get_log_mode() +#ifdef BTR_CUR_HASH_ADAPT + ,n_ahi_pages() != 0 +#endif + ); + mtr.memset(root_block, PAGE_HEADER + PAGE_BTR_SEG_LEAF, FSEG_HEADER_SIZE, 0); if (fseg_create(table->space, PAGE_HEADER + PAGE_BTR_SEG_LEAF, &mtr, false, root_block)) btr_root_page_init(root_block, id, this, &mtr); +#ifdef BTR_CUR_HASH_ADAPT + if (root_block->index) + btr_search_drop_page_hash_index(root_block); + ut_ad(n_ahi_pages() == 0); +#endif } mtr.commit(); diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index 24bcbdbb949..18aa1285f73 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -2402,7 +2402,8 @@ try_to_extend: @param[in] seg_inode segment inode @param[in,out] space tablespace @param[in] offset page number -@param[in,out] mtr mini-transaction */ +@param[in,out] mtr mini-transaction +@param[in] ahi Drop adaptive hash index */ static void fseg_free_page_low( @@ -2410,7 +2411,11 @@ fseg_free_page_low( buf_block_t* iblock, fil_space_t* space, page_no_t offset, - mtr_t* mtr) + mtr_t* mtr +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi=false +#endif /* BTR_CUR_HASH_ADAPT */ + ) { ib_id_t descr_id; ib_id_t seg_id; @@ -2423,6 +2428,13 @@ fseg_free_page_low( ut_ad(iblock->frame == page_align(seg_inode)); ut_d(space->modify_check(*mtr)); +#ifdef BTR_CUR_HASH_ADAPT + if (ahi) { + btr_search_drop_page_hash_when_freed( + page_id_t(space->id, offset)); + } +#endif /* BTR_CUR_HASH_ADAPT */ + const uint32_t extent_size = FSP_EXTENT_SIZE; ut_ad(ut_is_2pow(extent_size)); buf_block_t* xdes; @@ -2593,7 +2605,11 @@ fseg_free_extent( buf_block_t* iblock, fil_space_t* space, uint32_t page, - mtr_t* mtr) + mtr_t* mtr +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi=false +#endif /* BTR_CUR_HASH_ADAPT */ + ) { ut_ad(mtr != NULL); @@ -2611,6 +2627,21 @@ fseg_free_extent( const uint16_t xoffset= uint16_t(descr - xdes->frame + XDES_FLST_NODE); const uint16_t ioffset= uint16_t(seg_inode - iblock->frame); +#ifdef BTR_CUR_HASH_ADAPT + if (ahi) { + for (uint32_t i = 0; i < FSP_EXTENT_SIZE; i++) { + if (!xdes_is_free(descr, i)) { + /* 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)) { flst_remove(iblock, static_cast<uint16_t>(FSEG_FULL + ioffset), xdes, xoffset, mtr); @@ -2631,26 +2662,31 @@ fseg_free_extent( fsp_free_extent(space, page, mtr); - for (ulint i = 0; i < FSP_EXTENT_SIZE; i++) { + for (uint32_t i = 0; i < FSP_EXTENT_SIZE; i++) { if (!xdes_is_free(descr, i)) { - buf_page_free(space, first_page_in_extent + 1, mtr); + buf_page_free(space, first_page_in_extent + i, mtr); } } } -/**********************************************************************//** -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. +/** 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. +@param header segment header; NOTE: if the header resides on first + page of the frag list of the segment, this pointer + becomes obsolete after the last freeing step +@param mtr mini-transaction +@param ahi Drop the adaptive hash index @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 */ - mtr_t* mtr) /*!< in/out: mini-transaction */ + fseg_header_t* header, + mtr_t* mtr +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ) { ulint n; fseg_inode_t* inode; @@ -2686,7 +2722,11 @@ fseg_free_step( if (descr != NULL) { /* Free the extent held by the segment */ fseg_free_extent(inode, iblock, space, xdes_get_offset(descr), - mtr); + mtr +#ifdef BTR_CUR_HASH_ADAPT + , ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ); DBUG_RETURN(false); } @@ -2703,7 +2743,11 @@ fseg_free_step( fseg_free_page_low( inode, iblock, space, fseg_get_nth_frag_page_no(inode, n), - mtr); + mtr +#ifdef BTR_CUR_HASH_ADAPT + , ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ); n = fseg_find_last_used_frag_page_slot(inode); @@ -2717,15 +2761,14 @@ fseg_free_step( DBUG_RETURN(false); } -/**********************************************************************//** -Frees part of a segment. Differs from fseg_free_step because this function -leaves the header page unfreed. -@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 */ - mtr_t* mtr) /*!< in/out: mini-transaction */ + fseg_header_t* header, + mtr_t* mtr +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ) { ulint n; xdes_t* descr; @@ -2748,7 +2791,11 @@ fseg_free_step_not_header( if (descr != NULL) { /* Free the extent held by the segment */ fseg_free_extent(inode, iblock, space, xdes_get_offset(descr), - mtr); + mtr +#ifdef BTR_CUR_HASH_ADAPT + , ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ); return false; } @@ -2764,7 +2811,11 @@ fseg_free_step_not_header( return true; } - fseg_free_page_low(inode, iblock, space, page_no, mtr); + fseg_free_page_low(inode, iblock, space, page_no, mtr +#ifdef BTR_CUR_HASH_ADAPT + , ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ); return false; } diff --git a/storage/innobase/include/fsp0fsp.h b/storage/innobase/include/fsp0fsp.h index 43c45dcee0c..10765852529 100644 --- a/storage/innobase/include/fsp0fsp.h +++ b/storage/innobase/include/fsp0fsp.h @@ -493,29 +493,42 @@ fseg_free_page( bool fseg_page_is_free(fil_space_t* space, unsigned page) MY_ATTRIBUTE((nonnull, warn_unused_result)); -/**********************************************************************//** -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. + +/** 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. +@param header segment header; NOTE: if the header resides on first + page of the frag list of the segment, this pointer + becomes obsolete after the last freeing step +@param mtr mini-transaction +@param ahi Drop the adaptive hash index @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 */ - mtr_t* mtr) /*!< in/out: mini-transaction */ + fseg_header_t* header, + mtr_t* mtr +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi=false +#endif /* BTR_CUR_HASH_ADAPT */ + ) MY_ATTRIBUTE((warn_unused_result)); -/**********************************************************************//** -Frees part of a segment. Differs from fseg_free_step because this function -leaves the header page unfreed. + +/** Frees part of a segment. Differs from fseg_free_step because +this function leaves the header page unfreed. +@param header segment header which must reside on the first + fragment page of the segment +@param mtr mini-transaction +@param ahi drop the adaptive hash index @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 */ - mtr_t* mtr) /*!< in/out: mini-transaction */ + fseg_header_t* header, + mtr_t* mtr +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi=false +#endif /* BTR_CUR_HASH_ADAPT */ + ) MY_ATTRIBUTE((warn_unused_result)); /** Reset the page type. |