MDEV-24720 AHI removal during rollback of bulk insert

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ä
parent 1110becc
...@@ -13,3 +13,17 @@ SELECT * FROM t; ...@@ -13,3 +13,17 @@ SELECT * FROM t;
a b a b
1 3 1 3
DROP TEMPORARY TABLE t; 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;
--source include/have_innodb.inc --source include/have_innodb.inc
--source include/have_sequence.inc
--echo # --echo #
--echo # MDEV-24715 Assertion !node->table->skip_alter_undo --echo # MDEV-24715 Assertion !node->table->skip_alter_undo
...@@ -12,3 +13,16 @@ CREATE TEMPORARY TABLE t (a INT UNIQUE) ENGINE=InnoDB ...@@ -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; REPLACE SELECT 1 AS a, 2 AS b UNION SELECT 1 AS a, 3 AS c;
SELECT * FROM t; SELECT * FROM t;
DROP TEMPORARY TABLE 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;
...@@ -1118,7 +1118,11 @@ static ...@@ -1118,7 +1118,11 @@ static
void void
btr_free_but_not_root( btr_free_but_not_root(
buf_block_t* block, 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; mtr_t mtr;
...@@ -1147,7 +1151,11 @@ btr_free_but_not_root( ...@@ -1147,7 +1151,11 @@ btr_free_but_not_root(
fsp0fsp. */ fsp0fsp. */
bool finished = fseg_free_step(root + PAGE_HEADER + PAGE_BTR_SEG_LEAF, 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); mtr_commit(&mtr);
if (!finished) { if (!finished) {
...@@ -1167,7 +1175,11 @@ btr_free_but_not_root( ...@@ -1167,7 +1175,11 @@ btr_free_but_not_root(
#endif /* UNIV_BTR_DEBUG */ #endif /* UNIV_BTR_DEBUG */
finished = fseg_free_step_not_header( 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); mtr_commit(&mtr);
if (!finished) { if (!finished) {
...@@ -1191,12 +1203,22 @@ void dict_index_t::clear(que_thr_t *thr) ...@@ -1191,12 +1203,22 @@ void dict_index_t::clear(que_thr_t *thr)
table->space->zip_size(), table->space->zip_size(),
RW_X_LATCH, &mtr)) 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, mtr.memset(root_block, PAGE_HEADER + PAGE_BTR_SEG_LEAF,
FSEG_HEADER_SIZE, 0); FSEG_HEADER_SIZE, 0);
if (fseg_create(table->space, PAGE_HEADER + PAGE_BTR_SEG_LEAF, &mtr, false, if (fseg_create(table->space, PAGE_HEADER + PAGE_BTR_SEG_LEAF, &mtr, false,
root_block)) root_block))
btr_root_page_init(root_block, id, this, &mtr); 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(); mtr.commit();
......
...@@ -2402,7 +2402,8 @@ fsp_reserve_free_extents( ...@@ -2402,7 +2402,8 @@ fsp_reserve_free_extents(
@param[in] seg_inode segment inode @param[in] seg_inode segment inode
@param[in,out] space tablespace @param[in,out] space tablespace
@param[in] offset page number @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 static
void void
fseg_free_page_low( fseg_free_page_low(
...@@ -2410,7 +2411,11 @@ fseg_free_page_low( ...@@ -2410,7 +2411,11 @@ fseg_free_page_low(
buf_block_t* iblock, buf_block_t* iblock,
fil_space_t* space, fil_space_t* space,
page_no_t offset, 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 descr_id;
ib_id_t seg_id; ib_id_t seg_id;
...@@ -2423,6 +2428,13 @@ fseg_free_page_low( ...@@ -2423,6 +2428,13 @@ fseg_free_page_low(
ut_ad(iblock->frame == page_align(seg_inode)); ut_ad(iblock->frame == page_align(seg_inode));
ut_d(space->modify_check(*mtr)); 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; const uint32_t extent_size = FSP_EXTENT_SIZE;
ut_ad(ut_is_2pow(extent_size)); ut_ad(ut_is_2pow(extent_size));
buf_block_t* xdes; buf_block_t* xdes;
...@@ -2593,7 +2605,11 @@ fseg_free_extent( ...@@ -2593,7 +2605,11 @@ fseg_free_extent(
buf_block_t* iblock, buf_block_t* iblock,
fil_space_t* space, fil_space_t* space,
uint32_t page, 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); ut_ad(mtr != NULL);
...@@ -2611,6 +2627,21 @@ fseg_free_extent( ...@@ -2611,6 +2627,21 @@ fseg_free_extent(
const uint16_t xoffset= uint16_t(descr - xdes->frame + XDES_FLST_NODE); const uint16_t xoffset= uint16_t(descr - xdes->frame + XDES_FLST_NODE);
const uint16_t ioffset= uint16_t(seg_inode - iblock->frame); 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)) { if (xdes_is_full(descr)) {
flst_remove(iblock, static_cast<uint16_t>(FSEG_FULL + ioffset), flst_remove(iblock, static_cast<uint16_t>(FSEG_FULL + ioffset),
xdes, xoffset, mtr); xdes, xoffset, mtr);
...@@ -2638,19 +2669,24 @@ fseg_free_extent( ...@@ -2638,19 +2669,24 @@ fseg_free_extent(
} }
} }
/**********************************************************************//** /** Frees part of a segment. This function can be used to free
Frees part of a segment. This function can be used to free a segment by a segment by repeatedly calling this function in different
repeatedly calling this function in different mini-transactions. Doing mini-transactions. Doing the freeing in a single mini-transaction
the freeing in a single mini-transaction might result in too big a might result in too big a mini-transaction.
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 */ @return whether the freeing was completed */
bool bool
fseg_free_step( fseg_free_step(
fseg_header_t* header, /*!< in, own: segment header; NOTE: if the header fseg_header_t* header,
resides on the first page of the frag list mtr_t* mtr
of the segment, this pointer becomes obsolete #ifdef BTR_CUR_HASH_ADAPT
after the last freeing step */ ,bool ahi
mtr_t* mtr) /*!< in/out: mini-transaction */ #endif /* BTR_CUR_HASH_ADAPT */
)
{ {
ulint n; ulint n;
fseg_inode_t* inode; fseg_inode_t* inode;
...@@ -2686,7 +2722,11 @@ fseg_free_step( ...@@ -2686,7 +2722,11 @@ fseg_free_step(
if (descr != NULL) { if (descr != NULL) {
/* Free the extent held by the segment */ /* Free the extent held by the segment */
fseg_free_extent(inode, iblock, space, xdes_get_offset(descr), 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); DBUG_RETURN(false);
} }
...@@ -2702,7 +2742,11 @@ fseg_free_step( ...@@ -2702,7 +2742,11 @@ fseg_free_step(
page_no_t page_no = fseg_get_nth_frag_page_no(inode, n); page_no_t page_no = fseg_get_nth_frag_page_no(inode, n);
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 */
);
buf_page_free(space, page_no, mtr); buf_page_free(space, page_no, mtr);
...@@ -2718,15 +2762,14 @@ fseg_free_step( ...@@ -2718,15 +2762,14 @@ fseg_free_step(
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 whether the freeing was completed, except for the header page */
bool bool
fseg_free_step_not_header( fseg_free_step_not_header(
fseg_header_t* header, /*!< in: segment header which must reside on fseg_header_t* header,
the first fragment page of the segment */ mtr_t* mtr
mtr_t* mtr) /*!< in/out: mini-transaction */ #ifdef BTR_CUR_HASH_ADAPT
,bool ahi
#endif /* BTR_CUR_HASH_ADAPT */
)
{ {
ulint n; ulint n;
xdes_t* descr; xdes_t* descr;
...@@ -2749,7 +2792,11 @@ fseg_free_step_not_header( ...@@ -2749,7 +2792,11 @@ fseg_free_step_not_header(
if (descr != NULL) { if (descr != NULL) {
/* Free the extent held by the segment */ /* Free the extent held by the segment */
fseg_free_extent(inode, iblock, space, xdes_get_offset(descr), 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; return false;
} }
...@@ -2765,7 +2812,11 @@ fseg_free_step_not_header( ...@@ -2765,7 +2812,11 @@ fseg_free_step_not_header(
return true; 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 */
);
buf_page_free(space, page_no, mtr); buf_page_free(space, page_no, mtr);
return false; return false;
} }
......
...@@ -493,29 +493,42 @@ fseg_free_page( ...@@ -493,29 +493,42 @@ fseg_free_page(
bool bool
fseg_page_is_free(fil_space_t* space, unsigned page) fseg_page_is_free(fil_space_t* space, unsigned page)
MY_ATTRIBUTE((nonnull, warn_unused_result)); MY_ATTRIBUTE((nonnull, warn_unused_result));
/**********************************************************************//**
Frees part of a segment. This function can be used to free a segment /** Frees part of a segment. This function can be used to free
by repeatedly calling this function in different mini-transactions. a segment by repeatedly calling this function in different
Doing the freeing in a single mini-transaction might result in mini-transactions. Doing the freeing in a single mini-transaction
too big a 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 */ @return whether the freeing was completed */
bool bool
fseg_free_step( fseg_free_step(
fseg_header_t* header, /*!< in, own: segment header; NOTE: if the header fseg_header_t* header,
resides on the first page of the frag list mtr_t* mtr
of the segment, this pointer becomes obsolete #ifdef BTR_CUR_HASH_ADAPT
after the last freeing step */ ,bool ahi=false
mtr_t* mtr) /*!< in/out: mini-transaction */ #endif /* BTR_CUR_HASH_ADAPT */
)
MY_ATTRIBUTE((warn_unused_result)); MY_ATTRIBUTE((warn_unused_result));
/**********************************************************************//**
Frees part of a segment. Differs from fseg_free_step because this function /** Frees part of a segment. Differs from fseg_free_step because
leaves the header page unfreed. 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 */ @return whether the freeing was completed, except for the header page */
bool bool
fseg_free_step_not_header( fseg_free_step_not_header(
fseg_header_t* header, /*!< in: segment header which must reside on fseg_header_t* header,
the first fragment page of the segment */ mtr_t* mtr
mtr_t* mtr) /*!< in/out: mini-transaction */ #ifdef BTR_CUR_HASH_ADAPT
,bool ahi=false
#endif /* BTR_CUR_HASH_ADAPT */
)
MY_ATTRIBUTE((warn_unused_result)); MY_ATTRIBUTE((warn_unused_result));
/** Reset the page type. /** Reset the page type.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment