mirror of
https://github.com/MariaDB/server.git
synced 2026-01-06 05:22:24 +03:00
MDEV-20934 Infinite loop on innodb_fast_shutdown=0 with inconsistent change buffer
Due to a data corruption bug that may have occurred a long time earlier
(possibly involving physical backup and MySQL Bug #69122, which was
addressed in commit f166ec71b7)
it seems possible that the InnoDB change buffer might end up containing
entries, while no buffered changes exist according to the change buffer
bitmap pages in the .ibd files.
ibuf_delete_recs(): New function, to be invoked on slow shutdown only.
Remove all buffered changes for a specific page.
ibuf_merge_or_delete_for_page(): If the change buffer bitmap is clean
and a slow shutdown is in progress, invoke ibuf_delete_recs().
We do not want to do that during normal operation, due to the additional
overhead that is involved. The bitmap page should be consistent with
the change buffer in the first place.
This commit is contained in:
@@ -22,4 +22,5 @@ check table t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check Warning InnoDB: Index 'b' contains #### entries, should be 4096.
|
||||
test.t1 check error Corrupt
|
||||
SET GLOBAL innodb_fast_shutdown=0;
|
||||
DROP TABLE t1;
|
||||
|
||||
@@ -40,15 +40,43 @@ INSERT INTO t1 SELECT 0,b,c FROM t1;
|
||||
INSERT INTO t1 SELECT 0,b,c FROM t1;
|
||||
INSERT INTO t1 SELECT 0,b,c FROM t1;
|
||||
INSERT INTO t1 SELECT 0,b,c FROM t1;
|
||||
let MYSQLD_DATADIR=`select @@datadir`;
|
||||
let PAGE_SIZE=`select @@innodb_page_size`;
|
||||
|
||||
--source include/shutdown_mysqld.inc
|
||||
|
||||
# Corrupt the change buffer bitmap, to claim that pages are clean
|
||||
perl;
|
||||
do "$ENV{MTR_SUITE_DIR}/include/crc32.pl";
|
||||
my $file = "$ENV{MYSQLD_DATADIR}/test/t1.ibd";
|
||||
open(FILE, "+<$file") || die "Unable to open $file";
|
||||
binmode FILE;
|
||||
my $ps= $ENV{PAGE_SIZE};
|
||||
my $page;
|
||||
sysseek(FILE, $ps, 0) || die "Unable to seek $file\n";
|
||||
die "Unable to read $file" unless sysread(FILE, $page, $ps) == $ps;
|
||||
# Clean the change buffer bitmap.
|
||||
substr($page,38,$ps - 38 - 8) = chr(0) x ($ps - 38 - 8);
|
||||
my $polynomial = 0x82f63b78; # CRC-32C
|
||||
my $ck= pack("N",mycrc32(substr($page, 4, 22), 0, $polynomial) ^
|
||||
mycrc32(substr($page, 38, $ps - 38 - 8), 0, $polynomial));
|
||||
substr($page,0,4)=$ck;
|
||||
substr($page,$ps-8,4)=$ck;
|
||||
sysseek(FILE, $ps, 0) || die "Unable to rewind $file\n";
|
||||
syswrite(FILE, $page, $ps)==$ps || die "Unable to write $file\n";
|
||||
close(FILE) || die "Unable to close $file";
|
||||
EOF
|
||||
|
||||
--let $restart_parameters= --innodb-force-recovery=6 --innodb-change-buffer-dump
|
||||
--source include/restart_mysqld.inc
|
||||
--source include/start_mysqld.inc
|
||||
|
||||
--replace_regex /contains \d+ entries/contains #### entries/
|
||||
check table t1;
|
||||
|
||||
--let $restart_parameters=
|
||||
--source include/restart_mysqld.inc
|
||||
SET GLOBAL innodb_fast_shutdown=0;
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
# Cleanup
|
||||
DROP TABLE t1;
|
||||
|
||||
@@ -2537,8 +2537,6 @@ ibuf_merge_space(
|
||||
|
||||
ut_ad(space < SRV_LOG_SPACE_FIRST_ID);
|
||||
|
||||
ut_ad(space < SRV_LOG_SPACE_FIRST_ID);
|
||||
|
||||
ibuf_mtr_start(&mtr);
|
||||
|
||||
/* Position the cursor on the first matching record. */
|
||||
@@ -4329,6 +4327,71 @@ func_exit:
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
Delete any buffered entries for a page.
|
||||
This prevents an infinite loop on slow shutdown
|
||||
in the case where the change buffer bitmap claims that no buffered
|
||||
changes exist, while entries exist in the change buffer tree.
|
||||
@param page_id page number for which there should be no unbuffered changes */
|
||||
ATTRIBUTE_COLD static void ibuf_delete_recs(const page_id_t page_id)
|
||||
{
|
||||
ulint dops[IBUF_OP_COUNT];
|
||||
mtr_t mtr;
|
||||
btr_pcur_t pcur;
|
||||
mem_heap_t* heap = mem_heap_create(512);
|
||||
const dtuple_t* tuple = ibuf_search_tuple_build(
|
||||
page_id.space(), page_id.page_no(), heap);
|
||||
memset(dops, 0, sizeof(dops));
|
||||
|
||||
loop:
|
||||
ibuf_mtr_start(&mtr);
|
||||
btr_pcur_open(ibuf->index, tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF,
|
||||
&pcur, &mtr);
|
||||
|
||||
if (!btr_pcur_is_on_user_rec(&pcur)) {
|
||||
ut_ad(btr_pcur_is_after_last_in_tree(&pcur, &mtr));
|
||||
goto func_exit;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ut_ad(btr_pcur_is_on_user_rec(&pcur));
|
||||
|
||||
const rec_t* ibuf_rec = btr_pcur_get_rec(&pcur);
|
||||
|
||||
if (ibuf_rec_get_space(&mtr, ibuf_rec)
|
||||
!= page_id.space()
|
||||
|| ibuf_rec_get_page_no(&mtr, ibuf_rec)
|
||||
!= page_id.page_no()) {
|
||||
break;
|
||||
}
|
||||
|
||||
dops[ibuf_rec_get_op_type(&mtr, ibuf_rec)]++;
|
||||
|
||||
/* Delete the record from ibuf */
|
||||
if (ibuf_delete_rec(page_id.space(), page_id.page_no(),
|
||||
&pcur, tuple, &mtr)) {
|
||||
/* Deletion was pessimistic and mtr was committed:
|
||||
we start from the beginning again */
|
||||
ut_ad(mtr.has_committed());
|
||||
goto loop;
|
||||
}
|
||||
|
||||
if (btr_pcur_is_after_last_on_page(&pcur)) {
|
||||
ibuf_mtr_commit(&mtr);
|
||||
btr_pcur_close(&pcur);
|
||||
goto loop;
|
||||
}
|
||||
}
|
||||
|
||||
func_exit:
|
||||
ibuf_mtr_commit(&mtr);
|
||||
btr_pcur_close(&pcur);
|
||||
|
||||
ibuf_add_ops(ibuf->n_discarded_ops, dops);
|
||||
|
||||
mem_heap_free(heap);
|
||||
}
|
||||
|
||||
/** When an index page is read from a disk to the buffer pool, this function
|
||||
applies any buffered operations to the page and deletes the entries from the
|
||||
insert buffer. If the page is not read, but created in the buffer pool, this
|
||||
@@ -4348,9 +4411,7 @@ ibuf_merge_or_delete_for_page(
|
||||
const page_size_t* page_size,
|
||||
ibool update_ibuf_bitmap)
|
||||
{
|
||||
mem_heap_t* heap;
|
||||
btr_pcur_t pcur;
|
||||
dtuple_t* search_tuple;
|
||||
#ifdef UNIV_IBUF_DEBUG
|
||||
ulint volume = 0;
|
||||
#endif /* UNIV_IBUF_DEBUG */
|
||||
@@ -4423,9 +4484,17 @@ ibuf_merge_or_delete_for_page(
|
||||
ibuf_mtr_commit(&mtr);
|
||||
|
||||
if (!bitmap_bits) {
|
||||
/* No inserts buffered for this page */
|
||||
/* No changes are buffered for this page. */
|
||||
|
||||
fil_space_release(space);
|
||||
if (UNIV_UNLIKELY(srv_shutdown_state)
|
||||
&& !srv_fast_shutdown) {
|
||||
/* Prevent an infinite loop on slow
|
||||
shutdown, in case the bitmap bits are
|
||||
wrongly clear even though buffered
|
||||
changes exist. */
|
||||
ibuf_delete_recs(page_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -4438,9 +4507,9 @@ ibuf_merge_or_delete_for_page(
|
||||
space = NULL;
|
||||
}
|
||||
|
||||
heap = mem_heap_create(512);
|
||||
mem_heap_t* heap = mem_heap_create(512);
|
||||
|
||||
search_tuple = ibuf_search_tuple_build(
|
||||
const dtuple_t* search_tuple = ibuf_search_tuple_build(
|
||||
page_id.space(), page_id.page_no(), heap);
|
||||
|
||||
if (block != NULL) {
|
||||
|
||||
Reference in New Issue
Block a user