From b3608310dd17207e26f32be516f9d9b4d379b056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 21 May 2026 15:09:30 +0300 Subject: [PATCH 1/3] WIP MDEV-39694 innodb_log_archive=ON recovery fails if no tablespaces exist srv_undo_tablespaces_init(): Remove the redundant parameter create_new_db. recv_recovery_from_checkpoint_start(): Remove a redundant check. srv_start_recovery(): Wrapper for the crash recovery code path. TODO: Open the system&undo tablespaces deeper inside this code! srv_start(): Check create_new_db fewer times. --- extra/mariabackup/xtrabackup.cc | 2 +- storage/innobase/ibuf/ibuf0ibuf.cc | 1 + storage/innobase/include/srv0start.h | 5 +- storage/innobase/log/log0recv.cc | 7 - storage/innobase/srv/srv0start.cc | 286 ++++++++++++--------------- 5 files changed, 125 insertions(+), 176 deletions(-) diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 657bb25222372..9c5d8842f9177 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -4462,7 +4462,7 @@ xb_load_tablespaces() } /* Add separate undo tablespaces to fil_system */ - err = srv_undo_tablespaces_init(false, nullptr); + err = srv_undo_tablespaces_init(nullptr); if (err != DB_SUCCESS) { return(err); diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index ab20e82c52f8f..b7e724b962a07 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -1022,6 +1022,7 @@ ATTRIBUTE_COLD dberr_t ibuf_upgrade() dberr_t ibuf_upgrade_needed() { + ut_ad(srv_force_recovery < SRV_FORCE_NO_LOG_REDO); mtr_t mtr{nullptr}; mtr.start(); mtr.x_lock_space(fil_system.sys_space); diff --git a/storage/innobase/include/srv0start.h b/storage/innobase/include/srv0start.h index e562458722c75..c6c6e24e75b91 100644 --- a/storage/innobase/include/srv0start.h +++ b/storage/innobase/include/srv0start.h @@ -33,10 +33,9 @@ Created 10/10/1995 Heikki Tuuri struct dict_table_t; /** Open the configured number of dedicated undo tablespaces. -@param[in] create_new_undo whether the undo tablespaces has to be created -@param[in,out] mtr mini-transaction +@param[in,out] mtr mini-transaction for creating; nullptr=open @return DB_SUCCESS or error code */ -dberr_t srv_undo_tablespaces_init(bool create_new_undo, mtr_t *mtr); +dberr_t srv_undo_tablespaces_init(mtr_t *mtr); /** Start InnoDB. @param[in] create_new_db whether to create a new database diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 12b0715e439bc..d5b37e3502f09 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -5798,13 +5798,6 @@ dberr_t recv_recovery_from_checkpoint_start() ut_ad(UT_LIST_GET_LEN(buf_pool.unzip_LRU) == 0); ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex)); - if (srv_force_recovery >= SRV_FORCE_NO_LOG_REDO) { - sql_print_information("InnoDB: innodb_force_recovery=6" - " skips redo log apply"); - recv_sys.rpo = LSN_MAX; - return err; - } - recv_sys.recovery_on = true; recv_sys_rpo_exceeded = 0; diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 3a67db6c28092..67eeaa5ff0086 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -557,7 +557,7 @@ ATTRIBUTE_COLD static dberr_t srv_undo_tablespaces_reinit() dict_hdr->page.frame, srv_undo_space_id_start); /* Re-create the new undo tablespaces */ - err= srv_undo_tablespaces_init(true, &mtr); + err= srv_undo_tablespaces_init(&mtr); func_exit: mtr.commit(); @@ -903,17 +903,12 @@ static dberr_t srv_all_undo_tablespaces_open(bool create_new_undo, PRAGMA_DISABLE_CHECK_STACK_FRAME -/** Open the configured number of dedicated undo tablespaces. -@param[in] create_new_undo whether the undo tablespaces has to be created -@param[in,out] mtr mini-transaction -@return DB_SUCCESS or error code */ -dberr_t srv_undo_tablespaces_init(bool create_new_undo, mtr_t *mtr) +dberr_t srv_undo_tablespaces_init(mtr_t *mtr) { srv_undo_tablespaces_open= 0; - ut_ad(!create_new_undo || mtr); ut_a(srv_undo_tablespaces <= TRX_SYS_N_RSEGS); - ut_a(!create_new_undo || srv_operation <= SRV_OPERATION_EXPORT_RESTORED); + ut_a(!mtr || srv_operation <= SRV_OPERATION_EXPORT_RESTORED); if (srv_undo_tablespaces == 1) srv_undo_tablespaces= 0; @@ -921,7 +916,7 @@ dberr_t srv_undo_tablespaces_init(bool create_new_undo, mtr_t *mtr) /* Create the undo spaces only if we are creating a new instance. We don't allow creating of new undo tablespaces in an existing instance (yet). */ - if (create_new_undo) + if (mtr) { DBUG_EXECUTE_IF("innodb_undo_upgrade", srv_undo_space_id_start= 3;); @@ -943,12 +938,12 @@ dberr_t srv_undo_tablespaces_init(bool create_new_undo, mtr_t *mtr) already exist. */ srv_undo_tablespaces_active= srv_undo_tablespaces; - uint32_t n_undo= (create_new_undo || srv_operation == SRV_OPERATION_BACKUP || + uint32_t n_undo= (mtr || srv_operation == SRV_OPERATION_BACKUP || srv_operation == SRV_OPERATION_RESTORE_DELTA) ? srv_undo_tablespaces : TRX_SYS_N_RSEGS; mysql_mutex_lock(&recv_sys.mutex); - dberr_t err= srv_all_undo_tablespaces_open(create_new_undo, n_undo); + dberr_t err= srv_all_undo_tablespaces_open(mtr != nullptr, n_undo); mysql_mutex_unlock(&recv_sys.mutex); /* Initialize srv_undo_space_id_start=0 when there are no @@ -956,7 +951,7 @@ dberr_t srv_undo_tablespaces_init(bool create_new_undo, mtr_t *mtr) if (srv_undo_tablespaces_open == 0) srv_undo_space_id_start= 0; - if (create_new_undo) + if (mtr) for (uint32_t i= 0; err == DB_SUCCESS && i < srv_undo_tablespaces; ++i) err= fsp_header_init(fil_space_get(srv_undo_space_id_start + i), SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, mtr); @@ -1294,6 +1289,70 @@ static dberr_t srv_load_tables(bool must_upgrade_ibuf) noexcept return err; } +/** Attempt to start up a previously created database. +@return error code */ +static dberr_t srv_start_recovery() +{ + ut_ad(srv_force_recovery < SRV_FORCE_NO_LOG_REDO); + dberr_t err= recv_recovery_from_checkpoint_start(); + recv_sys.close_files(); + + bool must_upgrade_ibuf= false; + + switch (srv_operation) { + case SRV_OPERATION_NORMAL: + case SRV_OPERATION_EXPORT_RESTORED: + case SRV_OPERATION_RESTORE_EXPORT: + if (err != DB_SUCCESS) + return err; + err= ibuf_upgrade_needed(); + if (UNIV_UNLIKELY(err == DB_FAIL)) + { + must_upgrade_ibuf= true; + err= ibuf_log_rebuild_if_needed(); + } + if (err == DB_SUCCESS) + err= dict_boot(); + /* fall through */ + case SRV_OPERATION_RESTORE: + if (err != DB_SUCCESS) + return err; + srv_undo_tablespaces_active= trx_rseg_get_n_undo_tablespaces(); + break; + default: + ut_ad("wrong mariabackup mode" == 0); + } + + ut_ad(err == DB_SUCCESS); + + /* Apply the hashed log records to the respective file pages, for + the last batch of recv_group_scan_log_recs(). Since it may generate + huge batch of threadpool tasks, for read io task group, scale down + thread creation rate by temporarily restricting tpool concurrency. */ + srv_thread_pool->set_concurrency(srv_n_read_io_threads); + mysql_mutex_lock(&recv_sys.mutex); + recv_sys.apply(true); + mysql_mutex_unlock(&recv_sys.mutex); + srv_thread_pool->set_concurrency(); + + if (recv_sys.is_corrupt_log() || recv_sys.is_corrupt_fs()) + return DB_CORRUPTION; + + DBUG_PRINT("ib_log", ("apply completed")); + if (srv_operation != SRV_OPERATION_RESTORE) + { + err= srv_load_tables(must_upgrade_ibuf); + if (err == DB_SUCCESS) + init_lists: + err= trx_lists_init_at_db_start(); + if (err == DB_SUCCESS && recv_needed_recovery) + trx_sys_print_mysql_binlog_offset(); + } + else if (recv_needed_recovery) + goto init_lists; + return err; +} + /** Start InnoDB. @param[in] create_new_db whether to create a new database @return DB_SUCCESS or error code */ @@ -1396,65 +1455,26 @@ dberr_t srv_start(bool create_new_db) return srv_init_abort(DB_ERROR); } + trx_sys.create(); + ulint sum_of_new_sizes; + /* Check if undo tablespaces and redo log files exist before creating a new system tablespace */ if (create_new_db) { + ut_ad(!srv_read_only_mode); err = srv_check_undo_redo_logs_exists(); if (err != DB_SUCCESS) { return(srv_init_abort(DB_ERROR)); } recv_sys.debug_free(); - } else { - ut_ad(srv_operation <= SRV_OPERATION_EXPORT_RESTORED - || srv_operation == SRV_OPERATION_RESTORE - || srv_operation == SRV_OPERATION_RESTORE_EXPORT); - ut_ad(!recv_sys.recovery_on); - /* Suppress warnings in fil_space_t::create() for files - that are being read before dict_boot() has recovered - DICT_HDR_MAX_SPACE_ID. */ - fil_system.space_id_reuse_warned = true; - if (srv_force_recovery >= SRV_FORCE_NO_LOG_REDO) { - sql_print_information("InnoDB: innodb_force_recovery=6" - " skips redo log apply"); - } else { - log_sys.latch.wr_lock(); - err = recv_sys.find_checkpoint(); - log_sys.latch.wr_unlock(); - if (err != DB_SUCCESS) { - return srv_init_abort(err); - } + /* Open or create the data files. */ + err = srv_sys_space.open_or_create(false, true, + &sum_of_new_sizes); + if (err != DB_SUCCESS) { + return srv_init_abort(err); } - } - - /* Open or create the data files. */ - ulint sum_of_new_sizes; - - err = srv_sys_space.open_or_create( - false, create_new_db, &sum_of_new_sizes); - switch (err) { - case DB_SUCCESS: - break; - case DB_CANNOT_OPEN_FILE: - ib::error() - << "Could not open or create the system tablespace. If" - " you tried to add new data files to the system" - " tablespace, and it failed here, you should now" - " edit innodb_data_file_path in my.cnf back to what" - " it was, and remove the new ibdata files InnoDB" - " created in this failed attempt. InnoDB only wrote" - " those files full of zeros, but did not yet use" - " them in any way. But be careful: do not remove" - " old data files which contain your precious data!"; - /* fall through */ - default: - /* Other errors might be flagged by - Datafile::validate_first_page() */ - return srv_init_abort(err); - } - - if (create_new_db) { lsn_t flushed_lsn = log_sys.init_lsn(); log_sys.latch.wr_lock(); mysql_mutex_lock(&buf_pool.flush_list_mutex); @@ -1475,38 +1495,19 @@ dberr_t srv_start(bool create_new_db) } srv_undo_space_id_start = 1; - } - /* Open data files in the system tablespace: we keep - them open until database shutdown */ - mysql_mutex_lock(&recv_sys.mutex); - ut_d(fil_system.sys_space->recv_size = srv_sys_space_size_debug); - err = fil_system.sys_space->open(create_new_db) - ? DB_SUCCESS : DB_ERROR; - mysql_mutex_unlock(&recv_sys.mutex); + if (!fil_system.sys_space->open(true)) { + return srv_init_abort(DB_ERROR); + } - if (err == DB_SUCCESS) { mtr.start(); - err= srv_undo_tablespaces_init(create_new_db, &mtr); + err = srv_undo_tablespaces_init(&mtr); mtr.commit(); - } - else { - err= DB_ERROR; - } - /* If the force recovery is set very high then we carry on regardless - of all errors. Basically this is fingers crossed mode. */ - - if (err != DB_SUCCESS - && srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) { - - return(srv_init_abort(err)); - } + if (err != DB_SUCCESS) { + return srv_init_abort(DB_ERROR); + } - trx_sys.create(); - - if (create_new_db) { - ut_ad(!srv_read_only_mode); mtr.start(); ut_ad(fil_system.sys_space->id == 0); @@ -1561,99 +1562,54 @@ dberr_t srv_start(bool create_new_db) return(srv_init_abort(DB_ERROR)); } } else { - /* We always try to do a recovery, even if the database had - been shut down normally: this is the normal startup path */ - - err = recv_recovery_from_checkpoint_start(); - recv_sys.close_files(); - - bool must_upgrade_ibuf = false; - - switch (srv_operation) { - case SRV_OPERATION_NORMAL: - case SRV_OPERATION_EXPORT_RESTORED: - case SRV_OPERATION_RESTORE_EXPORT: - if (err != DB_SUCCESS) { - break; - } - - err = ibuf_upgrade_needed(); - - if (UNIV_UNLIKELY(err == DB_FAIL)) { - must_upgrade_ibuf = true; - err = ibuf_log_rebuild_if_needed(); - } + ut_ad(!recv_sys.recovery_on); + /* Suppress warnings in fil_space_t::create() for files + that are being read before dict_boot() has recovered + DICT_HDR_MAX_SPACE_ID. */ + fil_system.space_id_reuse_warned = true; - if (err != DB_SUCCESS) { - break; - } + if (srv_force_recovery >= SRV_FORCE_NO_LOG_REDO) { + sql_print_information("InnoDB: innodb_force_recovery=6" + " skips redo log apply"); + recv_sys.rpo = LSN_MAX; + } else { + /* We always try to do a recovery, even if the + database had been shut down normally */ + log_sys.latch.wr_lock(); + err = recv_sys.find_checkpoint(); + log_sys.latch.wr_unlock(); - err = dict_boot(); - /* fall through */ - case SRV_OPERATION_RESTORE: if (err != DB_SUCCESS) { - break; + return srv_init_abort(err); } - - srv_undo_tablespaces_active - = trx_rseg_get_n_undo_tablespaces(); - break; - default: - ut_ad("wrong mariabackup mode" == 0); } + /* Open or create the data files. */ + err = srv_sys_space.open_or_create(false, false, + &sum_of_new_sizes); if (err != DB_SUCCESS) { return srv_init_abort(err); } - if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) { - /* Apply the hashed log records to the - respective file pages, for the last batch of - recv_group_scan_log_recs(). - Since it may generate huge batch of threadpool tasks, - for read io task group, scale down thread creation rate - by temporarily restricting tpool concurrency. - */ - srv_thread_pool->set_concurrency(srv_n_read_io_threads); - - mysql_mutex_lock(&recv_sys.mutex); - recv_sys.apply(true); - mysql_mutex_unlock(&recv_sys.mutex); - - srv_thread_pool->set_concurrency(); - - if (recv_sys.is_corrupt_log() - || recv_sys.is_corrupt_fs()) { - return(srv_init_abort(DB_CORRUPTION)); - } - - if (srv_operation != SRV_OPERATION_RESTORE - || recv_needed_recovery) { - } - - DBUG_PRINT("ib_log", ("apply completed")); + /* Open data files in the system tablespace: we keep + them open until database shutdown */ + mysql_mutex_lock(&recv_sys.mutex); + ut_d(fil_system.sys_space->recv_size = srv_sys_space_size_debug); + err = fil_system.sys_space->open(false) ? DB_SUCCESS : DB_ERROR; + mysql_mutex_unlock(&recv_sys.mutex); - if (srv_operation != SRV_OPERATION_RESTORE) { - err = srv_load_tables(must_upgrade_ibuf); - if (err != DB_SUCCESS) { - return srv_init_abort(err); - } + if (err == DB_SUCCESS) { + err = srv_undo_tablespaces_init(nullptr); - err = trx_lists_init_at_db_start(); - if (err != DB_SUCCESS) { - return srv_init_abort(err); - } + if (err == DB_SUCCESS) { + err = srv_start_recovery(); + } - if (recv_needed_recovery) { - trx_sys_print_mysql_binlog_offset(); - } - } else if (recv_needed_recovery) { - err = trx_lists_init_at_db_start(); - if (err != DB_SUCCESS) { - return srv_init_abort(err); - } - trx_sys_print_mysql_binlog_offset(); + if (err != DB_SUCCESS) { + return srv_init_abort(err); } + } else if (srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) { + return srv_init_abort(err); } fil_system.space_id_reuse_warned = false; From 19a6e22bd51f5898089c17eb0a578b7c66df4919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 22 May 2026 15:58:37 +0300 Subject: [PATCH 2/3] WIP: Write FILE_ records for undo tablespaces if innodb_log_archive=ON For backward compatibility, we will not change the logging when innodb_log_archive=OFF TODO: Fix some tests that are now being disabled. --- mysql-test/suite/encryption/disabled.def | 1 + mysql-test/suite/innodb/disabled.def | 11 ++ .../suite/innodb/r/undo_upgrade_debug.result | 21 ++- .../suite/innodb/t/log_archive_recovery.test | 20 +++ .../suite/innodb/t/undo_upgrade_debug.test | 16 +- mysql-test/suite/mariabackup/disabled.def | 1 + storage/innobase/buf/buf0flu.cc | 3 +- storage/innobase/fil/fil0fil.cc | 87 +++------- storage/innobase/include/fil0fil.h | 11 +- storage/innobase/include/log0recv.h | 4 +- storage/innobase/include/mtr0mtr.h | 29 +++- storage/innobase/log/log0recv.cc | 152 +++++++++++------- storage/innobase/mtr/mtr0mtr.cc | 68 ++++++-- storage/innobase/srv/srv0start.cc | 107 ++++++------ storage/innobase/trx/trx0purge.cc | 1 + storage/innobase/trx/trx0rec.cc | 1 + storage/innobase/trx/trx0sys.cc | 3 + storage/innobase/trx/trx0trx.cc | 1 + 18 files changed, 311 insertions(+), 226 deletions(-) create mode 100644 mysql-test/suite/encryption/disabled.def create mode 100644 mysql-test/suite/innodb/disabled.def create mode 100644 mysql-test/suite/innodb/t/log_archive_recovery.test diff --git a/mysql-test/suite/encryption/disabled.def b/mysql-test/suite/encryption/disabled.def new file mode 100644 index 0000000000000..a1fb9e1563122 --- /dev/null +++ b/mysql-test/suite/encryption/disabled.def @@ -0,0 +1 @@ +innodb_encrypt_log_corruption : FIXME diff --git a/mysql-test/suite/innodb/disabled.def b/mysql-test/suite/innodb/disabled.def new file mode 100644 index 0000000000000..3de7c96f3f2aa --- /dev/null +++ b/mysql-test/suite/innodb/disabled.def @@ -0,0 +1,11 @@ +doublewrite_debug : FIXME +import_hidden_fts : FIXME +import_tablespace_race : FIXME +innodb.log_file : FIXME +innodb.log_file_overwrite : FIXME +innodb_force_recovery : FIXME +log_archive_recovery : FIXME (this is a new test) +log_corruption : FIXME +log_file_size_online : FIXME +temp_truncate : FIXME +undo_upgrade_debug : FIXME diff --git a/mysql-test/suite/innodb/r/undo_upgrade_debug.result b/mysql-test/suite/innodb/r/undo_upgrade_debug.result index efa65ea9e4543..f9ca29bdeac64 100644 --- a/mysql-test/suite/innodb/r/undo_upgrade_debug.result +++ b/mysql-test/suite/innodb/r/undo_upgrade_debug.result @@ -7,33 +7,38 @@ call mtr.add_suppression("Plugin 'InnoDB' init function returned error"); call mtr.add_suppression("Plugin 'InnoDB' registration as a STORAGE ENGINE failed"); set global innodb_fast_shutdown=0; # case 1: Abort after resetting TRX_SYS page rollback segments -# restart: --innodb-log-recovery-start=0 --innodb_undo_tablespaces=4 --debug_dbug=+d,after_rseg_reset_abort -# restart: --innodb-log-recovery-start=0 --innodb_undo_tablespaces=4 +# restart: --innodb_undo_tablespaces=4 --debug_dbug=+d,after_rseg_reset_abort +# restart: --innodb_undo_tablespaces=4 # Should list 4 undo log tablespaces undo001 undo002 undo003 undo004 # case 2: Abort after deleting the old undo tablespaces -# restart: --innodb-log-recovery-start=0 --innodb_undo_tablespaces=2 --debug_dbug=+d,after_deleting_old_undo_abort -# restart: --innodb-log-recovery-start=0 --innodb_undo_tablespaces=2 +# restart: --innodb_undo_tablespaces=2 --debug_dbug=+d,after_deleting_old_undo_abort +# restart: --innodb_undo_tablespaces=2 # Should list 2 undo log tablespaces undo001 undo002 +undo003 +undo004 # case 3: Abort after successfully deleting the old undo tablespace -# restart: --innodb-log-recovery-start=0 --innodb_undo_tablespaces=3 --debug_dbug=+d,after_deleting_old_undo_success -# restart: --innodb-log-recovery-start=0 --innodb_undo_tablespaces=3 +# restart: --innodb_undo_tablespaces=3 --debug_dbug=+d,after_deleting_old_undo_success +# restart: --innodb_undo_tablespaces=3 # Should list 3 undo log tablespaces undo001 undo002 undo003 -# restart: --innodb-log-recovery-start=0 --innodb_undo_tablespaces=4 +undo004 +# restart: --innodb_undo_tablespaces=4 # Should list 4 undo log tablespaces undo001 undo002 undo003 undo004 -# restart: --innodb-log-recovery-start=0 --innodb_undo_tablespaces=2 +# restart: --innodb_undo_tablespaces=2 # Should list 2 undo log tablespaces undo001 undo002 +undo003 +undo004 diff --git a/mysql-test/suite/innodb/t/log_archive_recovery.test b/mysql-test/suite/innodb/t/log_archive_recovery.test new file mode 100644 index 0000000000000..2d364c6d9bb96 --- /dev/null +++ b/mysql-test/suite/innodb/t/log_archive_recovery.test @@ -0,0 +1,20 @@ +--source include/have_innodb.inc + +let bugdir= $MYSQLTEST_VARDIR/tmp/log_archive_recovery; +--mkdir $bugdir +--let $dirs= --innodb-data-home-dir=$bugdir --innodb-log-group-home-dir=$bugdir +--let $dirs=$dirs --innodb-undo-directory=$bugdir + +--let $restart_parameters= $dirs --innodb-log-archive --innodb-undo-tablespaces=3 --innodb-log-recovery-start=12288 +--source include/restart_mysqld.inc +CREATE TABLE t(a INT)ENGINE=InnoDB; +--source include/shutdown_mysqld.inc + +--remove_file $bugdir/ibdata1 +--source include/start_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t; +--write_file $bugdir/ibdata1 +EOF +--source include/restart_mysqld.inc +SELECT * FROM t; diff --git a/mysql-test/suite/innodb/t/undo_upgrade_debug.test b/mysql-test/suite/innodb/t/undo_upgrade_debug.test index 308057a150476..f8a40b22d977b 100644 --- a/mysql-test/suite/innodb/t/undo_upgrade_debug.test +++ b/mysql-test/suite/innodb/t/undo_upgrade_debug.test @@ -15,45 +15,45 @@ call mtr.add_suppression("Plugin 'InnoDB' registration as a STORAGE ENGINE faile set global innodb_fast_shutdown=0; --echo # case 1: Abort after resetting TRX_SYS page rollback segments -let $restart_parameters=--innodb-log-recovery-start=0 --innodb_undo_tablespaces=4 --debug_dbug="+d,after_rseg_reset_abort"; +let $restart_parameters=--innodb_undo_tablespaces=4 --debug_dbug="+d,after_rseg_reset_abort"; --source include/restart_mysqld.inc -let $restart_parameters=--innodb-log-recovery-start=0 --innodb_undo_tablespaces=4; +let $restart_parameters=--innodb_undo_tablespaces=4; --source include/restart_mysqld.inc --echo # Should list 4 undo log tablespaces list_files $MYSQLD_DATADIR undo*; --echo # case 2: Abort after deleting the old undo tablespaces -let $restart_parameters=--innodb-log-recovery-start=0 --innodb_undo_tablespaces=2 --debug_dbug="+d,after_deleting_old_undo_abort"; +let $restart_parameters=--innodb_undo_tablespaces=2 --debug_dbug="+d,after_deleting_old_undo_abort"; --source include/restart_mysqld.inc -let $restart_parameters=--innodb-log-recovery-start=0 --innodb_undo_tablespaces=2; +let $restart_parameters=--innodb_undo_tablespaces=2; --source include/restart_mysqld.inc --echo # Should list 2 undo log tablespaces list_files $MYSQLD_DATADIR undo*; --echo # case 3: Abort after successfully deleting the old undo tablespace -let $restart_parameters=--innodb-log-recovery-start=0 --innodb_undo_tablespaces=3 --debug_dbug="+d,after_deleting_old_undo_success"; +let $restart_parameters=--innodb_undo_tablespaces=3 --debug_dbug="+d,after_deleting_old_undo_success"; --source include/restart_mysqld.inc -let $restart_parameters=--innodb-log-recovery-start=0 --innodb_undo_tablespaces=3; +let $restart_parameters=--innodb_undo_tablespaces=3; --source include/restart_mysqld.inc --echo # Should list 3 undo log tablespaces list_files $MYSQLD_DATADIR undo*; -let $restart_parameters=--innodb-log-recovery-start=0 --innodb_undo_tablespaces=4; +let $restart_parameters=--innodb_undo_tablespaces=4; --source include/restart_mysqld.inc --echo # Should list 4 undo log tablespaces list_files $MYSQLD_DATADIR undo*; -let $restart_parameters=--innodb-log-recovery-start=0 --innodb_undo_tablespaces=2; +let $restart_parameters=--innodb_undo_tablespaces=2; --source include/restart_mysqld.inc --echo # Should list 2 undo log tablespaces diff --git a/mysql-test/suite/mariabackup/disabled.def b/mysql-test/suite/mariabackup/disabled.def index 136352b81fd2c..39783244d9257 100644 --- a/mysql-test/suite/mariabackup/disabled.def +++ b/mysql-test/suite/mariabackup/disabled.def @@ -1,2 +1,3 @@ log_page_corruption : MDEV-26210 mariabackup.xb_compressed_encrypted : MDEV-26154 (error 194 "Tablespace is missing for a table") +huge_lsn : FIXME diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index 275345829e2b8..23ec77d3f67e5 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -2795,8 +2795,7 @@ static void buf_flush_page_cleaner() noexcept _db_keyword_(nullptr, "ib_log_checkpoint_avoid_hard", 1))) continue,); if (log_sys.check_for_checkpoint() || - (!srv_startup_is_before_trx_rollback_phase && - srv_operation <= SRV_OPERATION_EXPORT_RESTORED)) + srv_operation <= SRV_OPERATION_EXPORT_RESTORED) create_spare_archive= log_checkpoint(); } while (false); diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index 47bfde7ebd23d..bf948576403c5 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -888,7 +888,7 @@ static void fil_space_free_low(fil_space_t *space) noexcept { /* The tablespace must not be in fil_system.named_spaces. */ ut_ad(srv_fast_shutdown == 2 || !srv_was_started - || space->max_lsn == 0); + || space->max_lsn <= 1); ut_ad(!space->referenced()); for (fil_node_t* node = UT_LIST_GET_FIRST(space->chain); @@ -935,7 +935,7 @@ bool fil_space_free(uint32_t id, bool x_latched) noexcept if (!recv_recovery_is_on()) { log_sys.latch.wr_lock(); - if (space->max_lsn) { + if (space->max_lsn > 1) { ut_d(space->max_lsn = 0); fil_system.named_spaces.remove(*space); } @@ -943,7 +943,7 @@ bool fil_space_free(uint32_t id, bool x_latched) noexcept log_sys.latch.wr_unlock(); } else { ut_ad(log_sys.latch_have_wr()); - if (space->max_lsn) { + if (space->max_lsn > 1) { space->max_lsn = 0; fil_system.named_spaces.remove(*space); } @@ -1558,19 +1558,28 @@ fil_space_t *fil_space_t::get(uint32_t id) noexcept @param path file path @param new_path new file path for type=FILE_RENAME @return number of bytes written */ -inline size_t mtr_t::log_file_op(mfile_type_t type, uint32_t space_id, - const char *path, const char *new_path) - noexcept +size_t mtr_t::log_file_op(mfile_type_t type, uint32_t space_id, + const char *path, const char *new_path) noexcept { ut_ad((new_path != nullptr) == (type == FILE_RENAME)); ut_ad(!(byte(type) & 15)); - ut_ad(!is_predefined_tablespace(space_id)); + ut_ad(!is_system_tablespace(space_id)); +#ifdef UNIV_DEBUG + if (log_sys.archive) + { + int n{0}; uint32_t id; + if (1 == sscanf(path, "undo%03" PRIu32 "%n", &id, &n) && !path[n] && + id - 1 + srv_undo_space_id_start == space_id) + goto valid_name; + } +#endif /* fil_name_parse() requires that there be at least one path separator and that the file path end with ".ibd" or "ibb". */ ut_ad(strchr(path, '/')); ut_ad(!strcmp(&path[strlen(path) - strlen(DOT_IBD)], DOT_IBD) || !strcmp(&path[strlen(path) - strlen(DOT_IBB)], DOT_IBB)); + ut_d(valid_name:) m_modifications= true; if (!is_logged()) @@ -1727,7 +1736,7 @@ fil_space_t *fil_space_t::drop(uint32_t id, pfs_os_file_t *detached_handle) This clean-up corresponds to fil_space_free(). */ log_sys.latch.wr_lock(); ut_ad((space->pending() & ~NEEDS_FSYNC) == (STOPPING | CLOSING)); - if (space->max_lsn != 0) + if (space->max_lsn > 1) { space->max_lsn= 0; fil_system.named_spaces.remove(*space); @@ -3087,65 +3096,7 @@ void fil_delete_file(const char *ibd_filepath) noexcept } } -#ifdef UNIV_DEBUG -/** Check that a tablespace is valid for mtr_commit(). -@param[in] space persistent tablespace that has been changed */ -static -void fil_space_validate_for_mtr_commit(const fil_space_t *space) noexcept -{ - mysql_mutex_assert_not_owner(&fil_system.mutex); - ut_ad(space != NULL); - ut_ad(!is_predefined_tablespace(space->id)); - ut_ad(!space->is_being_imported()); - - /* We are serving mtr_commit(). While there is an active - mini-transaction, we should have !space->is_stopping(). This is - guaranteed by meta-data locks or transactional locks. */ - ut_ad(!space->is_stopping() || space->referenced()); -} -#endif /* UNIV_DEBUG */ - -/** Note that a non-predefined persistent tablespace has been modified -by redo log. -@param[in,out] space tablespace */ -void fil_names_dirty(fil_space_t *space) noexcept -{ - ut_ad(log_sys.latch_have_wr()); - ut_ad(recv_recovery_is_on()); - ut_ad(!srv_read_only_mode); - ut_ad(log_sys.get_lsn() != 0); - ut_ad(space->max_lsn == 0); - ut_d(fil_space_validate_for_mtr_commit(space)); - - if (UNIV_UNLIKELY(recv_sys.rpo != 0)) { - /* The log is read-only; do not write to it */ - return; - } - - fil_system.named_spaces.push_back(*space); - space->max_lsn = log_sys.get_lsn(); -} - -/** Write a FILE_MODIFY record when a non-predefined persistent -tablespace was modified for the first time since fil_names_clear(). */ -ATTRIBUTE_NOINLINE ATTRIBUTE_COLD void mtr_t::name_write() noexcept -{ - ut_ad(log_sys.latch_have_wr()); - ut_d(fil_space_validate_for_mtr_commit(m_user_space)); - ut_ad(!m_user_space->max_lsn); - m_user_space->max_lsn= log_sys.get_lsn(); - - fil_system.named_spaces.push_back(*m_user_space); - ut_ad(UT_LIST_GET_LEN(m_user_space->chain) == 1); - - mtr_t mtr{nullptr}; - mtr.start(); - mtr.log_file_op(FILE_MODIFY, m_user_space->id, - UT_LIST_GET_FIRST(m_user_space->chain)->name); - mtr.commit_files(); -} - -/** On a log checkpoint, reset fil_names_dirty_and_write() flags +/** On a log checkpoint, reset mtr_t::name_write() flags and write out FILE_MODIFY if needed, and write FILE_CHECKPOINT. @param lsn checkpoint LSN @return current LSN */ @@ -3185,7 +3136,7 @@ ATTRIBUTE_COLD lsn_t fil_names_clear(lsn_t lsn) noexcept fil_system.named_spaces.erase(it); } - /* max_lsn is the last LSN where fil_names_dirty_and_write() + /* max_lsn is the last LSN where mtr_t::name_write() was called. If we kept track of "min_lsn" (the first LSN where max_lsn turned nonzero), we could avoid the fil_names_clear() call if min_lsn > lsn. */ diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index a43020daf7b63..09b76bf010d37 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -336,7 +336,8 @@ struct fil_space_t final fil_space_t *hash= nullptr; /** log_sys.get_lsn() of the most recent fil_names_write_if_was_clean(). Reset to 0 by fil_names_clear(). Protected by log_sys.latch_have_wr(). - If and only if this is nonzero, the tablespace will be in named_spaces. */ + If and only if this is greater than 1, + the tablespace will be in named_spaces. */ lsn_t max_lsn= 0; /** base node for the chain of data files; multiple entries are only possible for is_temporary() or id==0 */ @@ -1800,15 +1801,9 @@ fil_delete_file( @retval nullptr if not found */ fil_space_t *fil_space_get_by_id(uint32_t id) noexcept; -/** Note that a non-predefined persistent tablespace has been modified -by redo log. -@param[in,out] space tablespace */ -void fil_names_dirty(fil_space_t *space) noexcept; - - bool fil_comp_algo_loaded(ulint comp_algo) noexcept; -/** On a log checkpoint, reset fil_names_dirty_and_write() flags +/** On a log checkpoint, reset mtr_t::name_write() flags and write out FILE_MODIFY if needed, and write FILE_CHECKPOINT. @param lsn checkpoint LSN @return current LSN */ diff --git a/storage/innobase/include/log0recv.h b/storage/innobase/include/log0recv.h index 2146acbe55df5..93afe9d38e46c 100644 --- a/storage/innobase/include/log0recv.h +++ b/storage/innobase/include/log0recv.h @@ -45,9 +45,9 @@ ATTRIBUTE_COLD MY_ATTRIBUTE((nonnull, warn_unused_result)) bool recv_recover_page(fil_space_t* space, buf_page_t* bpage); /** Start recovering from a redo log checkpoint. -of first system tablespace page +@param sum_new_sizes sum of sizes of the new files added to system tablespace @return error code or DB_SUCCESS */ -dberr_t recv_recovery_from_checkpoint_start(); +dberr_t recv_recovery_from_checkpoint_start(ulint *sum_of_new_sizes); /** Report an operation to create, delete, or rename a file during backup. @param[in] space_id tablespace identifier diff --git a/storage/innobase/include/mtr0mtr.h b/storage/innobase/include/mtr0mtr.h index 3f8b84574eafd..91114a78e9327 100644 --- a/storage/innobase/include/mtr0mtr.h +++ b/storage/innobase/include/mtr0mtr.h @@ -244,6 +244,18 @@ struct mtr_t { } } + /** Set the undo tablespace associated with the mini-transaction + (needed for generating a FILE_MODIFY record) + @param space undo tablespace */ + void set_undo_space(fil_space_t* space) + { + ut_ad(!m_undo_space_id); + ut_d(m_undo_space_id = space->id); + if (space->id) { + m_undo_space = space; + } + } + #ifdef UNIV_DEBUG /** Check the tablespace associated with the mini-transaction (needed for generating a FILE_MODIFY record) @@ -631,9 +643,8 @@ struct mtr_t { @param path file path @param new_path new file path for type=FILE_RENAME @return number of bytes written */ - inline size_t log_file_op(mfile_type_t type, uint32_t space_id, - const char *path, - const char *new_path= nullptr) noexcept; + size_t log_file_op(mfile_type_t type, uint32_t space_id, + const char *path, const char *new_path= nullptr) noexcept; /** Add freed page numbers to freed_pages */ void add_freed_offset(fil_space_t *space, uint32_t page) @@ -694,8 +705,11 @@ struct mtr_t { inline void log_write_extended(const buf_block_t &block, byte type); /** Write a FILE_MODIFY record when a non-predefined persistent - tablespace was modified for the first time since fil_names_clear(). */ - ATTRIBUTE_NOINLINE ATTRIBUTE_COLD void name_write() noexcept; + tablespace was modified for the first time since fil_names_clear(). + @param space persistent tablespace + @param trim whether this is an undo tablespace */ + static ATTRIBUTE_NOINLINE ATTRIBUTE_COLD void name_write(fil_space_t *space, + bool undo) noexcept; /** Encrypt the log @return the total size in bytes, excluding the 8-byte nonce */ @@ -810,11 +824,16 @@ struct mtr_t { private: /** user tablespace that is being modified by the mini-transaction */ fil_space_t *m_user_space; + /** undo tablespace that is being modified by the mini-transaction */ + fil_space_t *m_undo_space; #ifdef UNIV_DEBUG /** Persistent user tablespace associated with the mini-transaction, or 0 (TRX_SYS_SPACE) if none yet */ uint32_t m_user_space_id; + /** Persistent undo tablespace associated with the + mini-transaction, or 0 (TRX_SYS_SPACE) if none yet */ + uint32_t m_undo_space_id; #endif /* UNIV_DEBUG */ /** acquired dict_index_t::lock, fil_space_t::latch, buf_block_t */ diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index d5b37e3502f09..c10b43dbc5813 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -1057,6 +1057,25 @@ class mlog_init_t void clear() { inits.clear(); i= inits.end(); } }; +/** Note that a non-system tablespace has been modified +by redo log. +@param[in,out] space tablespace */ +static void fil_names_dirty(fil_space_t *space) noexcept +{ + ut_ad(recv_recovery_is_on()); + ut_ad(!srv_read_only_mode); + ut_ad(log_sys.get_lsn() != 0); + ut_ad(space->max_lsn == 0); + ut_ad(space->id); + + if (UNIV_UNLIKELY(recv_sys.rpo != 0)) + /* The log is read-only; do not write to it */ + return; + + fil_system.named_spaces.push_back(*space); + space->max_lsn= log_sys.get_lsn(); +} + static mlog_init_t mlog_init; /** Try to recover a tablespace that was not readable earlier @@ -3256,11 +3275,16 @@ log_parse_file(const page_id_t id, bool if_exists, goto file_rec_error; } - if (space_id == TRX_SYS_SPACE || srv_is_undo_tablespace(space_id)) - goto file_rec_error; - if (fnend - l < 4 || - (memcmp(fnend - 4, DOT_IBD, 4) && memcmp(fnend - 4, DOT_IBB, 4))) + if (space_id == TRX_SYS_SPACE || fnend - l < 4) goto file_rec_error; + if (memcmp(fnend - 4, DOT_IBD, 4) && memcmp(fnend - 4, DOT_IBB, 4)) + { + uint32_t u; int n{0}; + if (fn2 || + 1 != sscanf(reinterpret_cast(l), "undo%03" PRIu32 "%n", + &u, &n) || fnend - l != n || u > 127) + goto file_rec_error; + } if (UNIV_UNLIKELY(!recv_needed_recovery && srv_read_only_mode)) break; @@ -5782,26 +5806,40 @@ dberr_t recv_sys_t::find_checkpoint_archived(lsn_t first_lsn, bool silent) return DB_SUCCESS; } +/** Open the system and undo tablespaces. +@param sum_new_sizes sum of sizes of the new files added to system tablespace +@return error code or DB_SUCCESS */ +static dberr_t recv_recovery_tablespaces_open(ulint *sum_of_new_sizes) +{ + if (dberr_t err= srv_sys_space.open_or_create(false, false, + sum_of_new_sizes)) + return err; + + mysql_mutex_lock(&recv_sys.mutex); + ut_d(fil_system.sys_space->recv_size= srv_sys_space_size_debug); + const bool success{fil_system.sys_space->open(false)}; + mysql_mutex_unlock(&recv_sys.mutex); + + if (!success) + return DB_ERROR; + + dberr_t err= srv_undo_tablespaces_init(nullptr); + if (err != DB_SUCCESS && srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN) + err= DB_SUCCESS; + return err; +} + /** Start recovering from a redo log checkpoint. -of first system tablespace page +@param sum_new_sizes sum of sizes of the new files added to system tablespace @return error code or DB_SUCCESS */ -dberr_t recv_recovery_from_checkpoint_start() +dberr_t recv_recovery_from_checkpoint_start(ulint *sum_of_new_sizes) { + ut_ad(log_sys.latch_have_wr()); bool rescan = false; - dberr_t err = DB_SUCCESS; - - ut_ad(srv_operation <= SRV_OPERATION_EXPORT_RESTORED - || srv_operation == SRV_OPERATION_RESTORE - || srv_operation == SRV_OPERATION_RESTORE_EXPORT); - ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex)); - ut_ad(UT_LIST_GET_LEN(buf_pool.LRU) == 0); - ut_ad(UT_LIST_GET_LEN(buf_pool.unzip_LRU) == 0); - ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex)); recv_sys.recovery_on = true; recv_sys_rpo_exceeded = 0; - log_sys.latch.wr_lock(); log_sys.set_capacity(); /* Start reading the log from the checkpoint lsn. */ @@ -5809,9 +5847,7 @@ dberr_t recv_recovery_from_checkpoint_start() ut_ad(recv_sys.pages.empty()); if (log_sys.format == log_t::FORMAT_3_23) { -func_exit: - log_sys.latch.wr_unlock(); - return err; + return recv_recovery_tablespaces_open(sum_of_new_sizes); } recv_sys_t::parser parser[2]; @@ -5827,8 +5863,7 @@ dberr_t recv_recovery_from_checkpoint_start() recv_sys.recovery_start, log_sys.last_checkpoint_lsn.load(), recv_sys.lsn); - err = DB_CORRUPTION; - goto func_exit; + return DB_CORRUPTION; } const bool rewind = recv_sys.lsn != log_sys.last_checkpoint_lsn; @@ -5842,18 +5877,17 @@ dberr_t recv_recovery_from_checkpoint_start() " and " LSN_PF, log_sys.last_checkpoint_lsn.load(), recv_sys.lsn); - err = DB_READ_ONLY; recv_sys.rpo = recv_sys.scanned_lsn; - goto func_exit; + return DB_READ_ONLY; } if (recv_sys.is_corrupt_log()) { corrupt_log: sql_print_error("InnoDB: Log scan aborted at LSN " LSN_PF, recv_sys.lsn); - goto err_exit; + return DB_CORRUPTION; } if (recv_sys.is_corrupt_fs()) { - goto err_exit; + return DB_CORRUPTION; } ut_ad(recv_sys.file_checkpoint); ut_ad(log_sys.get_flushed_lsn() >= recv_sys.scanned_lsn); @@ -5867,6 +5901,13 @@ dberr_t recv_recovery_from_checkpoint_start() goto corrupt_log; } } + + if (log_sys.archive) { + } else if (dberr_t err = + recv_recovery_tablespaces_open(sum_of_new_sizes)) { + return err; + } + rescan = recv_scan_log(false, parser); if (srv_read_only_mode) { @@ -5880,7 +5921,7 @@ dberr_t recv_recovery_from_checkpoint_start() goto corrupt_log; } if (recv_sys.is_corrupt_fs()) { - goto err_exit; + return DB_CORRUPTION; } } @@ -5893,11 +5934,9 @@ dberr_t recv_recovery_from_checkpoint_start() if (recv_needed_recovery) { bool missing_tablespace = false; - err = recv_init_crash_recovery_spaces( - rescan, missing_tablespace); - - if (err != DB_SUCCESS) { - goto func_exit; + if (dberr_t err = recv_init_crash_recovery_spaces( + rescan, missing_tablespace)) { + return err; } if (missing_tablespace) { @@ -5908,18 +5947,16 @@ dberr_t recv_recovery_from_checkpoint_start() do { rescan = recv_scan_log(false, parser); - if (recv_sys.is_corrupt_log() || - recv_sys.is_corrupt_fs()) { - goto err_exit; + if (recv_sys.is_corrupt_log() + || recv_sys.is_corrupt_fs()) { + return DB_ERROR; } missing_tablespace = false; - err = recv_validate_tablespace( - rescan, missing_tablespace); - - if (err != DB_SUCCESS) { - goto func_exit; + if (dberr_t err = recv_validate_tablespace( + rescan, missing_tablespace)) { + return err; } } while (missing_tablespace); @@ -5953,7 +5990,7 @@ dberr_t recv_recovery_from_checkpoint_start() if ((recv_sys.is_corrupt_log() && !srv_force_recovery) || recv_sys.is_corrupt_fs()) { - goto err_exit; + return DB_ERROR; } /* In case of multi-batch recovery, @@ -5967,47 +6004,48 @@ dberr_t recv_recovery_from_checkpoint_start() if (log_sys.is_recoverable()) { if (recv_sys_rpo_exceeded || recv_sys.validate_checkpoint()) { -err_exit: - err = DB_ERROR; - goto func_exit; + return DB_ERROR; } if (!srv_read_only_mode) { log_sys.set_recovered(); if (UNIV_UNLIKELY(recv_sys.was_archive) && !log_sys.archive_rename()) { - goto err_exit; + return DB_ERROR; } } } - DBUG_EXECUTE_IF("before_final_redo_apply", goto err_exit;); + DBUG_EXECUTE_IF("before_final_redo_apply", return DB_ERROR;); mysql_mutex_lock(&recv_sys.mutex); if (UNIV_UNLIKELY(recv_sys.scanned_lsn != recv_sys.lsn) && log_sys.is_recoverable()) { ut_ad("log parsing error" == 0); mysql_mutex_unlock(&recv_sys.mutex); - err = DB_CORRUPTION; - goto func_exit; + return DB_CORRUPTION; } recv_sys.apply_log_recs = true; ut_d(recv_no_log_write = srv_operation == SRV_OPERATION_RESTORE || srv_operation == SRV_OPERATION_RESTORE_EXPORT); - if (srv_operation == SRV_OPERATION_NORMAL) { - err = recv_rename_files(); - } + + dberr_t err = srv_operation == SRV_OPERATION_NORMAL + ? recv_rename_files() : DB_SUCCESS; mysql_mutex_unlock(&recv_sys.mutex); - /* The database is now ready to start almost normal processing of user - transactions: transaction rollbacks and the application of the log - records in the hash table can be run in background. */ - if (err == DB_SUCCESS && deferred_spaces.reinit_all() - && !srv_force_recovery) { - err = DB_CORRUPTION; + if (err == DB_SUCCESS && log_sys.archive) { + err = recv_recovery_tablespaces_open(sum_of_new_sizes); } - goto func_exit; + if (err != DB_SUCCESS) { + return err; + } + + if (deferred_spaces.reinit_all() && !srv_force_recovery) { + return DB_CORRUPTION; + } + + return DB_SUCCESS; } bool recv_dblwr_t::validate_page(const page_id_t page_id, lsn_t max_lsn, diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index 51b6295250008..b97343fae4404 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -206,8 +206,10 @@ void mtr_t::start() m_latch_ex= false; m_modifications= false; m_log_mode= MTR_LOG_ALL; - ut_d(m_user_space_id= TRX_SYS_SPACE); m_user_space= nullptr; + m_undo_space= nullptr; + ut_d(m_user_space_id= TRX_SYS_SPACE); + ut_d(m_undo_space_id= TRX_SYS_SPACE); m_commit_lsn= 0; m_trim_pages= false; m_binlog_page= nullptr; @@ -814,6 +816,7 @@ ATTRIBUTE_COLD lsn_t mtr_t::commit_files(lsn_t checkpoint_lsn) ut_ad(!m_freed_space); ut_ad(!m_freed_pages); ut_ad(!m_user_space); + ut_ad(!m_undo_space); ut_ad(!m_latch_ex); m_latch_ex= true; @@ -839,6 +842,38 @@ ATTRIBUTE_COLD lsn_t mtr_t::commit_files(lsn_t checkpoint_lsn) return m_commit_lsn; } +/** Write a FILE_MODIFY record when a non-predefined persistent +tablespace was modified for the first time since fil_names_clear(). +@param space persistent tablespace +@param undo whether this is an undo tablespace */ +ATTRIBUTE_NOINLINE ATTRIBUTE_COLD +void mtr_t::name_write(fil_space_t *space, bool undo) noexcept +{ + mysql_mutex_assert_not_owner(&fil_system.mutex); + ut_ad(space); + ut_ad(space->id); + ut_ad(!space->is_being_imported()); + ut_ad(!space->is_stopping() || space->referenced()); + ut_ad(UT_LIST_GET_LEN(space->chain) == 1); + + if (UNIV_UNLIKELY(space->max_lsn)) + return; + const char *name= UT_LIST_GET_FIRST(space->chain)->name; + if (undo) + { + ut_ad(log_sys.archive); + name= base_name(name); + } + + space->max_lsn= log_sys.get_lsn(); + fil_system.named_spaces.push_back(*space); + + mtr_t mtr{nullptr}; + mtr.start(); + mtr.log_file_op(FILE_MODIFY, space->id, name); + mtr.commit_files(); +} + #ifdef UNIV_DEBUG /** Check if a tablespace is associated with the mini-transaction (needed for generating a FILE_MODIFY record) @@ -848,8 +883,11 @@ bool mtr_t::is_named_space(uint32_t space) const { ut_ad(!m_user_space || m_user_space->id != TRX_SYS_SPACE); - return !is_logged() || m_user_space_id == space || - is_predefined_tablespace(space); + ut_ad(!m_undo_space || m_undo_space->id != TRX_SYS_SPACE); + return !space || !is_logged() || m_user_space_id == space || + (log_sys.archive + ? (m_undo_space_id == space || srv_startup_is_before_trx_rollback_phase) + : srv_is_undo_tablespace(space)); } /** Check if a tablespace is associated with the mini-transaction (needed for generating a FILE_MODIFY record) @@ -858,9 +896,12 @@ mtr_t::is_named_space(uint32_t space) const bool mtr_t::is_named_space(const fil_space_t* space) const { ut_ad(!m_user_space || m_user_space->id != TRX_SYS_SPACE); + ut_ad(!m_undo_space || m_undo_space->id != TRX_SYS_SPACE); - return !is_logged() || m_user_space == space || - is_predefined_tablespace(space->id); + return !space->id || !is_logged() || m_user_space == space || + (log_sys.archive + ? (m_undo_space == space || srv_startup_is_before_trx_rollback_phase) + : srv_is_undo_tablespace(space->id)); } #endif /* UNIV_DEBUG */ @@ -876,6 +917,7 @@ fil_space_t *mtr_t::x_lock_space(uint32_t space_id) if (space_id == TRX_SYS_SPACE) { space = fil_system.sys_space; } else if ((space = m_user_space) && space_id == space->id) { + } else if ((space = m_undo_space) && space_id == space->id) { } else { space = fil_space_get(space_id); ut_ad(m_log_mode != MTR_LOG_NO_REDO @@ -1175,6 +1217,8 @@ std::pair mtr_t::do_write() noexcept ut_ad(!m_latch_ex || log_sys.latch_have_wr()); ut_ad(!m_user_space || (m_user_space->id > 0 && m_user_space->id < SRV_SPACE_ID_UPPER_BOUND)); + ut_ad(!m_undo_space || + (m_undo_space->id > 0 && m_undo_space->id < SRV_SPACE_ID_UPPER_BOUND)); m_commit_lsn= 0; #ifndef DBUG_OFF @@ -1198,20 +1242,22 @@ std::pair mtr_t::do_write() noexcept if (!m_latch_ex) log_sys.latch.rd_lock(); - if (UNIV_UNLIKELY(m_user_space && !m_user_space->max_lsn && - !srv_is_undo_tablespace((m_user_space->id)))) + if (UNIV_UNLIKELY(m_user_space && !m_user_space->max_lsn) || + UNIV_UNLIKELY(m_undo_space && !m_undo_space->max_lsn)) { if (!m_latch_ex) { m_latch_ex= true; log_sys.latch.rd_unlock(); log_sys.latch.wr_lock(); - if (UNIV_UNLIKELY(m_user_space->max_lsn != 0)) - goto func_exit; } - name_write(); + + if (m_user_space) + name_write(m_user_space, false); + if (m_undo_space) + name_write(m_undo_space, true); } -func_exit: + return finish_write(len); } diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 67eeaa5ff0086..1fbfaf7a754bd 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -230,9 +230,6 @@ static dberr_t create_log_file(bool create_new_db, lsn_t lsn) log_sys.create(lsn); ut_ad(srv_startup_is_before_trx_rollback_phase); - if (create_new_db) { - srv_startup_is_before_trx_rollback_phase = false; - } /* Enable log_checkpoint() in buf_flush_page_cleaner(). If we are upgrading or resizing the log at startup, we must not @@ -292,8 +289,8 @@ static dberr_t srv_undo_tablespace_create(const char* name) fh = os_file_create( innodb_data_file_key, name, - srv_read_only_mode ? OS_FILE_OPEN : OS_FILE_CREATE, - OS_DATA_FILE, srv_read_only_mode, &ret); + OS_FILE_CREATE, + OS_DATA_FILE, false, &ret); if (!ret) { if (os_file_get_last_error(false) != OS_FILE_ALREADY_EXISTS @@ -308,8 +305,6 @@ static dberr_t srv_undo_tablespace_create(const char* name) << name; } err = DB_ERROR; - } else if (srv_read_only_mode) { - ib::info() << name << " opened in read-only mode"; } else { /* We created the data file and now write it full of zeros */ @@ -418,6 +413,7 @@ ATTRIBUTE_COLD static dberr_t srv_undo_tablespaces_reinit() dberr_t err; buf_block_t *first_rseg_hdr; uint32_t latest_space_id; + bool assigned_space_id{false}; mtr.start(); @@ -552,13 +548,15 @@ ATTRIBUTE_COLD static dberr_t srv_undo_tablespaces_reinit() mtr.memo_push(dict_hdr, MTR_MEMO_PAGE_X_FIX); srv_undo_space_id_start= latest_space_id; - if (fil_assign_new_space_id(&srv_undo_space_id_start)) - mtr.write<4>(*dict_hdr, DICT_HDR + DICT_HDR_MAX_SPACE_ID + - dict_hdr->page.frame, srv_undo_space_id_start); + assigned_space_id= fil_assign_new_space_id(&srv_undo_space_id_start); /* Re-create the new undo tablespaces */ err= srv_undo_tablespaces_init(&mtr); func_exit: + if (assigned_space_id && err == DB_SUCCESS) + mtr.write<4>(*dict_hdr, DICT_HDR + DICT_HDR_MAX_SPACE_ID + + dict_hdr->page.frame, srv_undo_space_id_start); + mtr.commit(); if (err == DB_SUCCESS) @@ -733,6 +731,8 @@ static uint32_t srv_undo_tablespace_open(bool create, const char* name, FIL_ENCRYPTION_DEFAULT, true); ut_ad(space); fil_node_t *file= space->add(name, fh, 0, false, true); + if (!log_sys.archive) + space->max_lsn= 1; if (create) { @@ -908,25 +908,23 @@ dberr_t srv_undo_tablespaces_init(mtr_t *mtr) srv_undo_tablespaces_open= 0; ut_a(srv_undo_tablespaces <= TRX_SYS_N_RSEGS); - ut_a(!mtr || srv_operation <= SRV_OPERATION_EXPORT_RESTORED); if (srv_undo_tablespaces == 1) srv_undo_tablespaces= 0; - /* Create the undo spaces only if we are creating a new - instance. We don't allow creating of new undo tablespaces - in an existing instance (yet). */ if (mtr) { + ut_ad(!mtr->get_log_size()); + ut_a(srv_operation <= SRV_OPERATION_EXPORT_RESTORED); DBUG_EXECUTE_IF("innodb_undo_upgrade", srv_undo_space_id_start= 3;); - - for (ulint i= 0; i < srv_undo_tablespaces; ++i) + for (ulint i= 0; i++ < srv_undo_tablespaces; ) { char name[OS_FILE_MAX_PATH]; - snprintf(name, sizeof name, "%s/undo%03zu", srv_undo_dir, i + 1); + snprintf(name, sizeof name, "%s/undo%03zu", srv_undo_dir, i); if (dberr_t err= srv_undo_tablespace_create(name)) { - ib::error() << "Could not create undo tablespace '" << name << "'."; + sql_print_error("InnoDB: Could not create undo tablespace '%s'.", + name); return err; } } @@ -951,10 +949,22 @@ dberr_t srv_undo_tablespaces_init(mtr_t *mtr) if (srv_undo_tablespaces_open == 0) srv_undo_space_id_start= 0; - if (mtr) - for (uint32_t i= 0; err == DB_SUCCESS && i < srv_undo_tablespaces; ++i) - err= fsp_header_init(fil_space_get(srv_undo_space_id_start + i), - SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, mtr); + if (err == DB_SUCCESS && mtr) + { + if (log_sys.archive) + for (uint32_t i= 0; i < srv_undo_tablespaces; ++i) + { + char name[8]; + snprintf(name, sizeof name, "undo%03" PRIu32, i + 1); + mtr->log_file_op(FILE_CREATE, srv_undo_space_id_start + i, name); + } + + for (uint32_t i= 0; i < srv_undo_tablespaces; ++i) + if ((err= fsp_header_init(fil_space_get(srv_undo_space_id_start + i), + SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, mtr)) != + DB_SUCCESS) + break; + } return err; } @@ -1084,6 +1094,7 @@ srv_init_abort_low( ut_ad(srv_is_being_started); if (create_new_db) { + recv_sys.rpo = LSN_MAX; ib::error() << "Database creation was aborted" #ifdef UNIV_DEBUG " at " << innobase_basename(file) << "[" << line << "]" @@ -1290,12 +1301,25 @@ static dberr_t srv_load_tables(bool must_upgrade_ibuf) noexcept } /** Attempt to start up a previously created database. +@param sum_new_sizes sum of sizes of the new files added to system tablespace @return error code */ -static dberr_t srv_start_recovery() +static dberr_t srv_start_recovery(ulint *sum_of_new_sizes) { ut_ad(srv_force_recovery < SRV_FORCE_NO_LOG_REDO); - dberr_t err= recv_recovery_from_checkpoint_start(); - recv_sys.close_files(); + ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex)); + ut_ad(UT_LIST_GET_LEN(buf_pool.LRU) == 0); + ut_ad(UT_LIST_GET_LEN(buf_pool.unzip_LRU) == 0); + ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex)); + + log_sys.latch.wr_lock(); + dberr_t err= recv_sys.find_checkpoint(); + if (err == DB_SUCCESS) + { + err= recv_recovery_from_checkpoint_start(sum_of_new_sizes); + recv_sys.close_files(); + } + + log_sys.latch.wr_unlock(); bool must_upgrade_ibuf= false; @@ -1508,7 +1532,6 @@ dberr_t srv_start(bool create_new_db) return srv_init_abort(DB_ERROR); } - mtr.start(); ut_ad(fil_system.sys_space->id == 0); compile_time_assert(TRX_SYS_SPACE == 0); @@ -1575,43 +1598,13 @@ dberr_t srv_start(bool create_new_db) } else { /* We always try to do a recovery, even if the database had been shut down normally */ - log_sys.latch.wr_lock(); - err = recv_sys.find_checkpoint(); - log_sys.latch.wr_unlock(); + err = srv_start_recovery(&sum_of_new_sizes); if (err != DB_SUCCESS) { return srv_init_abort(err); } } - /* Open or create the data files. */ - err = srv_sys_space.open_or_create(false, false, - &sum_of_new_sizes); - if (err != DB_SUCCESS) { - return srv_init_abort(err); - } - - /* Open data files in the system tablespace: we keep - them open until database shutdown */ - mysql_mutex_lock(&recv_sys.mutex); - ut_d(fil_system.sys_space->recv_size = srv_sys_space_size_debug); - err = fil_system.sys_space->open(false) ? DB_SUCCESS : DB_ERROR; - mysql_mutex_unlock(&recv_sys.mutex); - - if (err == DB_SUCCESS) { - err = srv_undo_tablespaces_init(nullptr); - - if (err == DB_SUCCESS) { - err = srv_start_recovery(); - } - - if (err != DB_SUCCESS) { - return srv_init_abort(err); - } - } else if (srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) { - return srv_init_abort(err); - } - fil_system.space_id_reuse_warned = false; /* Upgrade or resize or rebuild the redo logs before diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index 9ba4a8f3e190f..84b355264fca2 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -356,6 +356,7 @@ inline dberr_t purge_sys_t::iterator::free_history_rseg(trx_rseg_t &rseg) const const auto last_page= rseg.space->free_limit; mtr.start(); + mtr.set_undo_space(rseg.space); dberr_t err; buf_block_t *rseg_hdr= rseg.get(&mtr, &err); diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc index 0f91ea888f9ed..0fbcfa1d63e7e 100644 --- a/storage/innobase/trx/trx0rec.cc +++ b/storage/innobase/trx/trx0rec.cc @@ -1931,6 +1931,7 @@ trx_undo_report_row_operation( == dict_table_t::IGNORE_UNDO; rseg = trx->rsegs.m_redo.rseg; + mtr.set_undo_space(rseg->space); undo_block = trx_undo_assign_low(&mtr, &err, rseg, pundo); if (clear_ignore) { diff --git a/storage/innobase/trx/trx0sys.cc b/storage/innobase/trx/trx0sys.cc index 2f2265a3df1fd..e85aa82e93869 100644 --- a/storage/innobase/trx/trx0sys.cc +++ b/storage/innobase/trx/trx0sys.cc @@ -258,6 +258,9 @@ static trx_rseg_t *trx_rseg_create(uint32_t space_id) ut_ad(!space->is_being_imported()); if (buf_block_t *sys_header= trx_sysf_get(&mtr)) { + if (space_id && log_sys.archive) + mtr.log_file_op(FILE_MODIFY, space->id, + base_name(space->chain.start->name)); ulint rseg_id= trx_sys_rseg_find_free(sys_header); dberr_t err; if (buf_block_t *rblock= rseg_id == ULINT_UNDEFINED diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 640352a0aacc3..cc303ed5698e8 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -1148,6 +1148,7 @@ inline void trx_t::write_serialisation_history(mtr_t *mtr) if (UNIV_LIKELY(undo != nullptr)) { MONITOR_INC(MONITOR_TRX_COMMIT_UNDO); + mtr->set_undo_space(rseg->space); /* We have to hold exclusive rseg->latch because undo log headers have to be put to the history list in the (serialisation) order of the From ac3a4ab419d7ae2ffcf59b8a67b231822a57ce22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 25 May 2026 16:01:45 +0300 Subject: [PATCH 3/3] Fix regressions in some innodb_log_archive=OFF tests --- mysql-test/suite/atomic/disabled.def | 1 + mysql-test/suite/encryption/disabled.def | 1 - mysql-test/suite/innodb/disabled.def | 10 +-- mysql-test/suite/innodb_gis/disabled.def | 1 + storage/innobase/buf/buf0flu.cc | 2 +- storage/innobase/ibuf/ibuf0ibuf.cc | 4 +- storage/innobase/include/log0recv.h | 9 +- storage/innobase/log/log0recv.cc | 107 ++++++++++++++--------- storage/innobase/srv/srv0start.cc | 42 +++++---- 9 files changed, 106 insertions(+), 71 deletions(-) delete mode 100644 mysql-test/suite/encryption/disabled.def diff --git a/mysql-test/suite/atomic/disabled.def b/mysql-test/suite/atomic/disabled.def index 3e1f2d6a2c391..c7dedb184840a 100644 --- a/mysql-test/suite/atomic/disabled.def +++ b/mysql-test/suite/atomic/disabled.def @@ -10,3 +10,4 @@ # ############################################################################## rename_trigger : MDEV-29282 2022:-08-16 Igor Babaev +alter_table_innodb : FIXME diff --git a/mysql-test/suite/encryption/disabled.def b/mysql-test/suite/encryption/disabled.def deleted file mode 100644 index a1fb9e1563122..0000000000000 --- a/mysql-test/suite/encryption/disabled.def +++ /dev/null @@ -1 +0,0 @@ -innodb_encrypt_log_corruption : FIXME diff --git a/mysql-test/suite/innodb/disabled.def b/mysql-test/suite/innodb/disabled.def index 3de7c96f3f2aa..ea6c5628c4185 100644 --- a/mysql-test/suite/innodb/disabled.def +++ b/mysql-test/suite/innodb/disabled.def @@ -1,11 +1,7 @@ +blob-crash : FIXME doublewrite_debug : FIXME import_hidden_fts : FIXME -import_tablespace_race : FIXME -innodb.log_file : FIXME -innodb.log_file_overwrite : FIXME -innodb_force_recovery : FIXME -log_archive_recovery : FIXME (this is a new test) -log_corruption : FIXME +log_archive_recovery : FIXME +log_corruption_recovery : FIXME log_file_size_online : FIXME -temp_truncate : FIXME undo_upgrade_debug : FIXME diff --git a/mysql-test/suite/innodb_gis/disabled.def b/mysql-test/suite/innodb_gis/disabled.def index 2d4d3686dd19d..4bed1a1602810 100644 --- a/mysql-test/suite/innodb_gis/disabled.def +++ b/mysql-test/suite/innodb_gis/disabled.def @@ -13,3 +13,4 @@ rtree_concurrent_srch : MDEV-15284 COUNT(*) mismatch rtree_recovery : MDEV-15284 COUNT(*) mismatch rtree_compress2 : MDEV-16269 CHECK TABLE reports wrong count +alter_spatial_index : FIXME diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index 23ec77d3f67e5..54938f863438e 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -2786,7 +2786,7 @@ static void buf_flush_page_cleaner() noexcept do { - if (recv_recovery_is_on()) + if (recv_recovery_is_on() || recv_sys.is_corrupt_log()) continue; create_spare_archive= buf_flush_archive_create(create_spare_archive); IF_DBUG(if (log_sys.last_checkpoint_lsn > log_sys.get_first_lsn() && diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index b7e724b962a07..c55a8a81c4b89 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -1022,7 +1022,9 @@ ATTRIBUTE_COLD dberr_t ibuf_upgrade() dberr_t ibuf_upgrade_needed() { - ut_ad(srv_force_recovery < SRV_FORCE_NO_LOG_REDO); + if (srv_force_recovery == SRV_FORCE_NO_LOG_REDO) + return DB_SUCCESS; + mtr_t mtr{nullptr}; mtr.start(); mtr.x_lock_space(fil_system.sys_space); diff --git a/storage/innobase/include/log0recv.h b/storage/innobase/include/log0recv.h index 93afe9d38e46c..b13dafab97e37 100644 --- a/storage/innobase/include/log0recv.h +++ b/storage/innobase/include/log0recv.h @@ -44,9 +44,16 @@ ATTRIBUTE_COLD MY_ATTRIBUTE((nonnull, warn_unused_result)) @return whether the page was recovered correctly */ bool recv_recover_page(fil_space_t* space, buf_page_t* bpage); +/** Open the system and undo tablespaces. +@param sum_new_sizes sum of sizes of the new files added to system tablespace +@return error code +@retval DB_SUCCESS on success */ +dberr_t recv_recovery_tablespaces_open(ulint *sum_of_new_sizes); + /** Start recovering from a redo log checkpoint. @param sum_new_sizes sum of sizes of the new files added to system tablespace -@return error code or DB_SUCCESS */ +@return error code +@retval DB_SUCCESS on success */ dberr_t recv_recovery_from_checkpoint_start(ulint *sum_of_new_sizes); /** Report an operation to create, delete, or rename a file during backup. diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index c10b43dbc5813..934e547ed31b1 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -1134,7 +1134,9 @@ fil_space_t *recv_sys_t::recover_deferred(const recv_sys_t::map::iterator &p, mysql_mutex_unlock(&fil_system.mutex); if (!space->acquire()) goto release_and_fail; + log_sys.latch.wr_lock(); fil_names_dirty(space); + log_sys.latch.wr_unlock(); const bool is_compressed= fil_space_t::is_compressed(flags); #ifdef _WIN32 const bool is_sparse= is_compressed; @@ -1771,6 +1773,7 @@ dberr_t recv_sys_t::find_checkpoint() lsn_t first_lsn= 0; bool read_only{srv_read_only_mode || srv_operation >= SRV_OPERATION_BACKUP}; os_offset_t size= 0; + dberr_t err= DB_ERROR; ut_ad(pages.empty()); ut_ad(log_archive.empty()); @@ -1792,11 +1795,11 @@ dberr_t recv_sys_t::find_checkpoint() { sql_print_error("InnoDB: innodb_log_archive=ON but %s exists", path.c_str()); - return DB_ERROR; + goto err_exit; } } else if (archive < 0 || srv_operation != SRV_OPERATION_NORMAL) - return DB_ERROR; + goto err_exit; else { path.reserve(strlen(srv_log_group_home_dir) + @@ -1806,7 +1809,10 @@ dberr_t recv_sys_t::find_checkpoint() tmp_buf= static_cast (ut_malloc_dontdump(tmp_buf_size, PSI_INSTRUMENT_ME)); if (!tmp_buf) - return DB_OUT_OF_MEMORY; + { + err= DB_OUT_OF_MEMORY; + goto err_exit; + } } #ifdef _WIN32 WIN32_FIND_DATAA entry; @@ -1831,7 +1837,7 @@ dberr_t recv_sys_t::find_checkpoint() { sql_print_error("InnoDB: innodb_log_archive files not found in '%s'", srv_log_group_home_dir); - return DB_ERROR; + goto err_exit; } archive= -1; goto retry; @@ -1919,7 +1925,7 @@ dberr_t recv_sys_t::find_checkpoint() { sql_print_error("InnoDB: No matching file found for" " innodb_log_recovery_start=" LSN_PF, recovery_start); - return DB_ERROR; + goto err_exit; } i= log_archive.end(); @@ -1953,7 +1959,7 @@ dberr_t recv_sys_t::find_checkpoint() c_str(), OS_FILE_OPEN, OS_LOG_FILE, open_read_only, &success); if (file == OS_FILE_CLOSED) - return DB_ERROR; + goto err_exit; if (UNIV_UNLIKELY(log_sys.buf_size > i->second.end - i->first)) log_sys.buf_size= unsigned(i->second.end - i->first); @@ -1962,9 +1968,9 @@ dberr_t recv_sys_t::find_checkpoint() log_t::START_OFFSET, log_t::READ_ONLY)) { os_file_close(file); - return DB_ERROR; + goto err_exit; } - const dberr_t err= + err= find_checkpoint_archived(i->first, !read_only && i != start); if (!uint16_t(~last_checkpoint_no)) { @@ -2032,7 +2038,11 @@ dberr_t recv_sys_t::find_checkpoint() if ((recovery_start ? i == found_recovery_start : read_only) || i == start) - return err; + { + if (err != DB_SUCCESS) + goto err_exit; + return DB_SUCCESS; + } next: log_sys.stash_archive_file(); } @@ -2051,12 +2061,12 @@ dberr_t recv_sys_t::find_checkpoint() { too_small: sql_print_error("InnoDB: File %s is too small", path.c_str()); - err_exit: + read_error: os_file_close(file); - return DB_ERROR; + goto err_exit; } else if (!log_sys.attach(file, size, log_t::log_access(read_only))) - goto err_exit; + goto read_error; else file= OS_FILE_CLOSED; } @@ -2088,7 +2098,7 @@ dberr_t recv_sys_t::find_checkpoint() if (!size) { if (first_lsn == LSN_MAX) - return DB_CORRUPTION; + goto corrupted; lsn= log_sys.last_checkpoint_lsn; log_sys.format= log_t::FORMAT_3_23; goto upgrade; @@ -2102,18 +2112,27 @@ dberr_t recv_sys_t::find_checkpoint() ut_ad(!file_checkpoint); lsn= 0; buf= my_assume_aligned<4096>(log_sys.buf); - if (!log_sys.is_mmap()) - if (dberr_t err= log_sys.log.read(0, {buf, log_sys.START_OFFSET})) - return err; + if (!log_sys.is_mmap() && + (err= log_sys.log.read(0, {buf, log_sys.START_OFFSET})) != DB_SUCCESS) + { + err_exit: + found_corrupt_log= true; + return err; + } /* Check the header page checksum. There was no checksum in the first redo log format (version 0). */ log_sys.format= mach_read_from_4(buf + LOG_HEADER_FORMAT); if (log_sys.format == log_t::FORMAT_3_23) { if (first_lsn == LSN_MAX) - return DB_CORRUPTION; - if (dberr_t err= recv_log_recover_pre_10_2()) - return err; + { + corrupted: + err= DB_CORRUPTION; + goto err_exit; + } + err= recv_log_recover_pre_10_2(); + if (err != DB_SUCCESS) + goto err_exit; upgrade: memset_aligned<4096>(const_cast(field_ref_zero), 0, 4096); /* Mark the redo log for upgrading. */ @@ -2123,7 +2142,7 @@ dberr_t recv_sys_t::find_checkpoint() { sql_print_error("InnoDB: cannot fulfill innodb_log_recovery_target=%" PRIu64 "!=%" PRIu64, rpo, lsn); - return DB_CORRUPTION; + goto corrupted; } if (UNIV_LIKELY(lsn != 0)) scanned_lsn= lsn; @@ -2134,7 +2153,7 @@ dberr_t recv_sys_t::find_checkpoint() if (!recv_check_log_block(buf)) { sql_print_error("InnoDB: Invalid log header checksum"); - return DB_CORRUPTION; + goto corrupted; } first_lsn= mach_read_from_8(buf + LOG_HEADER_START_LSN); @@ -2150,13 +2169,14 @@ dberr_t recv_sys_t::find_checkpoint() default: sql_print_error("InnoDB: Unsupported redo log format." " The redo log was created with %s.", creator); - return DB_ERROR; + err= DB_ERROR; + goto err_exit; case log_t::FORMAT_ENC_11: case log_t::FORMAT_10_8: if (files.size() != 1) { sql_print_error("InnoDB: Expecting only ib_logfile0"); - return DB_CORRUPTION; + goto corrupted; } if (*reinterpret_cast(buf + LOG_HEADER_FORMAT + 4) || @@ -2164,7 +2184,7 @@ dberr_t recv_sys_t::find_checkpoint() { sql_print_error("InnoDB: Invalid ib_logfile0 header block;" " the log was created with %s.", creator); - return DB_CORRUPTION; + goto corrupted; } if (!mach_read_from_4(buf + LOG_HEADER_CREATOR_END) && @@ -2219,7 +2239,7 @@ dberr_t recv_sys_t::find_checkpoint() if (files.size() != 1) { sql_print_error("InnoDB: Expecting only ib_logfile0"); - return DB_CORRUPTION; + goto corrupted; } /* fall through */ case log_t::FORMAT_10_2: @@ -2266,13 +2286,16 @@ dberr_t recv_sys_t::find_checkpoint() got_no_checkpoint: sql_print_error("InnoDB: No valid checkpoint was found;" " the log was created with %s.", creator); - return DB_ERROR; + err= DB_ERROR; + goto err_exit; } if (first_lsn == LSN_MAX) - return DB_CORRUPTION; + goto corrupted; - if (dberr_t err= recv_log_recover_10_5(lsn_offset)) + err= recv_log_recover_10_5(lsn_offset); + + if (err != DB_SUCCESS) { const char *msg1, *msg2, *msg3; msg1= srv_operation == SRV_OPERATION_NORMAL @@ -2292,7 +2315,7 @@ dberr_t recv_sys_t::find_checkpoint() sql_print_error("%s The redo log was created with %s%s%s", msg1, creator, msg2, msg3); - return err; + goto err_exit; } goto upgrade; @@ -5808,8 +5831,9 @@ dberr_t recv_sys_t::find_checkpoint_archived(lsn_t first_lsn, bool silent) /** Open the system and undo tablespaces. @param sum_new_sizes sum of sizes of the new files added to system tablespace -@return error code or DB_SUCCESS */ -static dberr_t recv_recovery_tablespaces_open(ulint *sum_of_new_sizes) +@return error code +@retval DB_SUCCESS on success */ +dberr_t recv_recovery_tablespaces_open(ulint *sum_of_new_sizes) { if (dberr_t err= srv_sys_space.open_or_create(false, false, sum_of_new_sizes)) @@ -5831,7 +5855,8 @@ static dberr_t recv_recovery_tablespaces_open(ulint *sum_of_new_sizes) /** Start recovering from a redo log checkpoint. @param sum_new_sizes sum of sizes of the new files added to system tablespace -@return error code or DB_SUCCESS */ +@return error code +@retval DB_SUCCESS on success */ dberr_t recv_recovery_from_checkpoint_start(ulint *sum_of_new_sizes) { ut_ad(log_sys.latch_have_wr()); @@ -5851,6 +5876,7 @@ dberr_t recv_recovery_from_checkpoint_start(ulint *sum_of_new_sizes) } recv_sys_t::parser parser[2]; + bool sys_opened{false}; if (log_sys.is_recoverable()) { if (recv_sys.recovery_start > recv_sys.lsn) { @@ -5902,7 +5928,9 @@ dberr_t recv_recovery_from_checkpoint_start(ulint *sum_of_new_sizes) } } - if (log_sys.archive) { + sys_opened = !log_sys.archive; + + if (!sys_opened) { } else if (dberr_t err = recv_recovery_tablespaces_open(sum_of_new_sizes)) { return err; @@ -6033,16 +6061,13 @@ dberr_t recv_recovery_from_checkpoint_start(ulint *sum_of_new_sizes) mysql_mutex_unlock(&recv_sys.mutex); - if (err == DB_SUCCESS && log_sys.archive) { + if (err == DB_SUCCESS && !sys_opened) { err = recv_recovery_tablespaces_open(sum_of_new_sizes); - } - if (err != DB_SUCCESS) { - return err; - } - - if (deferred_spaces.reinit_all() && !srv_force_recovery) { - return DB_CORRUPTION; + if (err == DB_SUCCESS + && deferred_spaces.reinit_all() && !srv_force_recovery) { + err = DB_CORRUPTION; + } } return DB_SUCCESS; diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 1fbfaf7a754bd..1b6ce3f7799f7 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -1305,21 +1305,31 @@ static dberr_t srv_load_tables(bool must_upgrade_ibuf) noexcept @return error code */ static dberr_t srv_start_recovery(ulint *sum_of_new_sizes) { - ut_ad(srv_force_recovery < SRV_FORCE_NO_LOG_REDO); ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex)); ut_ad(UT_LIST_GET_LEN(buf_pool.LRU) == 0); ut_ad(UT_LIST_GET_LEN(buf_pool.unzip_LRU) == 0); ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex)); - log_sys.latch.wr_lock(); - dberr_t err= recv_sys.find_checkpoint(); - if (err == DB_SUCCESS) + dberr_t err; + + if (UNIV_UNLIKELY(srv_force_recovery >= SRV_FORCE_NO_LOG_REDO)) { - err= recv_recovery_from_checkpoint_start(sum_of_new_sizes); - recv_sys.close_files(); + sql_print_information("InnoDB: innodb_force_recovery=6" + " skips redo log apply"); + recv_sys.rpo= LSN_MAX; + err= recv_recovery_tablespaces_open(sum_of_new_sizes); + } + else + { + log_sys.latch.wr_lock(); + err= recv_sys.find_checkpoint(); + if (err == DB_SUCCESS) + { + err= recv_recovery_from_checkpoint_start(sum_of_new_sizes); + recv_sys.close_files(); + } + log_sys.latch.wr_unlock(); } - - log_sys.latch.wr_unlock(); bool must_upgrade_ibuf= false; @@ -1591,18 +1601,12 @@ dberr_t srv_start(bool create_new_db) DICT_HDR_MAX_SPACE_ID. */ fil_system.space_id_reuse_warned = true; - if (srv_force_recovery >= SRV_FORCE_NO_LOG_REDO) { - sql_print_information("InnoDB: innodb_force_recovery=6" - " skips redo log apply"); - recv_sys.rpo = LSN_MAX; - } else { - /* We always try to do a recovery, even if the - database had been shut down normally */ - err = srv_start_recovery(&sum_of_new_sizes); + /* We always try to do a recovery, even if the + database had been shut down normally */ + err = srv_start_recovery(&sum_of_new_sizes); - if (err != DB_SUCCESS) { - return srv_init_abort(err); - } + if (err != DB_SUCCESS) { + return srv_init_abort(err); } fil_system.space_id_reuse_warned = false;