[MDEV-29383] Assertion mysql_mutex_assert_owner(&log_sys.flush_order_mutex) failed in mtr_t::commit() Created: 2022-08-25 Updated: 2022-09-15 Resolved: 2022-08-26 |
|
| Status: | Closed |
| Project: | MariaDB Server |
| Component/s: | Storage Engine - InnoDB |
| Affects Version/s: | 10.6.9, 10.7.5, 10.8.4, 10.9.2, 10.10.1 |
| Fix Version/s: | 10.6.10, 10.7.6, 10.8.5, 10.9.3, 10.10.2 |
| Type: | Bug | Priority: | Critical |
| Reporter: | Marko Mäkelä | Assignee: | Marko Mäkelä |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | recovery, regression | ||
| Attachments: |
|
||||||||||||||||||||||||||||
| Issue Links: |
|
||||||||||||||||||||||||||||
| Description |
|
The assertion mysql_mutex_assert_owner(&log_sys.flush_order_mutex) failed while rolling back an INSERT statement:
The cause of this appears to be that earlier in the mini-transaction, we failed to set the mtr_t::m_made_dirty flag when flagging a freed page as modified. The following change ought to fix that:
If I understood it correctly, this scenario can only be triggered if the page had been written out to the data file before this mini-transaction modified something in the page (most likely, deleted the only record that had been inserted into the page). |
| Comments |
| Comment by Marko Mäkelä [ 2022-08-25 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
For the record, the tree where the error was encountered was something that implemented a fix for If my understanding of the scenario is correct, then this error should occur more frequently when using the following setting:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Matthias Leich [ 2022-08-25 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2022-08-26 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
mleich, were you able to reproduce this bug more often than just the single occurrence? I got an idea for reproducing this, but unfortunately it failed to crash the server:
A more elaborate attempt failed to reproduce it as well:
Outside debug builds, I think that the impact of this bug could be broken crash recovery or backups. Because I am unable to reproduce this, I can’t assess the real impact either. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2022-08-26 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
With my test case modification and ./mtr --rr innodb.page_cleaner I found out that normally, mtr_t::m_made_dirty (indicating that the mini-transaction will have to add a previously clean page to buf_pool.flush_list) would be set earlier during the rollback:
That is, most of the time, it would not matter that mtr_t::free() is not setting the flag, because it would already have been set. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2022-08-26 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I analyzed the trace once more to find out why exactly mtr_t::m_made_dirty had not been set in the first place. It turns out that the page write was completed and buf_page_t::oldest_modification_ reset at the time the ROLLBACK was waiting here:
There is a race condition in btr_cur_latch_leaves():
The mtr_t::memo_push() did invoke mtr_t::is_block_dirtied(), but at that point of time, the write of the dirty block had not been completed. The page write would be protected by a U-latch (previously known as SX-latch), which would conflict with the exclusive latch that we would be waiting for. Only at that point it would be safe to execute mtr_t::memo_push() so that mtr_t::m_made_dirty would be set correctly. This code was refactored by me in
I checked all calls to mtr_t::memo_push() with MTR_MEMO_PAGE_X_FIX or MTR_MEMO_PAGE_SX_FIX, and it turns out that nothing was broken outside I no longer think that Side note: Performance could be improved if did not set mtr_t::m_made_dirty already when registering MTR_MEMO_PAGE_X_FIX or MTR_MEMO_PAGE_SX_FIX, but deferred it until the moment we set the MTR_MEMO_MODIFY flag on a block. In that way, even if a mini-transaction acquired a U or X latch on a page but never modified that page, mtr_t::commit() could avoid acquiring log_sys.flush_order_mutex. We only need that mutex when the mini-transaction actually needs to add a previously clean block to buf_pool.flush_list. I am afraid that it is practically impossible to write a reproducible test case for this. The scheduling of page writes is largely invisible to higher-level code. As far as I can tell, this bug can break crash recovery and backups (explaining | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Matthias Leich [ 2022-08-26 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Up till now I had no luck in reproducing the problem. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2022-09-15 ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I believe that in release builds, this failure could lead to a corrupted database after a seemingly successful backup or crash recovery, like in this failure:
This was with a code revision that did not include the fixes of |