Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-26672

innodb_undo_log_truncate may cause the transaction ID sequence to be reset

    XMLWordPrintable

    Details

      Description

      This was first noticed by Krunal Bauskar: innodb_undo_log_truncate may cause the sequence of DB_TRX_ID to be reset or rolled back.

      Ever since MDEV-15158, we no longer write that sequence in the TRX_SYS page, only in the undo log header pages. But, undo log truncation fails to preserve the transaction identifier when rebuilding the undo log header pages. This can be reproduced with the following patch to the current 10.3:

      diff --git a/mysql-test/suite/innodb/t/undo_truncate.test b/mysql-test/suite/innodb/t/undo_truncate.test
      index bc2e6a3a119..9ef0f5b52c6 100644
      --- a/mysql-test/suite/innodb/t/undo_truncate.test
      +++ b/mysql-test/suite/innodb/t/undo_truncate.test
      @@ -48,21 +48,14 @@ commit; disconnect con1;
       connection con2; commit; disconnect con2;
       
       connection default;
      -drop table t1, t2;
       
      ---source include/wait_all_purged.inc
      +--replace_regex /.*Trx id counter ([0-9]+).*/\1/
      +SHOW ENGINE INNODB STATUS;
       
      -# Truncation will normally not occur with innodb_page_size=64k,
      -# and occasionally not with innodb_page_size=32k,
      -# because the undo log will not grow enough.
      -if (`select @@innodb_page_size IN (4096,8192,16384)`)
      -{
      -  let $wait_condition = (SELECT variable_value!=@trunc_start
      -                         FROM information_schema.global_status
      -                         WHERE variable_name = 'innodb_undo_truncations');
      -  source include/wait_condition.inc;
      -}
      +set global innodb_fast_shutdown=0;
      +--source include/restart_mysqld.inc
      +--replace_regex /.*Trx id counter ([0-9]+).*/\1/
      +SHOW ENGINE INNODB STATUS;
       
      -SET GLOBAL innodb_undo_logs = @save_undo_logs;
      -SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;
      -SET GLOBAL innodb_undo_log_truncate = @save_truncate;
      +check table t1, t2;
      +drop table t1, t2;
      diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc
      index 9d19a14cb66..a5127400dfb 100644
      --- a/storage/innobase/srv/srv0srv.cc
      +++ b/storage/innobase/srv/srv0srv.cc
      @@ -2640,6 +2640,7 @@ static ulint srv_do_purge(ulint* n_total_purged
       		n_pages_purged = trx_purge(
       			n_use_threads,
       			(++count % rseg_truncate_frequency) == 0
      +			|| !srv_fast_shutdown
       #ifdef UNIV_DEBUG
       			, slot
       #endif
      

      The output indicates that the transaction identifier is moving backwards across the server restart:

      10.3 b46cf33ab8ce869af0f51c35026965d237d722c7

      innodb.undo_truncate '2,4k,innodb'       [ fail ]
              Test ended at 2021-09-24 10:10:33
       
      CURRENT_TEST: innodb.undo_truncate
      --- /mariadb/10.3/mysql-test/suite/innodb/r/undo_truncate.result	2021-09-22 17:42:10.012874162 +0300
      +++ /mariadb/10.3/mysql-test/suite/innodb/r/undo_truncate.reject	2021-09-24 10:10:32.937465462 +0300
      @@ -35,8 +35,25 @@
       commit;
       disconnect con2;
       connection default;
      +SHOW ENGINE INNODB STATUS;
      +Type	Name	Status
      +InnoDB		41
      +set global innodb_fast_shutdown=0;
      +SHOW ENGINE INNODB STATUS;
      +Type	Name	Status
      +InnoDB		38

      The fix is to write the latest transaction ID value to the rebuilt undo log header page, so that it can be found there on server restart:

      diff --git a/storage/innobase/trx/trx0rseg.cc b/storage/innobase/trx/trx0rseg.cc
      --- a/storage/innobase/trx/trx0rseg.cc
      +++ b/storage/innobase/trx/trx0rseg.cc
      @@ -286,6 +286,7 @@ void trx_rseg_format_upgrade(trx_rsegf_t* rseg_header, mtr_t* mtr)
       /** Create a rollback segment header.
       @param[in,out]	space		system, undo, or temporary tablespace
       @param[in]	rseg_id		rollback segment identifier
      +@param[in]	max_trx_id	new value of TRX_RSEG_MAX_TRX_ID
       @param[in,out]	sys_header	the TRX_SYS page (NULL for temporary rseg)
       @param[in,out]	mtr		mini-transaction
       @return the created rollback segment
      @@ -294,6 +295,7 @@ buf_block_t*
       trx_rseg_header_create(
       	fil_space_t*	space,
       	ulint		rseg_id,
      +	trx_id_t	max_trx_id,
       	buf_block_t*	sys_header,
       	mtr_t*		mtr)
       {
      @@ -319,6 +321,12 @@ trx_rseg_header_create(
       
       	mlog_write_ulint(TRX_RSEG + TRX_RSEG_HISTORY_SIZE + block->frame, 0,
       			 MLOG_4BYTES, mtr);
      +
      +	if (max_trx_id) {
      +		mlog_write_ull(TRX_RSEG + TRX_RSEG_MAX_TRX_ID + block->frame,
      +			       max_trx_id, mtr);
      +	}
      +
       	flst_init(TRX_RSEG + TRX_RSEG_HISTORY + block->frame, mtr);
       	trx_rsegf_t* rsegf = TRX_RSEG + block->frame;
       
      

      (snip many changes to pass the parameter as 0)

      diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc
      index d300e3b54e3..208f400c8a3 100644
      --- a/storage/innobase/trx/trx0purge.cc
      +++ b/storage/innobase/trx/trx0purge.cc
      @@ -977,7 +977,8 @@ trx_purge_initiate_truncate(
       	for (ulint i = 0; i < undo_trunc->rsegs_size(); ++i) {
       		trx_rseg_t*	rseg = undo_trunc->get_ith_rseg(i);
       		buf_block_t* rblock = trx_rseg_header_create(
      -			space, rseg->id, sys_header, &mtr);
      +			space, rseg->id, trx_sys.get_max_trx_id(),
      +			sys_header, &mtr);
       		ut_ad(rblock);
       		rseg->page_no = rblock ? rblock->page.id.page_no() : FIL_NULL;
       
      

      With that fix and the above instrumentation, the transaction identifier would be preserved across restart:

      +SHOW ENGINE INNODB STATUS;
      +Type	Name	Status
      +InnoDB		41
      +set global innodb_fast_shutdown=0;
      +SHOW ENGINE INNODB STATUS;
      +Type	Name	Status
      +InnoDB		43
      

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              marko Marko Mäkelä
              Reporter:
              marko Marko Mäkelä
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved:

                  Git Integration