Skip to content

Commit

Permalink
MDEV-30106 InnoDB fails to validate the change buffer on startup
Browse files Browse the repository at this point in the history
ibuf_init_at_db_start(): Validate the change buffer root page.
A later version may stop creating a change buffer, and this
validation check will prevent a downgrade from such later versions.

ibuf_max_size_update(): If the change buffer was not loaded, do nothing.

dict_boot(): Merge the local variable "error" to "err". Ignore
failures of ibuf_init_at_db_start() if innodb_force_recovery>=4.
  • Loading branch information
dr-m committed Nov 28, 2022
1 parent e0d672f commit db14eb1
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 33 deletions.
1 change: 1 addition & 0 deletions mysql-test/suite/innodb/t/log_corruption.test
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ call mtr.add_suppression("InnoDB: Obtaining redo log encryption key version 1 fa
call mtr.add_suppression("InnoDB: Decrypting checkpoint failed");
call mtr.add_suppression("InnoDB: Are you sure you are using the right ib_logfile0 to start up the database\\? Log sequence number in the ib_logfile0 is 1213964,");
call mtr.add_suppression("InnoDB: Log file .*ib_logfile1 is of different size 1048576 bytes than other log files 2097152 bytes!");
call mtr.add_suppression("InnoDB: The change buffer is corrupted");
--enable_query_log

let bugdir= $MYSQLTEST_VARDIR/tmp/log_corruption;
Expand Down
26 changes: 14 additions & 12 deletions storage/innobase/dict/dict0boot.cc
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,9 @@ dict_boot(void)
dict_mem_index_add_field(index, "NAME", 0);

index->id = DICT_TABLES_ID;
dberr_t error = dict_index_add_to_cache(
dberr_t err = dict_index_add_to_cache(
index, mach_read_from_4(dict_hdr + DICT_HDR_TABLES));
ut_a(error == DB_SUCCESS);
ut_a(err == DB_SUCCESS);
ut_ad(!table->is_instant());
table->indexes.start->n_core_null_bytes = static_cast<uint8_t>(
UT_BITS_IN_BYTES(unsigned(table->indexes.start->n_nullable)));
Expand All @@ -325,9 +325,9 @@ dict_boot(void)
dict_mem_index_add_field(index, "ID", 0);

index->id = DICT_TABLE_IDS_ID;
error = dict_index_add_to_cache(
err = dict_index_add_to_cache(
index, mach_read_from_4(dict_hdr + DICT_HDR_TABLE_IDS));
ut_a(error == DB_SUCCESS);
ut_a(err == DB_SUCCESS);

/*-------------------------*/
table = dict_mem_table_create("SYS_COLUMNS", fil_system.sys_space,
Expand Down Expand Up @@ -355,9 +355,9 @@ dict_boot(void)
dict_mem_index_add_field(index, "POS", 0);

index->id = DICT_COLUMNS_ID;
error = dict_index_add_to_cache(
err = dict_index_add_to_cache(
index, mach_read_from_4(dict_hdr + DICT_HDR_COLUMNS));
ut_a(error == DB_SUCCESS);
ut_a(err == DB_SUCCESS);
ut_ad(!table->is_instant());
table->indexes.start->n_core_null_bytes = static_cast<uint8_t>(
UT_BITS_IN_BYTES(unsigned(table->indexes.start->n_nullable)));
Expand Down Expand Up @@ -398,9 +398,9 @@ dict_boot(void)
dict_mem_index_add_field(index, "ID", 0);

index->id = DICT_INDEXES_ID;
error = dict_index_add_to_cache(
err = dict_index_add_to_cache(
index, mach_read_from_4(dict_hdr + DICT_HDR_INDEXES));
ut_a(error == DB_SUCCESS);
ut_a(err == DB_SUCCESS);
ut_ad(!table->is_instant());
table->indexes.start->n_core_null_bytes = static_cast<uint8_t>(
UT_BITS_IN_BYTES(unsigned(table->indexes.start->n_nullable)));
Expand All @@ -427,9 +427,9 @@ dict_boot(void)
dict_mem_index_add_field(index, "POS", 0);

index->id = DICT_FIELDS_ID;
error = dict_index_add_to_cache(
err = dict_index_add_to_cache(
index, mach_read_from_4(dict_hdr + DICT_HDR_FIELDS));
ut_a(error == DB_SUCCESS);
ut_a(err == DB_SUCCESS);
ut_ad(!table->is_instant());
table->indexes.start->n_core_null_bytes = static_cast<uint8_t>(
UT_BITS_IN_BYTES(unsigned(table->indexes.start->n_nullable)));
Expand All @@ -440,9 +440,11 @@ dict_boot(void)

/* Initialize the insert buffer table and index for each tablespace */

dberr_t err = ibuf_init_at_db_start();
err = ibuf_init_at_db_start();

if (err == DB_SUCCESS) {
if (err == DB_SUCCESS
|| srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) {
err = DB_SUCCESS;
/* Load definitions of other indexes on system tables */

dict_load_sys_table(dict_sys.sys_tables);
Expand Down
55 changes: 35 additions & 20 deletions storage/innobase/ibuf/ibuf0ibuf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Created 7/19/1997 Heikki Tuuri
#include "que0que.h"
#include "srv0start.h" /* srv_shutdown_state */
#include "rem0cmp.h"
#include "log.h"

/* STRUCTURE OF AN INSERT BUFFER RECORD
Expand Down Expand Up @@ -408,35 +409,24 @@ ibuf_init_at_db_start(void)
ulint n_used;

ut_ad(!ibuf.index);
dberr_t err;
mtr_t mtr;
mtr.start();
compile_time_assert(IBUF_SPACE_ID == TRX_SYS_SPACE);
compile_time_assert(IBUF_SPACE_ID == 0);
mtr_x_lock_space(fil_system.sys_space, &mtr);
buf_block_t* header_page = buf_page_get(
buf_block_t* header_page = buf_page_get_gen(
page_id_t(IBUF_SPACE_ID, FSP_IBUF_HEADER_PAGE_NO),
0, RW_X_LATCH, &mtr);
0, RW_X_LATCH, nullptr, BUF_GET,
__FILE__, __LINE__, &mtr, &err);

if (!header_page) {
err_exit:
sql_print_error("InnoDB: The change buffer is corrupted");
mtr.commit();
return DB_DECRYPTION_FAILED;
return err;
}

/* At startup we intialize ibuf to have a maximum of
CHANGE_BUFFER_DEFAULT_SIZE in terms of percentage of the
buffer pool size. Once ibuf struct is initialized this
value is updated with the user supplied size by calling
ibuf_max_size_update(). */
ibuf.max_size = ((buf_pool_get_curr_size() >> srv_page_size_shift)
* CHANGE_BUFFER_DEFAULT_SIZE) / 100;

mutex_create(LATCH_ID_IBUF, &ibuf_mutex);

mutex_create(LATCH_ID_IBUF_PESSIMISTIC_INSERT,
&ibuf_pessimistic_insert_mutex);

mutex_enter(&ibuf_mutex);

fseg_n_reserved_pages(*header_page,
IBUF_HEADER + IBUF_TREE_SEG_HEADER
+ header_page->frame, &n_used, &mtr);
Expand All @@ -448,15 +438,39 @@ ibuf_init_at_db_start(void)
{
buf_block_t* block;

block = buf_page_get(
block = buf_page_get_gen(
page_id_t(IBUF_SPACE_ID, FSP_IBUF_TREE_ROOT_PAGE_NO),
0, RW_X_LATCH, &mtr);
0, RW_X_LATCH, nullptr, BUF_GET,
__FILE__, __LINE__, &mtr, &err);

if (!block) goto err_exit;

buf_block_dbg_add_level(block, SYNC_IBUF_TREE_NODE);

root = buf_block_get_frame(block);
}

if (page_is_comp(root) || fil_page_get_type(root) != FIL_PAGE_INDEX
|| btr_page_get_index_id(root) != DICT_IBUF_ID_MIN) {
err = DB_CORRUPTION;
goto err_exit;
}

/* At startup we initialize ibuf to have a maximum of
CHANGE_BUFFER_DEFAULT_SIZE in terms of percentage of the
buffer pool size. Once ibuf struct is initialized this
value is updated with the user supplied size by calling
ibuf_max_size_update(). */
ibuf.max_size = ((buf_pool_get_curr_size() >> srv_page_size_shift)
* CHANGE_BUFFER_DEFAULT_SIZE) / 100;

mutex_create(LATCH_ID_IBUF, &ibuf_mutex);

mutex_create(LATCH_ID_IBUF_PESSIMISTIC_INSERT,
&ibuf_pessimistic_insert_mutex);

mutex_enter(&ibuf_mutex);

ibuf_size_update(root);
mutex_exit(&ibuf_mutex);

Expand Down Expand Up @@ -507,6 +521,7 @@ ibuf_max_size_update(
ulint new_val) /*!< in: new value in terms of
percentage of the buffer pool size */
{
if (UNIV_UNLIKELY(!ibuf.index)) return;
ulint new_size = ((buf_pool_get_curr_size() >> srv_page_size_shift)
* new_val) / 100;
mutex_enter(&ibuf_mutex);
Expand Down
3 changes: 2 additions & 1 deletion storage/innobase/srv/srv0start.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2056,7 +2056,8 @@ void innodb_shutdown()
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
ut_ad(lock_sys.is_initialised() || !srv_was_started);
ut_ad(log_sys.is_initialised() || !srv_was_started);
ut_ad(ibuf.index || !srv_was_started);
ut_ad(ibuf.index || !srv_was_started
|| srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE);

dict_stats_deinit();

Expand Down

0 comments on commit db14eb1

Please sign in to comment.