[MDEV-25683] Atomic DDL: With innodb_force_recovery=3 InnoDB: Trying to load index but the index tree has been freed Created: 2021-05-14 Updated: 2022-11-02 Resolved: 2021-10-29 |
|
| Status: | Closed |
| Project: | MariaDB Server |
| Component/s: | Embedded Server, Server, Storage Engine - InnoDB |
| Affects Version/s: | 10.6.1, 10.6.2 |
| Fix Version/s: | 10.6.5 |
| Type: | Bug | Priority: | Major |
| Reporter: | Elena Stepanova | Assignee: | Marko Mäkelä |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | regression | ||
| Issue Links: |
|
||||||||||||||||||||
| Description |
|
The test was running ALTER TABLE in two threads when it was sigkill-ed. The standard crash recovery seems to work all right (no errors reported and the server starts, other than numerous [Note] records "InnoDB: At LSN: ...: unable to open file ... for tablespace ..."), as well as innodb_force_recovery=[12], but with innodb_force_recovery=3 it throws errors and ends with an assertion failure. The original test was run on bb-10.6-monty-innodb 60f144ef1f. The recovery failure on a stored datadir is reproducible on bb-10.6-monty be1dcc8aa.
Additional remarks:
Data directory and logs are available. There is no rr profile for the initial run; the profile for recovery can always be created, since the failure is easily reproducible by starting the server on the stored datadir. |
| Comments |
| Comment by Marko Mäkelä [ 2021-05-15 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
From the InnoDB point of view, everything works as designed. The parameter innodb_force_recovery=3 prevents any recovered incomplete transactions from being rolled back. Based on the stack trace, it looks like a recovered DDL transaction would exist and hold locks on the InnoDB dictionary tables. Transactional lock waits are not supposed to be possible for InnoDB dictionary tables: the dict_sys.latch is supposed to be covering all modifications to dictionary tables. This problem was introduded with the In InnoDB, there is an internal variable high_level_read_only that would be set if innodb_force_recovery is set to 5 or 6, or innodb_read_only is set. Already the values 2 and 3 are somewhat problematic for the DDL log recovery:
I think that this needs to be addressed with an API change:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Michael Widenius [ 2021-09-07 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I think we need to first understand why one would use a high value for innodb_force_recovery. One problem with this is that if ddl recovery is not done, we will have an issue with probably-correct tables that where used for DDL's during the last crash. I call them possible-correct as these tables where exclusively locked, fully committed (and hopefully flushed)) and has no open transactions on them.
One way to fix this would be to separate innodb_force_recovery and ddl recovery a bit. Some suggestions:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-09-07 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
innodb_force_recovery is somewhat broken by design, and a collection of Boolean flags would be better. Let me enumerate the values:
As you can see, the settings 3 and 4 may prevent DDL recovery. With the settings 5 and 6, DDL recovery must really be refused. A possible simple revision might be as follows:
Can InnoDB plugin initialization be informed that the ddl-recovery.log is not logically empty? If so, then it could just refuse startup when it is undesirable. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-10-26 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I got a patch that implements a predicate ddl_log_check_if_engine_is_used(). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-10-26 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Because ddl_log_check_if_engine_is_used() only accepts the name of a storage engine and not a table, I think that it is simplest to refuse startup if DDL log for InnoDB exists and innodb_read_only=ON or innodb_force_recovery>2 has been specified. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-10-26 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Please fix the init_server_components() in my work-in-progress change. It does not actually fix the bug (by refusing InnoDB startup) because ddl_log_initialize() is being called after innodb_init() and not before it. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-10-27 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
The test case innodb.alter_copy would cover this, but it currently exposes a problem:
In addition to this error, we would ‘leak’ a #sql-alter table from an ALTER TABLE operation that was interrupted by killing the server. Because in the general case it is not possible to apply only part of the DDL recovery log, it may be simplest to make the entire server to refuse start up if some storage engine is unable to perform DDL recovery. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-10-29 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
With the original dataset and with a recent 10.6 that includes a fix of
The server is hanging in ddl_log_execute_recovery() and not responding to SIGQUIT. The culprit is a lock wait on the InnoDB table SYS_TABLES when trying to drop the table #sql-alter-27122f-10. That lock wait will occur because a DDL transaction was recovered, but its rollback was prohibited by innodb_force_recovery=3. The last messages about #sql-alter-27122f-10 are issued by dict_load_indexes() and should be removed by Now, how would the situation change if I set innodb_force_recovery=5 or innodb_read_only=ON? In those modes, InnoDB should refuse any DDL or DML operation, except maybe to temporary tables.
The server will start up, but not nicely. There is no log message saying that we failed to drop the table #sql-alter-27122f-10 from InnoDB. The .frm file will be removed, but the #sql-alter-27122f-10.ibd will remain, as will the InnoDB data dictionary entries for the table. This would not be corrected on the next restart without that InnoDB setting. Note: I would not recommend to trust any dumped data if the server was started up with innodb_force_recovery=5. That would make all transactions use the READ UNCOMMITTED isolation level. Currently, a start-up with innodb_force_recovery=5 would avoid the DDL recovery hang and leave the InnoDB dictionary inconsistent with the DDL log. But, it would also allow a subsequent restart with innodb_force_recovery=3, which should be safer for dumping the data, because the proper REPEATABLE READ isolation level will be available. (If some pointers to undo log are corrupted, then the server might crash while reading some data.) Now, one last attempt. What if we implement my earlier suggestion that we will start to distinguish innodb_force_recovery=4 from innodb_force_recovery=3 in 10.6? (That setting was effectively removed by
The final fix would rename SRV_FORCE_NO_IBUF_MERGE to a more descriptive name SRV_FORCE_NO_DDL_UNDO and change the documentation of innodb_force_recovery. With the above patch, the server will start up and perform the DDL log recovery normally on both data directories:
There will be no #sql files in the data directories afterwards. With or without the above patch, the hang can be reproduced with innodb_force_recovery=4 just fine. And the message about #sql-alter- will be back too:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-10-29 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Fix: innodb_force_recovery=3 will no longer prevent the rollback of recovered DDL transactions. This allows the data set to recover correctly when using innodb_force_recovery=3. Other settings than innodb_force_recovery=3 were unaffected by this change. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Sergei Golubchik [ 2021-10-29 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
greenman, see above and in particular see the commit comment. This impacts the documentation of innodb_force_recovery | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-11-04 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
After
Yes, it will still hang there due to the lock conflict on a dictionary table, because the recovered DDL transaction will not be rolled back at innodb_force_recovery=4. This |