summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2019-11-06 08:24:48 +0200
committerMarko Mäkelä <marko.makela@mariadb.com>2019-11-06 08:48:48 +0200
commitd7a2401750bb29dfdb45929a536539b9f17b347f (patch)
tree2e06b1abc82af53c12a755adc71b3f763d6eba33
parent7bc26de591c5de185e351e673f12aa69528ea75d (diff)
downloadmariadb-git-d7a2401750bb29dfdb45929a536539b9f17b347f.tar.gz
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 f166ec71b78fdf7a08ba413509cf00ad9e003b3c) 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.
-rw-r--r--mysql-test/suite/innodb/r/ibuf_not_empty.result1
-rw-r--r--mysql-test/suite/innodb/t/ibuf_not_empty.test30
-rw-r--r--storage/innobase/ibuf/ibuf0ibuf.cc83
3 files changed, 106 insertions, 8 deletions
diff --git a/mysql-test/suite/innodb/r/ibuf_not_empty.result b/mysql-test/suite/innodb/r/ibuf_not_empty.result
index 2c898b8916d..7c61e74850b 100644
--- a/mysql-test/suite/innodb/r/ibuf_not_empty.result
+++ b/mysql-test/suite/innodb/r/ibuf_not_empty.result
@@ -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;
diff --git a/mysql-test/suite/innodb/t/ibuf_not_empty.test b/mysql-test/suite/innodb/t/ibuf_not_empty.test
index 9ee0b180f44..8b16d197e03 100644
--- a/mysql-test/suite/innodb/t/ibuf_not_empty.test
+++ b/mysql-test/suite/innodb/t/ibuf_not_empty.test
@@ -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;
diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc
index e701271379e..57ff91fc14e 100644
--- a/storage/innobase/ibuf/ibuf0ibuf.cc
+++ b/storage/innobase/ibuf/ibuf0ibuf.cc
@@ -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) {