MDEV-26450: Corruption due to innodb_undo_log_truncate

At least since commit 055a3334ad
(MDEV-13564) the undo log truncation in InnoDB did not work correctly.

The main issue is that during the execution of
trx_purge_truncate_history() some pages of the newly truncated
undo tablespace could be discarded.

fsp_try_extend_data_file(): Apply the peculiar rounding of
fil_space_t::size_in_header only to the system tablespace,
whose size can be expressed in megabytes in a configuration parameter.
Other files may freely grow by a number of pages.

fseg_alloc_free_page_low(): Do allow the extension of undo tablespaces,
and mention the file name in the error message.

mtr_t::commit_shrink(): Implement crash-safe shrinking of a tablespace
file. First, durably write the log, then shrink the file, and finally
release the page latches of the rebuilt tablespace. Refactored from
trx_purge_truncate_history().

log_write_and_flush_prepare(), log_write_and_flush(): New functions
to durably write log during mtr_t::commit_shrink().
This commit is contained in:
Marko Mäkelä 2021-09-22 14:15:00 +03:00
commit 1cb218c37c
9 changed files with 219 additions and 123 deletions

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2020, MariaDB Corporation.
Copyright (c) 2017, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -1062,26 +1062,11 @@ trx_purge_initiate_truncate(
rseg->last_del_marks = FALSE;
}
mtr.commit();
/* Write-ahead the redo log record. */
log_write_up_to(mtr.commit_lsn(), true);
mtr.commit_shrink(*space);
/* Trim the file size. */
os_file_truncate(file->name, file->handle,
os_offset_t(size) << srv_page_size_shift, true);
/* This is only executed by the srv_purge_coordinator_thread. */
/* No mutex; this is only updated by the purge coordinator. */
export_vars.innodb_undo_truncations++;
/* TODO: PUNCH_HOLE the garbage (with write-ahead logging) */
mutex_enter(&fil_system->mutex);
ut_ad(space->stop_new_ops);
ut_ad(space->is_being_truncated);
space->stop_new_ops = false;
space->is_being_truncated = false;
mutex_exit(&fil_system->mutex);
if (purge_sys->rseg != NULL
&& purge_sys->rseg->last_page_no == FIL_NULL) {
/* If purge_sys->rseg is pointing to rseg that was recently