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) {