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.

This is improved from commit 1cb218c37c
which was applied to earlier-version branches.

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:
(1) durably write the log
(2) release the page latches of the rebuilt tablespace
(3) release the mutexes
(4) truncate the file
(5) release the tablespace latch
This is 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-24 08:22:19 +03:00
commit f5fddae3cb
9 changed files with 173 additions and 119 deletions

View file

@ -107,12 +107,16 @@ be flushed to the file system
@param[in] rotate_key whether to rotate the encryption key */
void log_write_up_to(lsn_t lsn, bool flush_to_disk, bool rotate_key = false);
/** write to the log file up to the last log entry.
@param[in] sync whether we want the written log
also to be flushed to disk. */
void
log_buffer_flush_to_disk(
bool sync = true);
/** Write to the log file up to the last log entry.
@param sync whether to wait for a durable write to complete */
void log_buffer_flush_to_disk(bool sync= true);
/** Prepare to invoke log_write_and_flush(), before acquiring log_sys.mutex. */
ATTRIBUTE_COLD void log_write_and_flush_prepare();
/** Durably write the log up to log_sys.lsn() and release log_sys.mutex. */
ATTRIBUTE_COLD void log_write_and_flush();
/** Make a checkpoint */
ATTRIBUTE_COLD void log_make_checkpoint();

View file

@ -98,6 +98,10 @@ struct mtr_t {
/** Commit the mini-transaction. */
void commit();
/** Commit a mini-transaction that is shrinking a tablespace.
@param space tablespace that is being shrunk */
ATTRIBUTE_COLD void commit_shrink(fil_space_t &space);
/** Commit a mini-transaction that did not modify any pages,
but generated some redo log on a higher level, such as
FILE_MODIFY records and an optional FILE_CHECKPOINT marker.

View file

@ -48,8 +48,8 @@ mtr_t::memo_push(void* object, mtr_memo_type_t type)
/* If this mtr has x-fixed a clean page then we set
the made_dirty flag. This tells us if we need to
grab log_flush_order_mutex at mtr_commit so that we
can insert the dirtied page to the flush list. */
grab log_sys.flush_order_mutex at mtr_t::commit() so that we
can insert the dirtied page into the flush list. */
if ((type == MTR_MEMO_PAGE_X_FIX || type == MTR_MEMO_PAGE_SX_FIX)
&& !m_made_dirty) {