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

Assertion `trx_id <= create_id' failed in innodb_check_version()

    XMLWordPrintable

    Details

      Description

      data_copy.tar.xz trips a debug assertion in InnoDB crash recovery.

      sql/mariadbd --innodb-page-size=4k --datadir /path/to/data_copy
      

      10.6 253806dffc6e80a3bebb1d7fa2fa4b1899252306

      mariadbd: /mariadb/10.6/storage/innobase/handler/ha_innodb.cc:2050: int innodb_check_version(handlerton*, const char*, const LEX_CUSTRING*, ulonglong): Assertion `trx_id <= create_id' failed.
      

      If the failing assertion is removed, the dataset will recover, and the output of INFORMATION_SCHEMA.INNODB_SYS_TABLES will be consistent with .frm files.

      In the rr replay trace of the server that was killed before the failed recovery, there was a successfully completed ALTER TABLE…DROP INDEX operation that correctly caused recovery to report trx_id=1111. A subsequent ALTER TABLE on the same table was actually a no-op for InnoDB, and it did not finish before the server was killed. For that operation, ha_innobase::prepare_inplace_alter_table() returned early:

      	if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)) {
      		/* Nothing to do */
      		DBUG_ASSERT(!m_prebuilt->trx->dict_operation_lock_mode);
      		DBUG_RETURN(false);
      	}
      

      This is why ha_innobase::table_version() ended up reporting m_prebuilt->trx_id=1088, which was recovered as create_id.

      The original purpose of row_prebuilt_t::trx_id and dict_table_t::def_trx_id was to prevent recently created secondary indexes or rebuilt tables from being (first-time) accessed by transactions whose read views were created before the ALTER TABLE operation was started and finished. In MDEV-25180, the purpose was extended to cover the recovery of ALTER TABLE, to determine whether an operation was committed inside the storage engine.

      Because the being-recovered ALTER TABLE operation was a no-op for InnoDB, it does not really matter which table definition version was recovered. It could be useful to retain the debug assertion, so that it can catch more genuine errors in the future.

      If I patch the little-endian 32-bit value 0x440 (1088) at byte offset 0x3020 in the data_copy/ddl_recovery.log file that is contained in data_copy.tar.xz to 0, then the data directory will recover just fine without tripping the assertion. This should be equivalent to having run the killed server with the following fix:

      diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
      index 889a53889d2..4a8861dd5dc 100644
      --- a/storage/innobase/handler/handler0alter.cc
      +++ b/storage/innobase/handler/handler0alter.cc
      @@ -7614,6 +7614,7 @@ ha_innobase::prepare_inplace_alter_table(
       	if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)) {
       		/* Nothing to do */
       		DBUG_ASSERT(!m_prebuilt->trx->dict_operation_lock_mode);
      +		m_prebuilt->trx_id = 0;
       		DBUG_RETURN(false);
       	}
       
      

      The only other DBUG_RETURN(false) in that function can also set m_prebuilt->trx_id to 0, so this should be fine from the correctness point of view.

      I tried to create a test case for reproducing this, but failed, with both values of innodb_file_per_table. Here is my attempt:

      --source include/have_innodb.inc
      --source include/have_debug.inc
      --source include/have_debug_sync.inc
       
      connection default;
      CREATE TABLE t1(a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
      ALTER TABLE t1 ADD INDEX (b);
       
      connect ddl,localhost,root;
      SET DEBUG_SYNC='innodb_commit_inplace_alter_table_enter SIGNAL stuck WAIT_FOR ever';
      send ALTER TABLE t1 MODIFY b INT NULL;
       
      connection default;
      SET DEBUG_SYNC='now WAIT_FOR stuck';
       
      --let $shutdown_timeout=0
      --source include/restart_mysqld.inc
       
      disconnect ddl;
       
      SELECT * FROM t1;
      DROP TABLE t1;
      

      As far as I can tell, this bug does not affect the correctness of recovery for non-debug builds.

        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

                  Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.