diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2022-11-23 17:34:05 +0200 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2022-11-23 17:34:05 +0200 |
commit | 165564d3c33ae3d677d70644a83afcb744bdbf65 (patch) | |
tree | 68f774294f76749e9c75eccb86f166086eb73604 /storage/innobase/srv | |
parent | 9d388192c74f65b808702b87eb7dfbd1426717f4 (diff) | |
download | mariadb-git-165564d3c33ae3d677d70644a83afcb744bdbf65.tar.gz |
MDEV-30009 InnoDB shutdown hangs when the change buffer is corrupted
The InnoDB change buffer (ibuf.index, stored in the system tablespace)
and the change buffer bitmaps in persistent tablespaces could get out
of sync with each other: According to the bitmap, no changes exist for
a page, while there actually exist buffered entries in ibuf.index.
InnoDB performs lazy deletion of buffered changes. When a secondary
index leaf page is freed (possibly as part of DROP INDEX), any
buffered changes will not be deleted. Instead, they would be deleted
on a subsequent buf_page_create_low().
One scenario where InnoDB failed to delete buffered changes is
as follows:
1. Some changes were buffered for a secondary index leaf page.
2. The index page had been freed.
3. ibuf_read_merge_pages() invoked ibuf_merge_or_delete_for_page(),
which noticed that the page had been freed, and reset the change buffer
bits, but did not delete the records from ibuf.index.
4. The index page was reallocated for something else.
5. The index page was removed from the buffer pool.
6. Some changes were buffered for the newly created page.
7. Finally, the buffered changes from both 1. and 6. were merged.
8. The index is corrupted.
An alternative outcome is:
4. Shutdown with innodb_fast_shutdown=0 gets into an infinite loop.
An alternative scenario is:
3. ibuf_set_bitmap_for_bulk_load() reset the IBUF_BITMAP_BUFFERED bit
but did not delete the ibuf.index records for that page number.
The shutdown hang was already once fixed in
commit d7a2401750bb29dfdb45929a536539b9f17b347f, refactored for
10.5 in commit 77e8a311e1f919f15845c75d08de4340965c0bc4 and
disabled in commit 310dff5d847b3c117ab6bca8e6ccbcc8bca818d9
due to corruption.
We will fix this as follows:
ibuf_delete_recs(): Delete all ibuf.index entries for the specified page.
ibuf_merge_or_delete_for_page(): When the change buffer bitmap bits
were set and the page had been freed, and the page does not belong
to ibuf.index itself, invoke ibuf_delete_recs(). This prevents the
corruption from occurring when a DML operation is allocating a
previously freed page for which changes had been buffered.
ibuf_set_bitmap_for_bulk_load(): When the change buffer bitmap bits
were set, invoke ibuf_delete_recs(). This prevents the corruption
from occurring when CREATE INDEX is reusing a previously freed page.
ibuf_read_merge_pages(): On slow shutdown, remove the orphan records
by invoking ibuf_delete_recs(). This fixes the hang when the change
buffer had become corrupted. We also remove the dops[] accounting,
because nothing can monitor it during shutdown. We invoke
ibuf_delete_recs() if:
(a) buf_page_get_gen() failed to load the page or merge changes
(b) the page is not a valid index leaf page
(c) the page number is out of tablespace bounds
srv_shutdown(): Invoke ibuf_max_size_update(0) to ensure that
the race condition that motivated us to disable the code in
ibuf_read_merge_pages() in commit 310dff5d847b3c117ab6bca8e6ccbcc8bca818d9
is no longer possible. That is, during slow shutdown, both the
rollback of transactions and the purge of history will return
early from ibuf_insert_low().
ibuf_merge_space(), ibuf_delete_for_discarded_space(): Cleanup:
Do not allocate a memory heap.
This was implemented by Thirunarayanan Balathandayuthapani
and tested with innodb_change_buffering_debug=1 by Matthias Leich.
Diffstat (limited to 'storage/innobase/srv')
-rw-r--r-- | storage/innobase/srv/srv0srv.cc | 4 |
1 files changed, 4 insertions, 0 deletions
diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index 5a01b408a4b..a2d98ad88c4 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -1675,6 +1675,10 @@ void srv_shutdown(bool ibuf_merge) if (ibuf_merge) { srv_main_thread_op_info = "doing insert buffer merge"; + /* Disallow the use of change buffer to + avoid a race condition with + ibuf_read_merge_pages() */ + ibuf_max_size_update(0); log_free_check(); n_read = ibuf_contract(); } |