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

Assertion `!((flags & ((~(~0U << 1)) << 0)) >> 0) || flags2 & 16U' failed in dict_check_sys_tables on upgrade from 5.5

Details

    Description

      The attached datadir was created on MariaDB 5.5.50 by running simple workflow and then shutting down the server normally (general log is also attached).

      Further attempt to start 10.2 (with all default options) on this datadir causes the assertion failure:

      10.2 8f98835bb86

      mysqld: /data/src/10.2/storage/innobase/dict/dict0load.cc:1444: ulint dict_check_sys_tables(bool): Assertion `!((flags & ((~(~0U << 1)) << 0)) >> 0) || flags2 & 16U' failed.
      180308  0:37:35 [ERROR] mysqld got signal 6 ;
       
      #7  0x00007f9071a9eee2 in __assert_fail () from /lib/x86_64-linux-gnu/libc.so.6
      #8  0x00005571a9f25955 in dict_check_sys_tables (validate=false) at /data/src/10.2/storage/innobase/dict/dict0load.cc:1443
      #9  0x00005571a9f25d93 in dict_check_tablespaces_and_store_max_id (validate=false) at /data/src/10.2/storage/innobase/dict/dict0load.cc:1535
      #10 0x00005571a9e09104 in innobase_start_or_create_for_mysql () at /data/src/10.2/storage/innobase/srv/srv0start.cc:2543
      #11 0x00005571a9c4de38 in innobase_init (p=0x5571ab947320) at /data/src/10.2/storage/innobase/handler/ha_innodb.cc:4359
      #12 0x00005571a9941180 in ha_initialize_handlerton (plugin=0x5571ab91ca00) at /data/src/10.2/sql/handler.cc:520
      #13 0x00005571a96e74ca in plugin_initialize (tmp_root=0x7fff47626e00, plugin=0x5571ab91ca00, argc=0x5571aac5f9f0 <remaining_argc>, argv=0x5571ab8c77f8, options_only=false) at /data/src/10.2/sql/sql_plugin.cc:1416
      #14 0x00005571a96e80d8 in plugin_init (argc=0x5571aac5f9f0 <remaining_argc>, argv=0x5571ab8c77f8, flags=2) at /data/src/10.2/sql/sql_plugin.cc:1698
      #15 0x00005571a95f13a9 in init_server_components () at /data/src/10.2/sql/mysqld.cc:5268
      #16 0x00005571a95f242e in mysqld_main (argc=8, argv=0x5571ab8c77f8) at /data/src/10.2/sql/mysqld.cc:5858
      #17 0x00005571a95e71d0 in main (argc=7, argv=0x7fff47627b98) at /data/src/10.2/sql/main.cc:25
      

      10.1 starts all right. Starting 10.1 and running mysql_upgrade doesn't help, 10.2 still fails after that.
      10.2.6 also fails, I didn't check earlier 10.2 builds.
      no visible effect on a non-debug build.

      Please note that I had to pack ibdata/ib_logfiles separately, to get around 10Mb limitation in JIRA. Put them back to the datadir.

      Attachments

        1. data.tar.gz
          2.90 MB
        2. ibfiles.tar.gz
          8.45 MB
        3. mysql.log.gz
          2.72 MB

        Issue Links

          Activity

            The problematic SYS_TABLES clustered index record contents is as follows:

            name contents
            NAME 'test/t001'
            DB_TRX_ID 0x3e3
            DB_ROLL_PTR (insert)
            ID 0x16
            N_COLS COMPACT|13
            TYPE 1
            MIX_ID 0
            MIX_LEN 0
            CLUSTER_NAME NULL
            SPACE 0x13

            This looks valid to me. The fields MIX_ID, MIX_LEN and CLUSTER_NAME were originally unused and in very old InnoDB versions written as garbage, so for ROW_FORMAT=REDUNDANT we must ignore these fields. The fields were originally intended to be used for mixed clustered indexes, where a clustered index could contain records from multiple tables sharing a similar primary key.

            The MIX_LEN field was repurposed for additional table flags in the InnoDB Plugin for MySQL 5.1: Applying InnoDB snapshot, fixes BUG#41609. Until MySQL 5.6, the only flag was DICT_TF2_TEMPORARY, intended to suppress warnings about non-found files for temporary tables. (Until MySQL 5.7 and MariaDB 10.2, InnoDB treated temporary tables as persistent ones.) 5.6 introduced further persistently stored flags in SYS_TABLES.MIX_LEN mainly for passing transient information during table creation.

            The failing debug assertion seems bogus:

            		/* If the table is not a predefined tablespace then it must
            		be in a file-per-table tablespace.
            		Note that flags2 is not available for REDUNDANT tables,
            		so don't check those. */
            		ut_ad(!DICT_TF_GET_COMPACT(flags)
            		      || flags2 & DICT_TF2_USE_FILE_PER_TABLE);
            

            The flag DICT_TF2_USE_FILE_PER_TABLE was introduced in SYS_TABLES.MIX_LEN, originally called DICT_TF2_USE_TABLESPACE in MySQL 5.6.5. It does not make sense to store this transient information ("was innodb_file_per_table set at the start of CREATE TABLE?") in a persistent data structure, but that is what was done. Finally, MySQL 5.7.6 introduced this bogus assertion in a change that was approved by me. (My bad, I should have remembered that the flag did not exist before 5.6.)

            I believe that Oracle does not test upgrades with debug binaries. The fix is simple: remove the bogus debug assertion. We must only ensure that if a table that resides in an .ibd file is rebuilt, it will remain in an .ibd file even if innodb_file_per_table=0:

            SET GLOBAL innodb_file_per_table=0;
            ALTER TABLE t001 FORCE;
            

            That actually was the case, after I removed one more failing debug assertion in dict_table_is_file_per_table():

            	/* If the table is file-per-table and it is not redundant, then
            	it should have the flags2 bit for DICT_TF2_USE_FILE_PER_TABLE. */
            	ut_ad(!is_file_per_table
            	      || !DICT_TF_GET_COMPACT(table->flags)
            	      || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_USE_FILE_PER_TABLE));
            

            After these removals, the remaining references to DICT_TF2_USE_TABLESPACE look reasonable. It would seem reasonable to stop setting the flag in MIX_LEN altogether, but that would cause trouble when downgrading to earlier versions. So, we should basically treat the flag as garbage.

            marko Marko Mäkelä added a comment - The problematic SYS_TABLES clustered index record contents is as follows: name contents NAME 'test/t001' DB_TRX_ID 0x3e3 DB_ROLL_PTR (insert) ID 0x16 N_COLS COMPACT|13 TYPE 1 MIX_ID 0 MIX_LEN 0 CLUSTER_NAME NULL SPACE 0x13 This looks valid to me. The fields MIX_ID , MIX_LEN and CLUSTER_NAME were originally unused and in very old InnoDB versions written as garbage, so for ROW_FORMAT=REDUNDANT we must ignore these fields. The fields were originally intended to be used for mixed clustered indexes, where a clustered index could contain records from multiple tables sharing a similar primary key. The MIX_LEN field was repurposed for additional table flags in the InnoDB Plugin for MySQL 5.1: Applying InnoDB snapshot, fixes BUG#41609 . Until MySQL 5.6, the only flag was DICT_TF2_TEMPORARY , intended to suppress warnings about non-found files for temporary tables. (Until MySQL 5.7 and MariaDB 10.2, InnoDB treated temporary tables as persistent ones.) 5.6 introduced further persistently stored flags in SYS_TABLES.MIX_LEN mainly for passing transient information during table creation. The failing debug assertion seems bogus: /* If the table is not a predefined tablespace then it must be in a file-per-table tablespace. Note that flags2 is not available for REDUNDANT tables, so don't check those. */ ut_ad(!DICT_TF_GET_COMPACT(flags) || flags2 & DICT_TF2_USE_FILE_PER_TABLE); The flag DICT_TF2_USE_FILE_PER_TABLE was introduced in SYS_TABLES.MIX_LEN , originally called DICT_TF2_USE_TABLESPACE in MySQL 5.6.5 . It does not make sense to store this transient information ("was innodb_file_per_table set at the start of CREATE TABLE ?") in a persistent data structure, but that is what was done. Finally, MySQL 5.7.6 introduced this bogus assertion in a change that was approved by me. (My bad, I should have remembered that the flag did not exist before 5.6.) I believe that Oracle does not test upgrades with debug binaries. The fix is simple: remove the bogus debug assertion. We must only ensure that if a table that resides in an .ibd  file is rebuilt, it will remain in an .ibd  file even if innodb_file_per_table=0 : SET GLOBAL innodb_file_per_table=0; ALTER TABLE t001 FORCE; That actually was the case, after I removed one more failing debug assertion in dict_table_is_file_per_table() : /* If the table is file-per-table and it is not redundant, then it should have the flags2 bit for DICT_TF2_USE_FILE_PER_TABLE. */ ut_ad(!is_file_per_table || !DICT_TF_GET_COMPACT(table->flags) || DICT_TF2_FLAG_IS_SET(table, DICT_TF2_USE_FILE_PER_TABLE)); After these removals, the remaining references to DICT_TF2_USE_TABLESPACE look reasonable. It would seem reasonable to stop setting the flag in MIX_LEN altogether, but that would cause trouble when downgrading to earlier versions. So, we should basically treat the flag as garbage.

            This bug only affected debug builds. Release builds may have been affected before MDEV-13084 (MariaDB 10.2.7).

            marko Marko Mäkelä added a comment - This bug only affected debug builds. Release builds may have been affected before MDEV-13084 (MariaDB 10.2.7).

            People

              marko Marko Mäkelä
              elenst Elena Stepanova
              Votes:
              0 Vote for this issue
              Watchers:
              2 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.