MDEV-13564: Remove old crash-upgrade logic in 10.4

Stop supporting the additional *trunc.log files that were
introduced via MySQL 5.7 to MariaDB Server 10.2 and 10.3.

DB_TABLESPACE_TRUNCATED: Remove.

purge_sys.truncate: A new structure to track undo tablespace
file truncation.

srv_start(): Remove the call to buf_pool_invalidate(). It is
no longer necessary, given that we no longer access things in
ways that violate the ARIES protocol. This call was originally
added for innodb_file_format, and it may later have been necessary
for the proper function of the MySQL 5.7 TRUNCATE recovery, which
we are now removing.

trx_purge_cleanse_purge_queue(): Take the undo tablespace as a
parameter.

trx_purge_truncate_history(): Rewrite everything mostly in a
single function, replacing references to undo::Truncate.

recv_apply_hashed_log_recs(): If any redo log is to be applied,
and if the log_sys.log.subformat indicates that separately
logged truncate may have been used, refuse to proceed except if
innodb_force_recovery is set. We will still refuse crash-upgrade
if TRUNCATE TABLE was logged. Undo tablespace truncation would
only be logged in undo*trunc.log files, which we are no longer
checking for.
This commit is contained in:
Marko Mäkelä 2018-09-10 18:01:54 +03:00
parent 67fa97dc2c
commit 09af00cbde
40 changed files with 397 additions and 3829 deletions

View file

@ -628,15 +628,6 @@ static void backup_optimized_ddl_op(ulint space_id)
pthread_mutex_unlock(&backup_mutex);
}
/** Callback whenever MLOG_TRUNCATE happens. */
static void backup_truncate_fail()
{
msg("mariabackup: Incompatible TRUNCATE operation detected.%s\n",
opt_lock_ddl_per_table
? ""
: " Use --lock-ddl-per-table to lock all tables before backup.");
}
/* ======== Date copying thread context ======== */
typedef struct {
@ -4179,7 +4170,6 @@ fail_before_log_copying_thread_start:
log_copy_scanned_lsn = checkpoint_lsn_start;
recv_sys->recovered_lsn = log_copy_scanned_lsn;
log_optimized_ddl_op = backup_optimized_ddl_op;
log_truncate = backup_truncate_fail;
if (xtrabackup_copy_logfile())
goto fail_before_log_copying_thread_start;

View file

@ -125,13 +125,13 @@ WHERE engine = 'innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
COUNT(*)
1
FOUND 2 /InnoDB: Upgrading redo log:/ in mysqld.1.err
FOUND 3 /InnoDB: Upgrading redo log:/ in mysqld.1.err
# Minimal MariaDB 10.1.21 encrypted redo log
SELECT COUNT(*) `1` FROM INFORMATION_SCHEMA.ENGINES WHERE engine='innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
1
1
FOUND 2 /InnoDB: Encrypting redo log/ in mysqld.1.err
FOUND 1 /InnoDB: Encrypting redo log/ in mysqld.1.err
ib_buffer_pool
ib_logfile0
ib_logfile1

View file

@ -125,7 +125,7 @@ WHERE engine = 'innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
COUNT(*)
1
FOUND 2 /InnoDB: Upgrading redo log:/ in mysqld.1.err
FOUND 3 /InnoDB: Upgrading redo log:/ in mysqld.1.err
# Minimal MariaDB 10.1.21 encrypted redo log
SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb'

View file

@ -113,7 +113,6 @@ SET(INNOBASE_SOURCES
row/row0purge.cc
row/row0row.cc
row/row0sel.cc
row/row0trunc.cc
row/row0uins.cc
row/row0umod.cc
row/row0undo.cc
@ -179,7 +178,6 @@ IF(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
mtr/mtr0mtr.cc
row/row0merge.cc
row/row0mysql.cc
row/row0trunc.cc
srv/srv0srv.cc
COMPILE_FLAGS "-O0"
)

View file

@ -438,7 +438,7 @@ btr_page_create(
ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
if (page_zip) {
page_create_zip(block, index, level, 0, NULL, mtr);
page_create_zip(block, index, level, 0, mtr);
} else {
page_create(block, mtr, dict_table_is_comp(index->table),
dict_index_is_spatial(index));
@ -1179,21 +1179,18 @@ btr_free_root_check(
/** Create the root node for a new index tree.
@param[in] type type of the index
@param[in,out] space tablespace where created
@param[in] index_id index id
@param[in] index index, or NULL when applying TRUNCATE
log record during recovery
@param[in] btr_redo_create_info used for applying TRUNCATE log
@param[in] mtr mini-transaction handle
record during recovery
@return page number of the created root, FIL_NULL if did not succeed */
@param[in,out] space tablespace where created
@param[in] index index
@param[in,out] mtr mini-transaction
@return page number of the created root
@retval FIL_NULL if did not succeed */
ulint
btr_create(
ulint type,
fil_space_t* space,
index_id_t index_id,
dict_index_t* index,
const btr_create_t* btr_redo_create_info,
mtr_t* mtr)
{
buf_block_t* block;
@ -1208,7 +1205,7 @@ btr_create(
(for an ibuf tree, not in the root, but on a separate ibuf header
page) */
if (type & DICT_IBUF) {
if (UNIV_UNLIKELY(type & DICT_IBUF)) {
/* Allocate first the ibuf header page */
buf_block_t* ibuf_hdr_block = fseg_create(
space, 0,
@ -1273,44 +1270,11 @@ btr_create(
page_zip = buf_block_get_page_zip(block);
if (page_zip) {
if (index != NULL) {
page = page_create_zip(block, index, 0, 0, NULL, mtr);
} else {
/* Create a compressed index page when applying
TRUNCATE log record during recovery */
ut_ad(btr_redo_create_info != NULL);
redo_page_compress_t page_comp_info;
page_comp_info.type = type;
page_comp_info.index_id = index_id;
page_comp_info.n_fields =
btr_redo_create_info->n_fields;
page_comp_info.field_len =
btr_redo_create_info->field_len;
page_comp_info.fields = btr_redo_create_info->fields;
page_comp_info.trx_id_pos =
btr_redo_create_info->trx_id_pos;
page = page_create_zip(block, NULL, 0, 0,
&page_comp_info, mtr);
}
page = page_create_zip(block, index, 0, 0, mtr);
} else {
if (index != NULL) {
page = page_create(block, mtr,
dict_table_is_comp(index->table),
dict_index_is_spatial(index));
} else {
ut_ad(btr_redo_create_info != NULL);
page = page_create(
block, mtr, btr_redo_create_info->format_flags,
type == DICT_SPATIAL);
}
page = page_create(block, mtr,
dict_table_is_comp(index->table),
dict_index_is_spatial(index));
/* Set the level of the new index page */
btr_page_set_level(page, NULL, 0, mtr);
}
@ -1322,18 +1286,14 @@ btr_create(
btr_page_set_next(page, page_zip, FIL_NULL, mtr);
btr_page_set_prev(page, page_zip, FIL_NULL, mtr);
/* We reset the free bits for the page to allow creation of several
trees in the same mtr, otherwise the latch on a bitmap page would
prevent it because of the latching order.
index will be NULL if we are recreating the table during recovery
on behalf of TRUNCATE.
/* We reset the free bits for the page in a separate
mini-transaction to allow creation of several trees in the
same mtr, otherwise the latch on a bitmap page would prevent
it because of the latching order.
Note: Insert Buffering is disabled for temporary tables given that
most temporary tables are smaller in size and short-lived. */
if (!(type & DICT_CLUSTERED)
&& (index == NULL || !index->table->is_temporary())) {
if (!(type & DICT_CLUSTERED) && !index->table->is_temporary()) {
ibuf_reset_free_bits(block);
}
@ -1675,7 +1635,7 @@ btr_page_reorganize_low(
}
if (page_zip
&& !page_zip_compress(page_zip, page, index, z_level, NULL, mtr)) {
&& !page_zip_compress(page_zip, page, index, z_level, mtr)) {
/* Restore the old page and exit. */
#if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG
@ -1924,7 +1884,7 @@ btr_page_empty(
: 0;
if (page_zip) {
page_create_zip(block, index, level, autoinc, NULL, mtr);
page_create_zip(block, index, level, autoinc, mtr);
} else {
page_create(block, mtr, dict_table_is_comp(index->table),
dict_index_is_spatial(index));

View file

@ -94,7 +94,7 @@ PageBulk::init()
if (new_page_zip) {
page_create_zip(new_block, m_index, m_level, 0,
NULL, &m_mtr);
&m_mtr);
memset(FIL_PAGE_PREV + new_page, 0xff, 8);
page_zip_write_header(new_page_zip,
FIL_PAGE_PREV + new_page,
@ -374,7 +374,7 @@ PageBulk::compress()
ut_ad(m_page_zip != NULL);
return(page_zip_compress(m_page_zip, m_page, m_index,
page_zip_level, NULL, &m_mtr));
page_zip_level, &m_mtr));
}
/** Get node pointer

View file

@ -6248,7 +6248,6 @@ database_corrupted:
&& !recv_no_ibuf_operations
&& (bpage->id.space() == 0
|| !is_predefined_tablespace(bpage->id.space()))
&& !srv_is_tablespace_truncated(bpage->id.space())
&& fil_page_get_type(frame) == FIL_PAGE_INDEX
&& page_is_leaf(frame)) {

View file

@ -556,12 +556,9 @@ buf_dblwr_process()
if (page_no >= space->size) {
/* Do not report the warning if the tablespace
is scheduled for truncation or was truncated
and we have parsed an MLOG_TRUNCATE record. */
if (!srv_is_tablespace_truncated(space_id)
&& !srv_was_tablespace_truncated(space)
&& !srv_is_undo_tablespace(space_id)) {
/* Do not report the warning for undo
tablespaces, because they can be truncated in place. */
if (!srv_is_undo_tablespace(space_id)) {
ib::warn() << "A copy of page " << page_id
<< " in the doublewrite buffer slot "
<< page_no_dblwr

View file

@ -95,11 +95,9 @@ buffer buf_pool if it is not already there, in which case does nothing.
Sets the io_fix flag and sets an exclusive lock on the buffer frame. The
flag is cleared and the x-lock released by an i/o-handler thread.
@param[out] err DB_SUCCESS, DB_TABLESPACE_DELETED or
DB_TABLESPACE_TRUNCATED if we are trying
to read from a non-existent tablespace, a
tablespace which is just now being dropped,
or a tablespace which is truncated
@param[out] err DB_SUCCESS or DB_TABLESPACE_DELETED
if we are trying
to read from a non-existent tablespace
@param[in] sync true if synchronous aio is desired
@param[in] type IO type, SIMULATED, IGNORE_MISSING
@param[in] mode BUF_READ_IBUF_PAGES_ONLY, ...,
@ -187,20 +185,8 @@ buf_read_page_low(
}
if (*err != DB_SUCCESS) {
if (*err == DB_TABLESPACE_TRUNCATED) {
/* Remove the page which is outside the
truncated tablespace bounds when recovering
from a crash happened during a truncation */
buf_read_page_handle_error(bpage);
if (recv_recovery_on) {
mutex_enter(&recv_sys->mutex);
ut_ad(recv_sys->n_addrs > 0);
recv_sys->n_addrs--;
mutex_exit(&recv_sys->mutex);
}
return(0);
} else if (IORequest::ignore_missing(type)
|| *err == DB_TABLESPACE_DELETED) {
if (IORequest::ignore_missing(type)
|| *err == DB_TABLESPACE_DELETED) {
buf_read_page_handle_error(bpage);
return(0);
}
@ -369,7 +355,6 @@ read_ahead:
switch (err) {
case DB_SUCCESS:
case DB_TABLESPACE_TRUNCATED:
case DB_ERROR:
break;
case DB_TABLESPACE_DELETED:
@ -472,7 +457,6 @@ buf_read_page_background(
switch (err) {
case DB_SUCCESS:
case DB_TABLESPACE_TRUNCATED:
case DB_ERROR:
break;
case DB_TABLESPACE_DELETED:
@ -755,7 +739,6 @@ buf_read_ahead_linear(
switch (err) {
case DB_SUCCESS:
case DB_TABLESPACE_TRUNCATED:
case DB_TABLESPACE_DELETED:
case DB_ERROR:
break;
@ -853,7 +836,6 @@ tablespace_deleted:
switch(err) {
case DB_SUCCESS:
case DB_TABLESPACE_TRUNCATED:
case DB_ERROR:
break;
case DB_TABLESPACE_DELETED:

View file

@ -214,7 +214,7 @@ dict_hdr_create(
/*--------------------------*/
root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE,
fil_system.sys_space, DICT_TABLES_ID,
dict_ind_redundant, NULL, mtr);
dict_ind_redundant, mtr);
if (root_page_no == FIL_NULL) {
return(FALSE);
@ -225,7 +225,7 @@ dict_hdr_create(
/*--------------------------*/
root_page_no = btr_create(DICT_UNIQUE,
fil_system.sys_space, DICT_TABLE_IDS_ID,
dict_ind_redundant, NULL, mtr);
dict_ind_redundant, mtr);
if (root_page_no == FIL_NULL) {
return(FALSE);
@ -236,7 +236,7 @@ dict_hdr_create(
/*--------------------------*/
root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE,
fil_system.sys_space, DICT_COLUMNS_ID,
dict_ind_redundant, NULL, mtr);
dict_ind_redundant, mtr);
if (root_page_no == FIL_NULL) {
return(FALSE);
@ -247,7 +247,7 @@ dict_hdr_create(
/*--------------------------*/
root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE,
fil_system.sys_space, DICT_INDEXES_ID,
dict_ind_redundant, NULL, mtr);
dict_ind_redundant, mtr);
if (root_page_no == FIL_NULL) {
return(FALSE);
@ -258,7 +258,7 @@ dict_hdr_create(
/*--------------------------*/
root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE,
fil_system.sys_space, DICT_FIELDS_ID,
dict_ind_redundant, NULL, mtr);
dict_ind_redundant, mtr);
if (root_page_no == FIL_NULL) {
return(FALSE);

View file

@ -863,7 +863,7 @@ dict_create_index_tree_step(
node->page_no = btr_create(
index->type, index->table->space,
index->id, index, NULL, &mtr);
index->id, index, &mtr);
if (node->page_no == FIL_NULL) {
err = DB_OUT_OF_FILE_SPACE;
@ -909,7 +909,7 @@ dict_create_index_tree_in_mem(
ut_ad(!(index->table->flags2 & DICT_TF2_DISCARDED));
index->page = btr_create(index->type, index->table->space,
index->id, index, NULL, &mtr);
index->id, index, &mtr);
mtr_commit(&mtr);
index->trx_id = trx->id;
@ -975,13 +975,6 @@ dict_drop_index_tree(
return(false);
}
/* If tablespace is scheduled for truncate, do not try to drop
the indexes in that tablespace. There is a truncate fixup action
which will take care of it. */
if (srv_is_tablespace_truncated(space)) {
return(false);
}
btr_free_if_exists(page_id_t(space, root_page_no), page_size,
mach_read_from_8(ptr), mtr);
@ -1057,7 +1050,7 @@ dict_recreate_index_tree(
ulint root_page_no = (index->type & DICT_FTS)
? FIL_NULL
: btr_create(type, table->space,
index_id, index, NULL, mtr);
index_id, index, mtr);
index->page = unsigned(root_page_no);
return root_page_no;
}

View file

@ -45,7 +45,6 @@ Created 10/25/1995 Heikki Tuuri
#include "os0file.h"
#include "page0zip.h"
#include "row0mysql.h"
#include "row0trunc.h"
#include "srv0start.h"
#include "trx0purge.h"
#include "ut0new.h"
@ -553,8 +552,7 @@ fil_node_open_file(
if (first_time_open
|| (space->purpose == FIL_TYPE_TABLESPACE
&& node == UT_LIST_GET_FIRST(space->chain)
&& srv_startup_is_before_trx_rollback_phase
&& !undo::Truncate::was_tablespace_truncated(space->id))) {
&& srv_startup_is_before_trx_rollback_phase)) {
/* We do not know the size of the file yet. First we
open the file in the normal mode, no async I/O here,
for simplicity. Then do some checks, and close the
@ -4241,7 +4239,7 @@ fil_report_invalid_page_access(
@param[in] message message for aio handler if non-sync aio
used, else ignored
@param[in] ignore_missing_space true=ignore missing space duging read
@return DB_SUCCESS, DB_TABLESPACE_DELETED or DB_TABLESPACE_TRUNCATED
@return DB_SUCCESS, or DB_TABLESPACE_DELETED
if we are trying to do i/o on a tablespace which does not exist */
dberr_t
fil_io(
@ -4373,19 +4371,6 @@ fil_io(
break;
} else {
if (space->id != TRX_SYS_SPACE
&& UT_LIST_GET_LEN(space->chain) == 1
&& (srv_is_tablespace_truncated(space->id)
|| srv_was_tablespace_truncated(space))
&& req_type.is_read()) {
/* Handle page which is outside the truncated
tablespace bounds when recovering from a crash
happened during a truncation */
mutex_exit(&fil_system.mutex);
return(DB_TABLESPACE_TRUNCATED);
}
cur_page_no -= node->size;
node = UT_LIST_GET_NEXT(chain, node);
@ -5126,116 +5111,6 @@ fil_names_clear(
return(do_write);
}
/** Truncate a single-table tablespace. The tablespace must be cached
in the memory cache.
@param space_id space id
@param dir_path directory path
@param tablename the table name in the usual
databasename/tablename format of InnoDB
@param flags tablespace flags
@param trunc_to_default truncate to default size if tablespace
is being newly re-initialized.
@return DB_SUCCESS or error */
dberr_t
truncate_t::truncate(
/*=================*/
ulint space_id,
const char* dir_path,
const char* tablename,
ulint flags,
bool trunc_to_default)
{
dberr_t err = DB_SUCCESS;
char* path;
ut_a(!is_system_tablespace(space_id));
if (FSP_FLAGS_HAS_DATA_DIR(flags)) {
ut_ad(dir_path != NULL);
path = fil_make_filepath(dir_path, tablename, IBD, true);
} else {
path = fil_make_filepath(NULL, tablename, IBD, false);
}
if (path == NULL) {
return(DB_OUT_OF_MEMORY);
}
mutex_enter(&fil_system.mutex);
fil_space_t* space = fil_space_get_by_id(space_id);
/* The following code must change when InnoDB supports
multiple datafiles per tablespace. */
ut_a(UT_LIST_GET_LEN(space->chain) == 1);
fil_node_t* node = UT_LIST_GET_FIRST(space->chain);
if (trunc_to_default) {
space->size = node->size = FIL_IBD_FILE_INITIAL_SIZE;
}
const bool already_open = node->is_open();
if (!already_open) {
bool ret;
node->handle = os_file_create_simple_no_error_handling(
innodb_data_file_key, path, OS_FILE_OPEN,
OS_FILE_READ_WRITE,
space->purpose != FIL_TYPE_TEMPORARY
&& srv_read_only_mode, &ret);
if (!ret) {
ib::error() << "Failed to open tablespace file "
<< path << ".";
ut_free(path);
return(DB_ERROR);
}
ut_a(node->is_open());
}
os_offset_t trunc_size = trunc_to_default
? FIL_IBD_FILE_INITIAL_SIZE
: space->size;
const bool success = os_file_truncate(
path, node->handle, trunc_size << srv_page_size_shift);
if (!success) {
ib::error() << "Cannot truncate file " << path
<< " in TRUNCATE TABLESPACE.";
err = DB_ERROR;
}
space->stop_new_ops = false;
/* If we opened the file in this function, close it. */
if (!already_open) {
bool closed = os_file_close(node->handle);
if (!closed) {
ib::error() << "Failed to close tablespace file "
<< path << ".";
err = DB_ERROR;
} else {
node->handle = OS_FILE_CLOSED;
}
}
mutex_exit(&fil_system.mutex);
ut_free(path);
return(err);
}
/* Unit Tests */
#ifdef UNIV_ENABLE_UNIT_TEST_MAKE_FILEPATH
#define MF fil_make_filepath

View file

@ -624,8 +624,7 @@ fsp_space_modify_check(
case MTR_LOG_NO_REDO:
ut_ad(space->purpose == FIL_TYPE_TEMPORARY
|| space->purpose == FIL_TYPE_IMPORT
|| my_atomic_loadlint(&space->redo_skipped_count)
|| srv_is_tablespace_truncated(space->id));
|| my_atomic_loadlint(&space->redo_skipped_count));
return;
case MTR_LOG_ALL:
/* We may only write redo log for a persistent tablespace. */
@ -1065,13 +1064,6 @@ fsp_fill_free_list(
mtr_start(&ibuf_mtr);
ibuf_mtr.set_named_space(space);
/* Avoid logging while truncate table
fix-up is active. */
if (srv_is_tablespace_truncated(space->id)) {
mtr_set_log_mode(
&ibuf_mtr, MTR_LOG_NO_REDO);
}
const page_id_t page_id(
space->id,
i + FSP_IBUF_BITMAP_OFFSET);

View file

@ -914,7 +914,7 @@ rtr_split_page_move_rec_list(
mtr_set_log_mode(mtr, log_mode);
if (!page_zip_compress(new_page_zip, new_page, index,
page_zip_level, NULL, mtr)) {
page_zip_level, mtr)) {
ulint ret_pos;
/* Before trying to reorganize the page,

View file

@ -100,7 +100,6 @@ this program; if not, write to the Free Software Foundation, Inc.,
#include "row0mysql.h"
#include "row0quiesce.h"
#include "row0sel.h"
#include "row0trunc.h"
#include "row0upd.h"
#include "fil0crypt.h"
#include "ut0timer.h"

View file

@ -916,10 +916,7 @@ ibuf_set_free_bits_func(
ut_ad(0);
break;
case FIL_TYPE_TABLESPACE:
/* Avoid logging while fixing up truncate of table. */
if (!srv_is_tablespace_truncated(block->page.id.space())) {
break;
}
break;
/* fall through */
case FIL_TYPE_TEMPORARY:
case FIL_TYPE_IMPORT:

View file

@ -361,19 +361,16 @@ btr_node_ptr_get_child_page_no(
@param[in] type type of the index
@param[in,out] space tablespace where created
@param[in] index_id index id
@param[in] index index, or NULL when applying TRUNCATE
log record during recovery
@param[in] btr_redo_create_info used for applying TRUNCATE log
@param[in] mtr mini-transaction handle
record during recovery
@return page number of the created root, FIL_NULL if did not succeed */
@param[in] index index
@param[in,out] mtr mini-transaction
@return page number of the created root
@retval FIL_NULL if did not succeed */
ulint
btr_create(
ulint type,
fil_space_t* space,
index_id_t index_id,
dict_index_t* index,
const btr_create_t* btr_redo_create_info,
mtr_t* mtr);
/** Free a persistent index tree if it exists.

View file

@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, 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
@ -58,35 +59,4 @@ in the index record. */
#define BTR_EXTERN_LOCAL_STORED_MAX_SIZE \
(BTR_EXTERN_FIELD_REF_SIZE * 2)
/** The information is used for creating a new index tree when
applying TRUNCATE log record during recovery */
struct btr_create_t {
explicit btr_create_t(const byte* const ptr)
:
format_flags(),
n_fields(),
field_len(),
fields(ptr),
trx_id_pos(ULINT_UNDEFINED)
{
/* Do nothing */
}
/** Page format */
ulint format_flags;
/** Numbr of index fields */
ulint n_fields;
/** The length of the encoded meta-data */
ulint field_len;
/** Field meta-data, encoded. */
const byte* const fields;
/** Position of trx-id column. */
ulint trx_id_pos;
};
#endif

View file

@ -135,8 +135,6 @@ enum dberr_t {
DB_FTS_TOO_MANY_WORDS_IN_PHRASE,
/*< Too many words in a phrase */
DB_TABLESPACE_TRUNCATED, /*!< tablespace was truncated */
DB_DECRYPTION_FAILED, /* Tablespace encrypted and
decrypt operation failed because
of missing key management plugin,

View file

@ -40,7 +40,6 @@ extern my_bool srv_use_doublewrite_buf;
extern struct buf_dblwr_t* buf_dblwr;
struct trx_t;
class page_id_t;
class truncate_t;
/** Structure containing encryption specification */
struct fil_space_crypt_t;
@ -1086,7 +1085,7 @@ fil_space_extend(
@param[in] message message for aio handler if non-sync aio
used, else ignored
@param[in] ignore_missing_space true=ignore missing space during read
@return DB_SUCCESS, DB_TABLESPACE_DELETED or DB_TABLESPACE_TRUNCATED
@return DB_SUCCESS, or DB_TABLESPACE_DELETED
if we are trying to do i/o on a tablespace which does not exist */
dberr_t
fil_io(

View file

@ -145,10 +145,6 @@ corresponding to MLOG_INDEX_LOAD.
*/
extern void (*log_optimized_ddl_op)(ulint space_id);
/** Report backup-unfriendly TRUNCATE operation (with separate log file),
corresponding to MLOG_TRUNCATE. */
extern void (*log_truncate)();
/** Report an operation to create, delete, or rename a file during backup.
@param[in] space_id tablespace identifier
@param[in] flags tablespace flags (NULL if not create)

View file

@ -216,7 +216,8 @@ enum mlog_id_t {
/** initialize a file page */
MLOG_INIT_FILE_PAGE2 = 59,
/** Table is being truncated. (Marked only for file-per-table) */
/** Table is being truncated. (Was used in 10.2 and 10.3;
not supported for crash-upgrade to 10.4 or later.) */
MLOG_TRUNCATE = 60,
/** notify that an index tree is being loaded without writing

View file

@ -1061,10 +1061,6 @@ page_create_zip(
ulint level, /*!< in: the B-tree level of
the page */
trx_id_t max_trx_id, /*!< in: PAGE_MAX_TRX_ID */
const redo_page_compress_t* page_comp_info,
/*!< in: used for applying
TRUNCATE log
record during recovery */
mtr_t* mtr); /*!< in/out: mini-transaction
handle */
/**********************************************************//**

View file

@ -85,18 +85,6 @@ enum page_cur_mode_t {
PAGE_CUR_RTREE_GET_FATHER = 14
};
/** The information used for compressing a page when applying
TRUNCATE log record during recovery */
struct redo_page_compress_t {
ulint type; /*!< index type */
index_id_t index_id; /*!< index id */
ulint n_fields; /*!< number of index fields */
ulint field_len; /*!< the length of index field */
const byte* fields; /*!< index field information */
ulint trx_id_pos; /*!< position of trx-id column. */
};
/** Compressed page descriptor */
struct page_zip_des_t
{

View file

@ -2,7 +2,7 @@
Copyright (c) 2005, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
Copyright (c) 2017, MariaDB Corporation.
Copyright (c) 2017, 2018, 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
@ -164,10 +164,6 @@ page_zip_compress(
dict_index_t* index, /*!< in: index of the B-tree
node */
ulint level, /*!< in: commpression level */
const redo_page_compress_t* page_comp_info,
/*!< in: used for applying
TRUNCATE log
record during recovery */
mtr_t* mtr); /*!< in/out: mini-transaction,
or NULL */

View file

@ -414,7 +414,7 @@ page_zip_parse_compress_no_data(
was successful. Crash in this case. */
if (page
&& !page_zip_compress(page_zip, page, index, level, NULL, NULL)) {
&& !page_zip_compress(page_zip, page, index, level, NULL)) {
ut_error;
}

View file

@ -1,417 +0,0 @@
/*****************************************************************************
Copyright (c) 2013, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, 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
Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
*****************************************************************************/
/**************************************************//**
@file include/row0trunc.h
TRUNCATE implementation
Created 2013-04-25 Krunal Bauskar
*******************************************************/
#ifndef row0trunc_h
#define row0trunc_h
#include "row0mysql.h"
#include "dict0boot.h"
#include "fil0fil.h"
#include "srv0start.h"
#include "ut0new.h"
#include <vector>
/** The information of TRUNCATE log record.
This class handles the recovery stage of TRUNCATE table. */
class truncate_t {
public:
/**
Constructor
@param old_table_id old table id assigned to table before truncate
@param new_table_id new table id that will be assigned to table
after truncate
@param dir_path directory path */
truncate_t(
table_id_t old_table_id,
table_id_t new_table_id,
const char* dir_path);
/**
Constructor
@param log_file_name parse the log file during recovery to populate
information related to table to truncate */
truncate_t(const char* log_file_name);
/**
Consturctor
@param space_id space in which table reisde
@param name table name
@param tablespace_flags tablespace flags use for recreating tablespace
@param log_flags page format flag
@param recv_lsn lsn of redo log record. */
truncate_t(
ulint space_id,
const char* name,
ulint tablespace_flags,
ulint log_flags,
lsn_t recv_lsn);
/** Destructor */
~truncate_t();
/** The index information of MLOG_FILE_TRUNCATE redo record */
struct index_t {
/* Default copy constructor and destructor should be OK. */
index_t();
/**
Set the truncate log values for a compressed table.
@return DB_CORRUPTION or error code */
dberr_t set(const dict_index_t* index);
typedef std::vector<byte, ut_allocator<byte> > fields_t;
/** Index id */
index_id_t m_id;
/** Index type */
ulint m_type;
/** Root Page Number */
ulint m_root_page_no;
/** New Root Page Number.
Note: This field is not persisted to TRUNCATE log but used
during truncate table fix-up for updating SYS_XXXX tables. */
ulint m_new_root_page_no;
/** Number of index fields */
ulint m_n_fields;
/** DATA_TRX_ID column position. */
ulint m_trx_id_pos;
/** Compressed table field meta data, encode by
page_zip_fields_encode. Empty for non-compressed tables.
Should be NUL terminated. */
fields_t m_fields;
};
/**
@return the directory path, can be NULL */
const char* get_dir_path() const
{
return(m_dir_path);
}
/**
Register index information
@param index index information logged as part of truncate log. */
void add(index_t& index)
{
m_indexes.push_back(index);
}
/**
Add table to truncate post recovery.
@param ptr table information need to complete truncate of table. */
static void add(truncate_t* ptr)
{
s_tables.push_back(ptr);
}
/**
Clear registered index vector */
void clear()
{
m_indexes.clear();
}
/**
@return old table id of the table to truncate */
table_id_t old_table_id() const
{
return(m_old_table_id);
}
/**
@return new table id of the table to truncate */
table_id_t new_table_id() const
{
return(m_new_table_id);
}
/**
Update root page number in SYS_XXXX tables.
@param trx transaction object
@param table_id table id for which information needs to
be updated.
@param reserve_dict_mutex if TRUE, acquire/release
dict_sys->mutex around call to pars_sql.
@param mark_index_corrupted if true, then mark index corrupted
@return DB_SUCCESS or error code */
dberr_t update_root_page_no(
trx_t* trx,
table_id_t table_id,
ibool reserve_dict_mutex,
bool mark_index_corrupted) const;
/** Create an index for a table.
@param[in] table_name table name, for which to create
the index
@param[in,out] space tablespace
@param[in] index_type type of index to truncate
@param[in] index_id id of index to truncate
@param[in] btr_redo_create_info control info for ::btr_create()
@param[in,out] mtr mini-transaction covering the
create index
@return root page no or FIL_NULL on failure */
inline ulint create_index(
const char* table_name,
fil_space_t* space,
ulint index_type,
index_id_t index_id,
const btr_create_t& btr_redo_create_info,
mtr_t* mtr) const;
/** Create the indexes for a table
@param[in] table_name table name, for which to create the
indexes
@param[in,out] space tablespace
@param[in] format_flags page format flags
@return DB_SUCCESS or error code. */
inline dberr_t create_indexes(
const char* table_name,
fil_space_t* space,
ulint format_flags);
/** Check if index has been modified since TRUNCATE log snapshot
was recorded.
@param[in] space tablespace
@param[in] root_page_no index root page number
@return true if modified else false */
inline bool is_index_modified_since_logged(
const fil_space_t* space,
ulint root_page_no) const;
/** Drop indexes for a table.
@param[in,out] space tablespace
@return DB_SUCCESS or error code. */
void drop_indexes(fil_space_t* space) const;
/**
Parses log record during recovery
@param start_ptr buffer containing log body to parse
@param end_ptr buffer end
@return DB_SUCCESS or error code */
dberr_t parse(
byte* start_ptr,
const byte* end_ptr);
/** Parse MLOG_TRUNCATE log record from REDO log file during recovery.
@param[in,out] start_ptr buffer containing log body to parse
@param[in] end_ptr buffer end
@param[in] space_id tablespace identifier
@return parsed upto or NULL. */
static byte* parse_redo_entry(
byte* start_ptr,
const byte* end_ptr,
ulint space_id);
/**
Write a log record for truncating a single-table tablespace.
@param start_ptr buffer to write log record
@param end_ptr buffer end
@param space_id space id
@param tablename the table name in the usual
databasename/tablename format of InnoDB
@param flags tablespace flags
@param format_flags page format
@param lsn lsn while logging */
dberr_t write(
byte* start_ptr,
byte* end_ptr,
ulint space_id,
const char* tablename,
ulint flags,
ulint format_flags,
lsn_t lsn) const;
/**
@return number of indexes parsed from the truncate log record */
size_t indexes() const;
/**
Truncate a single-table tablespace. The tablespace must be cached
in the memory cache.
Note: This is defined in fil0fil.cc because it needs to access some
types that are local to that file.
@param space_id space id
@param dir_path directory path
@param tablename the table name in the usual
databasename/tablename format of InnoDB
@param flags tablespace flags
@param default_size if true, truncate to default size if tablespace
is being newly re-initialized.
@return DB_SUCCESS or error */
static dberr_t truncate(
ulint space_id,
const char* dir_path,
const char* tablename,
ulint flags,
bool default_size);
/**
Fix the table truncate by applying information parsed from TRUNCATE log.
Fix-up includes re-creating table (drop and re-create indexes)
@return error code or DB_SUCCESS */
static dberr_t fixup_tables_in_system_tablespace();
/**
Fix the table truncate by applying information parsed from TRUNCATE log.
Fix-up includes re-creating tablespace.
@return error code or DB_SUCCESS */
static dberr_t fixup_tables_in_non_system_tablespace();
/**
Check whether a tablespace was truncated during recovery
@param space_id tablespace id to check
@return true if the tablespace was truncated */
static bool is_tablespace_truncated(ulint space_id);
/** Was tablespace truncated (on crash before checkpoint).
If the MLOG_TRUNCATE redo-record is still available then tablespace
was truncated and checkpoint is yet to happen.
@param[in] space_id tablespace id to check.
@return true if tablespace was truncated. */
static bool was_tablespace_truncated(ulint space_id);
/** Get the lsn associated with space.
@param[in] space_id tablespace id to check.
@return associated lsn. */
static lsn_t get_truncated_tablespace_init_lsn(ulint space_id);
private:
typedef std::vector<index_t, ut_allocator<index_t> > indexes_t;
/** Space ID of tablespace */
ulint m_space_id;
/** ID of table that is being truncated. */
table_id_t m_old_table_id;
/** New ID that will be assigned to table on truncation. */
table_id_t m_new_table_id;
/** Data dir path of tablespace */
char* m_dir_path;
/** Table name */
char* m_tablename;
/** Tablespace Flags */
ulint m_tablespace_flags;
/** Format flags (log flags; stored in page-no field of header) */
ulint m_format_flags;
/** Index meta-data */
indexes_t m_indexes;
/** LSN of TRUNCATE log record. */
lsn_t m_log_lsn;
/** Log file name. */
char* m_log_file_name;
/** Encryption information of the table */
fil_encryption_t m_encryption;
uint32_t m_key_id;
/** Vector of tables to truncate. */
typedef std::vector<truncate_t*, ut_allocator<truncate_t*> >
tables_t;
/** Information about tables to truncate post recovery */
static tables_t s_tables;
/** Information about truncated table
This is case when truncate is complete but checkpoint hasn't. */
typedef std::map<ulint, lsn_t> truncated_tables_t;
static truncated_tables_t s_truncated_tables;
public:
/** If true then fix-up of table is active and so while creating
index instead of grabbing information from dict_index_t, grab it
from parsed truncate log record. */
static bool s_fix_up_active;
};
/**
Parse truncate log file. */
class TruncateLogParser {
public:
/**
Scan and Parse truncate log files.
@param dir_path look for log directory in following path
@return DB_SUCCESS or error code. */
static dberr_t scan_and_parse(
const char* dir_path);
private:
typedef std::vector<char*, ut_allocator<char*> >
trunc_log_files_t;
private:
/**
Scan to find out truncate log file from the given directory path.
@param dir_path look for log directory in following path.
@param log_files cache to hold truncate log file name found.
@return DB_SUCCESS or error code. */
static dberr_t scan(
const char* dir_path,
trunc_log_files_t& log_files);
/**
Parse the log file and populate table to truncate information.
(Add this table to truncate information to central vector that is then
used by truncate fix-up routine to fix-up truncate action of the table.)
@param log_file_name log file to parse
@return DB_SUCCESS or error code. */
static dberr_t parse(
const char* log_file_name);
};
#endif /* row0trunc_h */

View file

@ -900,23 +900,6 @@ srv_purge_wakeup();
/** Shut down the purge threads. */
void srv_purge_shutdown();
/** Check if tablespace is being truncated.
(Ignore system-tablespace as we don't re-create the tablespace
and so some of the action that are suppressed by this function
for independent tablespace are not applicable to system-tablespace).
@param space_id space_id to check for truncate action
@return true if being truncated, false if not being
truncated or tablespace is system-tablespace. */
bool
srv_is_tablespace_truncated(ulint space_id);
/** Check if tablespace was truncated.
@param[in] space space object to check for truncate action
@return true if tablespace was truncated and we still have an active
MLOG_TRUNCATE REDO log record. */
bool
srv_was_tablespace_truncated(const fil_space_t* space);
#ifdef UNIV_DEBUG
/** Disables master thread. It's used by:
SET GLOBAL innodb_master_thread_disabled_debug = 1 (0).

View file

@ -133,202 +133,6 @@ private:
TrxUndoRsegs::const_iterator m_iter;
};
/* Namespace to hold all the related functions and variables need for truncate
of undo tablespace. */
namespace undo {
typedef std::vector<ulint> undo_spaces_t;
typedef std::vector<trx_rseg_t*> rseg_for_trunc_t;
/** Mark completion of undo truncate action by writing magic number to
the log file and then removing it from the disk.
If we are going to remove it from disk then why write magic number ?
This is to safeguard from unlink (file-system) anomalies that will keep
the link to the file even after unlink action is successfull and
ref-count = 0.
@param[in] space_id id of the undo tablespace to truncate.*/
void done(ulint space_id);
/** Check if TRUNCATE_DDL_LOG file exist.
@param[in] space_id id of the undo tablespace.
@return true if exist else false. */
bool is_log_present(ulint space_id);
/** Track UNDO tablespace mark for truncate. */
class Truncate {
public:
void create()
{
m_undo_for_trunc = ULINT_UNDEFINED;
m_scan_start = 1;
m_purge_rseg_truncate_frequency =
ulint(srv_purge_rseg_truncate_frequency);
}
/** Clear the cached rollback segment. Normally done
when purge is about to shutdown. */
void clear()
{
reset();
rseg_for_trunc_t temp;
m_rseg_for_trunc.swap(temp);
}
/** Is tablespace selected for truncate.
@return true if undo tablespace is marked for truncate */
bool is_marked() const
{
return(!(m_undo_for_trunc == ULINT_UNDEFINED));
}
/** Mark the tablespace for truncate.
@param[in] undo_id tablespace for truncate. */
void mark(ulint undo_id)
{
m_undo_for_trunc = undo_id;
m_scan_start = (undo_id + 1)
% (srv_undo_tablespaces_active + 1);
if (m_scan_start == 0) {
/* Note: UNDO tablespace ids starts from 1. */
m_scan_start = 1;
}
/* We found an UNDO-tablespace to truncate so set the
local purge rseg truncate frequency to 1. This will help
accelerate the purge action and in turn truncate. */
m_purge_rseg_truncate_frequency = 1;
}
/** Get the tablespace marked for truncate.
@return tablespace id marked for truncate. */
ulint get_marked_space_id() const
{
return(m_undo_for_trunc);
}
/** Add rseg to truncate vector.
@param[in,out] rseg rseg for truncate */
void add_rseg_to_trunc(trx_rseg_t* rseg)
{
m_rseg_for_trunc.push_back(rseg);
}
/** Get number of rsegs registered for truncate.
@return return number of rseg that belongs to tablespace mark
for truncate. */
ulint rsegs_size() const
{
return(m_rseg_for_trunc.size());
}
/** Get ith registered rseg.
@param[in] id index of rseg to get.
@return reference to registered rseg. */
trx_rseg_t* get_ith_rseg(ulint id)
{
ut_ad(id < m_rseg_for_trunc.size());
return(m_rseg_for_trunc.at(id));
}
/** Reset for next rseg truncate. */
void reset()
{
m_undo_for_trunc = ULINT_UNDEFINED;
m_rseg_for_trunc.clear();
/* Sync with global value as we are done with
truncate now. */
m_purge_rseg_truncate_frequency = static_cast<ulint>(
srv_purge_rseg_truncate_frequency);
}
/** Get the tablespace id to start scanning from.
@return id of UNDO tablespace to start scanning from. */
ulint get_scan_start() const
{
return(m_scan_start);
}
/** Check if the tablespace needs fix-up (based on presence of
DDL truncate log)
@param space_id space id of the undo tablespace to check
@return true if fix up is needed else false */
bool needs_fix_up(ulint space_id) const
{
return(is_log_present(space_id));
}
/** Add undo tablespace to truncate vector.
@param[in] space_id space id of tablespace to
truncate */
static void add_space_to_trunc_list(ulint space_id)
{
s_spaces_to_truncate.push_back(space_id);
}
/** Clear the truncate vector. */
static void clear_trunc_list()
{
s_spaces_to_truncate.clear();
}
/** Is tablespace marked for truncate.
@param[in] space_id space id to check
@return true if marked for truncate, else false. */
static bool is_tablespace_truncated(ulint space_id)
{
return(std::find(s_spaces_to_truncate.begin(),
s_spaces_to_truncate.end(), space_id)
!= s_spaces_to_truncate.end());
}
/** Was a tablespace truncated at startup
@param[in] space_id space id to check
@return whether space_id was truncated at startup */
static bool was_tablespace_truncated(ulint space_id)
{
return(std::find(s_fix_up_spaces.begin(),
s_fix_up_spaces.end(),
space_id)
!= s_fix_up_spaces.end());
}
/** Get local rseg purge truncate frequency
@return rseg purge truncate frequency. */
ulint get_rseg_truncate_frequency() const
{
return(m_purge_rseg_truncate_frequency);
}
private:
/** UNDO tablespace is mark for truncate. */
ulint m_undo_for_trunc;
/** rseg that resides in UNDO tablespace is marked for
truncate. */
rseg_for_trunc_t m_rseg_for_trunc;
/** Start scanning for UNDO tablespace from this space_id.
This is to avoid bias selection of one tablespace always. */
ulint m_scan_start;
/** Rollback segment(s) purge frequency. This is local
value maintained along with global value. It is set to global
value on start but when tablespace is marked for truncate it
is updated to 1 and then minimum value among 2 is used by
purge action. */
ulint m_purge_rseg_truncate_frequency;
/** List of UNDO tablespace(s) to truncate. */
static undo_spaces_t s_spaces_to_truncate;
public:
/** Undo tablespaces that were truncated at startup */
static undo_spaces_t s_fix_up_spaces;
}; /* class Truncate */
}; /* namespace undo */
/** The control structure used in the purge operation */
class purge_sys_t
{
@ -410,9 +214,14 @@ public:
by the pq_mutex */
PQMutex pq_mutex; /*!< Mutex protecting purge_queue */
undo::Truncate undo_trunc; /*!< Track UNDO tablespace marked
for truncate. */
/** Undo tablespace file truncation (only accessed by the
srv_purge_coordinator_thread) */
struct {
/** The undo tablespace that is currently being truncated */
fil_space_t* current;
/** The undo tablespace that was last truncated */
fil_space_t* last;
} truncate;
/**
Constructor.

View file

@ -54,7 +54,6 @@ Created 9/20/1997 Heikki Tuuri
#include "fil0fil.h"
#include "fsp0sysspace.h"
#include "ut0new.h"
#include "row0trunc.h"
#include "buf0rea.h"
#include "srv0srv.h"
#include "srv0start.h"
@ -203,10 +202,6 @@ corresponding to MLOG_INDEX_LOAD.
*/
void (*log_optimized_ddl_op)(ulint space_id);
/** Report backup-unfriendly TRUNCATE operation (with separate log file),
corresponding to MLOG_TRUNCATE. */
void (*log_truncate)();
/** Report an operation to create, delete, or rename a file during backup.
@param[in] space_id tablespace identifier
@param[in] flags tablespace flags (NULL if not create)
@ -1205,14 +1200,10 @@ recv_parse_or_apply_log_rec_body(
}
return(ptr + 8);
case MLOG_TRUNCATE:
if (log_truncate) {
ut_ad(srv_operation != SRV_OPERATION_NORMAL);
log_truncate();
recv_sys->found_corrupt_fs = true;
return NULL;
}
return(truncate_t::parse_redo_entry(ptr, end_ptr, space_id));
ib::error() << "Cannot crash-upgrade from "
"old-style TRUNCATE TABLE";
recv_sys->found_corrupt_log = true;
return NULL;
default:
break;
}
@ -1795,13 +1786,10 @@ recv_recover_page(bool just_read_in, buf_block_t* block)
page_t* page;
page_zip_des_t* page_zip;
recv_addr_t* recv_addr;
recv_t* recv;
byte* buf;
lsn_t start_lsn;
lsn_t end_lsn;
lsn_t page_lsn;
lsn_t page_newest_lsn;
ibool modification_to_page;
mtr_t mtr;
mutex_enter(&(recv_sys->mutex));
@ -1876,57 +1864,19 @@ recv_recover_page(bool just_read_in, buf_block_t* block)
page_lsn = page_newest_lsn;
}
modification_to_page = FALSE;
start_lsn = end_lsn = 0;
recv = UT_LIST_GET_FIRST(recv_addr->rec_list);
fil_space_t* space = fil_space_acquire(block->page.id.space());
while (recv) {
for (recv_t* recv = UT_LIST_GET_FIRST(recv_addr->rec_list);
recv; recv = UT_LIST_GET_NEXT(rec_list, recv)) {
end_lsn = recv->end_lsn;
ut_ad(end_lsn <= log_sys.log.scanned_lsn);
if (recv->len > RECV_DATA_BLOCK_SIZE) {
/* We have to copy the record body to a separate
buffer */
buf = static_cast<byte*>(ut_malloc_nokey(recv->len));
recv_data_copy_to_buf(buf, recv);
} else {
buf = ((byte*)(recv->data)) + sizeof(recv_data_t);
}
/* If per-table tablespace was truncated and there exist REDO
records before truncate that are to be applied as part of
recovery (checkpoint didn't happen since truncate was done)
skip such records using lsn check as they may not stand valid
post truncate.
LSN at start of truncate is recorded and any redo record
with LSN less than recorded LSN is skipped.
Note: We can't skip complete recv_addr as same page may have
valid REDO records post truncate those needs to be applied. */
/* Ignore applying the redo logs for tablespace that is
truncated. Post recovery there is fixup action that will
restore the tablespace back to normal state.
Applying redo at this stage can result in error given that
redo will have action recorded on page before tablespace
was re-inited and that would lead to an error while applying
such action. */
if (recv->start_lsn >= page_lsn
&& !srv_is_tablespace_truncated(space->id)
&& !(srv_was_tablespace_truncated(space)
&& recv->start_lsn
< truncate_t::get_truncated_tablespace_init_lsn(
space->id))) {
lsn_t end_lsn;
if (!modification_to_page) {
modification_to_page = TRUE;
ut_ad(recv->start_lsn);
if (recv->start_lsn >= page_lsn) {
if (!start_lsn) {
start_lsn = recv->start_lsn;
}
@ -1942,29 +1892,41 @@ recv_recover_page(bool just_read_in, buf_block_t* block)
<< " len " << recv->len
<< " page " << block->page.id);
byte* buf;
if (recv->len > RECV_DATA_BLOCK_SIZE) {
/* We have to copy the record body to
a separate buffer */
buf = static_cast<byte*>(ut_malloc_nokey(
recv->len));
recv_data_copy_to_buf(buf, recv);
} else {
buf = reinterpret_cast<byte*>(recv->data)
+ sizeof *recv->data;
}
recv_parse_or_apply_log_rec_body(
recv->type, buf, buf + recv->len,
block->page.id.space(),
block->page.id.page_no(),
true, block, &mtr);
block->page.id.page_no(), true, block, &mtr);
end_lsn = recv->start_lsn + recv->len;
lsn_t end_lsn = recv->start_lsn + recv->len;
mach_write_to_8(FIL_PAGE_LSN + page, end_lsn);
mach_write_to_8(srv_page_size
- FIL_PAGE_END_LSN_OLD_CHKSUM
+ page, end_lsn);
if (page_zip) {
mach_write_to_8(FIL_PAGE_LSN
+ page_zip->data, end_lsn);
mach_write_to_8(FIL_PAGE_LSN + page_zip->data,
end_lsn);
}
if (recv->len > RECV_DATA_BLOCK_SIZE) {
ut_free(buf);
}
}
if (recv->len > RECV_DATA_BLOCK_SIZE) {
ut_free(buf);
}
recv = UT_LIST_GET_NEXT(rec_list, recv);
}
space->release();
@ -1978,9 +1940,7 @@ recv_recover_page(bool just_read_in, buf_block_t* block)
}
#endif /* UNIV_ZIP_DEBUG */
if (modification_to_page) {
ut_a(block);
if (start_lsn) {
log_flush_order_mutex_enter();
buf_flush_recv_note_modification(block, start_lsn, end_lsn);
log_flush_order_mutex_exit();
@ -2095,6 +2055,17 @@ recv_apply_hashed_log_recs(bool last_batch)
ut_d(recv_no_log_write = recv_no_ibuf_operations);
if (ulint n = recv_sys->n_addrs) {
if (!log_sys.log.subformat && !srv_force_recovery
&& srv_undo_tablespaces_open) {
ib::error() << "Recovery of separately logged"
" TRUNCATE operations is no longer supported."
" Set innodb_force_recovery=1"
" if no *trunc.log files exist";
recv_sys->found_corrupt_log = true;
mutex_exit(&recv_sys->mutex);
return;
}
const char* msg = last_batch
? "Starting final batch to recover "
: "Starting a batch to recover ";
@ -2120,15 +2091,6 @@ recv_apply_hashed_log_recs(bool last_batch)
recv_addr = static_cast<recv_addr_t*>(
HASH_GET_NEXT(addr_hash, recv_addr))) {
if (srv_is_tablespace_truncated(recv_addr->space)) {
/* Avoid applying REDO log for the tablespace
that is schedule for TRUNCATE. */
ut_a(recv_sys->n_addrs);
recv_addr->state = RECV_DISCARDED;
recv_sys->n_addrs--;
continue;
}
if (recv_addr->state == RECV_DISCARDED) {
ut_a(recv_sys->n_addrs);
recv_sys->n_addrs--;

View file

@ -32,7 +32,6 @@ Created 11/26/1995 Heikki Tuuri
#include "page0types.h"
#include "mtr0log.h"
#include "log0log.h"
#include "row0trunc.h"
#include "log0recv.h"
@ -695,8 +694,7 @@ mtr_t::x_lock_space(ulint space_id, const char* file, unsigned line)
ut_ad(get_log_mode() != MTR_LOG_NO_REDO
|| space->purpose == FIL_TYPE_TEMPORARY
|| space->purpose == FIL_TYPE_IMPORT
|| my_atomic_loadlint(&space->redo_skipped_count) > 0
|| srv_is_tablespace_truncated(space->id));
|| my_atomic_loadlint(&space->redo_skipped_count) > 0);
}
ut_ad(space);

View file

@ -1566,7 +1566,7 @@ page_cur_insert_rec_zip(
get rid of the modification log. */
page_create_zip(page_cur_get_block(cursor), index,
page_header_get_field(page, PAGE_LEVEL),
0, NULL, mtr);
0, mtr);
ut_ad(!page_header_get_ptr(page, PAGE_FREE));
if (page_zip_available(
@ -1641,7 +1641,7 @@ page_cur_insert_rec_zip(
if (!log_compressed) {
if (page_zip_compress(
page_zip, page, index,
level, NULL, NULL)) {
level, NULL)) {
page_cur_insert_rec_write_log(
insert_rec, rec_size,
cursor->rec, index, mtr);

View file

@ -30,7 +30,6 @@ Created 2/2/1994 Heikki Tuuri
#include "page0zip.h"
#include "buf0buf.h"
#include "btr0btr.h"
#include "row0trunc.h"
#include "srv0srv.h"
#include "lock0lock.h"
#include "fut0lst.h"
@ -454,22 +453,15 @@ page_create_zip(
ulint level, /*!< in: the B-tree level
of the page */
trx_id_t max_trx_id, /*!< in: PAGE_MAX_TRX_ID */
const redo_page_compress_t* page_comp_info,
/*!< in: used for applying
TRUNCATE log
record during recovery */
mtr_t* mtr) /*!< in/out: mini-transaction
handle */
{
page_t* page;
page_zip_des_t* page_zip = buf_block_get_page_zip(block);
bool is_spatial;
ut_ad(block);
ut_ad(page_zip);
ut_ad(index == NULL || dict_table_is_comp(index->table));
is_spatial = index ? dict_index_is_spatial(index)
: page_comp_info->type & DICT_SPATIAL;
ut_ad(dict_table_is_comp(index->table));
/* PAGE_MAX_TRX_ID or PAGE_ROOT_AUTO_INC are always 0 for
temporary tables. */
@ -487,22 +479,11 @@ page_create_zip(
|| !dict_index_is_sec_or_ibuf(index)
|| index->table->is_temporary());
page = page_create_low(block, TRUE, is_spatial);
page = page_create_low(block, TRUE, dict_index_is_spatial(index));
mach_write_to_2(PAGE_HEADER + PAGE_LEVEL + page, level);
mach_write_to_8(PAGE_HEADER + PAGE_MAX_TRX_ID + page, max_trx_id);
if (truncate_t::s_fix_up_active) {
/* Compress the index page created when applying
TRUNCATE log during recovery */
if (!page_zip_compress(page_zip, page, index, page_zip_level,
page_comp_info, NULL)) {
/* The compression of a newly created
page should always succeed. */
ut_error;
}
} else if (!page_zip_compress(page_zip, page, index,
page_zip_level, NULL, mtr)) {
if (!page_zip_compress(page_zip, page, index, page_zip_level, mtr)) {
/* The compression of a newly created
page should always succeed. */
ut_error;
@ -546,7 +527,7 @@ page_create_empty(
ut_ad(!index->table->is_temporary());
page_create_zip(block, index,
page_header_get_field(page, PAGE_LEVEL),
max_trx_id, NULL, mtr);
max_trx_id, mtr);
} else {
page_create(block, mtr, page_is_comp(page),
dict_index_is_spatial(index));
@ -721,11 +702,8 @@ page_copy_rec_list_end(
if (new_page_zip) {
mtr_set_log_mode(mtr, log_mode);
if (!page_zip_compress(new_page_zip,
new_page,
index,
page_zip_level,
NULL, mtr)) {
if (!page_zip_compress(new_page_zip, new_page, index,
page_zip_level, mtr)) {
/* Before trying to reorganize the page,
store the number of preceding records on the page. */
ulint ret_pos
@ -887,7 +865,7 @@ page_copy_rec_list_start(
goto zip_reorganize;);
if (!page_zip_compress(new_page_zip, new_page, index,
page_zip_level, NULL, mtr)) {
page_zip_level, mtr)) {
ulint ret_pos;
#ifndef DBUG_OFF
zip_reorganize:

View file

@ -46,7 +46,6 @@ const byte field_ref_zero[FIELD_REF_SIZE] = {
#include "page0types.h"
#include "log0recv.h"
#include "row0row.h"
#include "row0trunc.h"
#include "zlib.h"
#include "buf0buf.h"
#include "buf0types.h"
@ -1248,17 +1247,11 @@ page_zip_compress(
dict_index_t* index, /*!< in: index of the B-tree
node */
ulint level, /*!< in: commpression level */
const redo_page_compress_t* page_comp_info,
/*!< in: used for applying
TRUNCATE log
record during recovery */
mtr_t* mtr) /*!< in/out: mini-transaction,
or NULL */
{
z_stream c_stream;
int err;
ulint n_fields; /* number of index fields
needed */
byte* fields; /*!< index field information */
byte* buf; /*!< compressed payload of the
page */
@ -1273,7 +1266,6 @@ page_zip_compress(
ulint n_blobs = 0;
byte* storage; /* storage of uncompressed
columns */
index_id_t ind_id;
uintmax_t usec = ut_time_us(NULL);
#ifdef PAGE_ZIP_COMPRESS_DBG
FILE* logfile = NULL;
@ -1288,10 +1280,8 @@ page_zip_compress(
ut_a(fil_page_index_page_check(page));
ut_ad(page_simple_validate_new((page_t*) page));
ut_ad(page_zip_simple_validate(page_zip));
ut_ad(!index
|| (index
&& dict_table_is_comp(index->table)
&& !dict_index_is_ibuf(index)));
ut_ad(dict_table_is_comp(index->table));
ut_ad(!dict_index_is_ibuf(index));
UNIV_MEM_ASSERT_RW(page, srv_page_size);
@ -1311,18 +1301,10 @@ page_zip_compress(
== PAGE_NEW_SUPREMUM);
}
if (truncate_t::s_fix_up_active) {
ut_ad(page_comp_info != NULL);
n_fields = page_comp_info->n_fields;
ind_id = page_comp_info->index_id;
} else {
if (page_is_leaf(page)) {
n_fields = dict_index_get_n_fields(index);
} else {
n_fields = dict_index_get_n_unique_in_tree_nonleaf(index);
}
ind_id = index->id;
}
const ulint n_fields = page_is_leaf(page)
? dict_index_get_n_fields(index)
: dict_index_get_n_unique_in_tree_nonleaf(index);
index_id_t ind_id = index->id;
/* The dense directory excludes the infimum and supremum records. */
n_dense = ulint(page_dir_get_n_heap(page)) - PAGE_HEAP_NO_USER_LOW;
@ -1433,20 +1415,11 @@ page_zip_compress(
/* Dense page directory and uncompressed columns, if any */
if (page_is_leaf(page)) {
if ((index && dict_index_is_clust(index))
|| (page_comp_info
&& (page_comp_info->type & DICT_CLUSTERED))) {
if (index) {
trx_id_col = dict_index_get_sys_col_pos(
index, DATA_TRX_ID);
ut_ad(trx_id_col > 0);
ut_ad(trx_id_col != ULINT_UNDEFINED);
} else if (page_comp_info
&& (page_comp_info->type
& DICT_CLUSTERED)) {
trx_id_col = page_comp_info->trx_id_pos;
}
if (dict_index_is_clust(index)) {
trx_id_col = dict_index_get_sys_col_pos(
index, DATA_TRX_ID);
ut_ad(trx_id_col > 0);
ut_ad(trx_id_col != ULINT_UNDEFINED);
slot_size = PAGE_ZIP_DIR_SLOT_SIZE
+ DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN;
@ -1454,10 +1427,8 @@ page_zip_compress(
} else {
/* Signal the absence of trx_id
in page_zip_fields_encode() */
if (index) {
ut_ad(dict_index_get_sys_col_pos(
index, DATA_TRX_ID) == ULINT_UNDEFINED);
}
ut_ad(dict_index_get_sys_col_pos(
index, DATA_TRX_ID) == ULINT_UNDEFINED);
trx_id_col = 0;
slot_size = PAGE_ZIP_DIR_SLOT_SIZE;
}
@ -1471,19 +1442,9 @@ page_zip_compress(
goto zlib_error;
}
c_stream.avail_out -= static_cast<uInt>(n_dense * slot_size);
if (truncate_t::s_fix_up_active) {
ut_ad(page_comp_info != NULL);
c_stream.avail_in = static_cast<uInt>(
page_comp_info->field_len);
for (ulint i = 0; i < page_comp_info->field_len; i++) {
fields[i] = page_comp_info->fields[i];
}
} else {
c_stream.avail_in = static_cast<uInt>(
page_zip_fields_encode(
n_fields, index, trx_id_col, fields));
}
c_stream.avail_out -= uInt(n_dense * slot_size);
c_stream.avail_in = uInt(page_zip_fields_encode(n_fields, index,
trx_id_col, fields));
c_stream.next_in = fields;
if (UNIV_LIKELY(!trx_id_col)) {
@ -1637,7 +1598,7 @@ err_exit:
mutex_exit(&page_zip_stat_per_index_mutex);
}
if (page_is_leaf(page) && !truncate_t::s_fix_up_active) {
if (page_is_leaf(page)) {
dict_index_zip_success(index);
}
@ -4807,9 +4768,7 @@ page_zip_reorganize(
/* Restore logging. */
mtr_set_log_mode(mtr, log_mode);
if (!page_zip_compress(page_zip, page, index,
page_zip_level, NULL, mtr)) {
if (!page_zip_compress(page_zip, page, index, page_zip_level, mtr)) {
buf_block_free(temp_block);
return(FALSE);
}

File diff suppressed because it is too large Load diff

View file

@ -62,7 +62,6 @@ Created 10/8/1995 Heikki Tuuri
#include "pars0pars.h"
#include "que0que.h"
#include "row0mysql.h"
#include "row0trunc.h"
#include "row0log.h"
#include "srv0mon.h"
#include "srv0srv.h"
@ -2585,16 +2584,10 @@ srv_do_purge(ulint* n_total_purged)
break;
}
ulint undo_trunc_freq =
purge_sys.undo_trunc.get_rseg_truncate_frequency();
ulint rseg_truncate_frequency = ut_min(
static_cast<ulint>(srv_purge_rseg_truncate_frequency),
undo_trunc_freq);
n_pages_purged = trx_purge(
n_use_threads,
(++count % rseg_truncate_frequency) == 0);
!(++count % srv_purge_rseg_truncate_frequency)
|| purge_sys.truncate.current);
*n_total_purged += n_pages_purged;
} while (n_pages_purged > 0 && !purge_sys.paused()
@ -2729,11 +2722,6 @@ DECLARE_THREAD(srv_purge_coordinator_thread)(
/* Note that we are shutting down. */
rw_lock_x_lock(&purge_sys.latch);
purge_sys.coordinator_shutdown();
/* If there are any pending undo-tablespace truncate then clear
it off as we plan to shutdown the purge thread. */
purge_sys.undo_trunc.clear();
/* Ensure that the wait in purge_sys_t::stop() will terminate. */
os_event_set(purge_sys.event);
@ -2840,38 +2828,3 @@ void srv_purge_shutdown()
srv_purge_wakeup();
} while (srv_sys.sys_threads[SRV_PURGE_SLOT].in_use);
}
/** Check if tablespace is being truncated.
(Ignore system-tablespace as we don't re-create the tablespace
and so some of the action that are suppressed by this function
for independent tablespace are not applicable to system-tablespace).
@param space_id space_id to check for truncate action
@return true if being truncated, false if not being
truncated or tablespace is system-tablespace. */
bool
srv_is_tablespace_truncated(ulint space_id)
{
if (is_system_tablespace(space_id)) {
return(false);
}
return(truncate_t::is_tablespace_truncated(space_id)
|| undo::Truncate::is_tablespace_truncated(space_id));
}
/** Check if tablespace was truncated.
@param[in] space space object to check for truncate action
@return true if tablespace was truncated and we still have an active
MLOG_TRUNCATE REDO log record. */
bool
srv_was_tablespace_truncated(const fil_space_t* space)
{
if (space == NULL) {
ut_ad(0);
return(false);
}
return (!is_system_tablespace(space->id)
&& truncate_t::was_tablespace_truncated(space->id));
}

View file

@ -77,7 +77,6 @@ Created 2/16/1996 Heikki Tuuri
#include "srv0srv.h"
#include "btr0defragment.h"
#include "fsp0sysspace.h"
#include "row0trunc.h"
#include "mysql/service_wsrep.h" /* wsrep_recovery */
#include "trx0rseg.h"
#include "os0proc.h"
@ -100,7 +99,6 @@ Created 2/16/1996 Heikki Tuuri
#include "row0upd.h"
#include "row0row.h"
#include "row0mysql.h"
#include "row0trunc.h"
#include "btr0pcur.h"
#include "os0event.h"
#include "zlib.h"
@ -815,8 +813,6 @@ srv_check_undo_redo_logs_exists()
return(DB_SUCCESS);
}
undo::undo_spaces_t undo::Truncate::s_fix_up_spaces;
/** Open the configured number of dedicated undo tablespaces.
@param[in] create_new_db whether the database is being initialized
@return DB_SUCCESS or error code */
@ -898,46 +894,8 @@ srv_undo_tablespaces_init(bool create_new_db)
prev_space_id = srv_undo_space_id_start - 1;
break;
case SRV_OPERATION_NORMAL:
if (create_new_db) {
break;
}
/* fall through */
case SRV_OPERATION_RESTORE:
case SRV_OPERATION_RESTORE_EXPORT:
ut_ad(!create_new_db);
/* Check if any of the UNDO tablespace needs fix-up because
server crashed while truncate was active on UNDO tablespace.*/
for (i = 0; i < n_undo_tablespaces; ++i) {
undo::Truncate undo_trunc;
if (undo_trunc.needs_fix_up(undo_tablespace_ids[i])) {
char name[OS_FILE_MAX_PATH];
snprintf(name, sizeof(name),
"%s%cundo%03zu",
srv_undo_dir, OS_PATH_SEPARATOR,
undo_tablespace_ids[i]);
os_file_delete(innodb_data_file_key, name);
err = srv_undo_tablespace_create(
name,
SRV_UNDO_TABLESPACE_SIZE_IN_PAGES);
if (err != DB_SUCCESS) {
ib::error() << "Could not fix-up undo "
" tablespace truncate '"
<< name << "'.";
return(err);
}
undo::Truncate::s_fix_up_spaces.push_back(
undo_tablespace_ids[i]);
}
}
break;
}
@ -1044,64 +1002,6 @@ srv_undo_tablespaces_init(bool create_new_db)
}
}
if (!undo::Truncate::s_fix_up_spaces.empty()) {
/* Step-1: Initialize the tablespace header and rsegs header. */
mtr_t mtr;
mtr_start(&mtr);
/* Turn off REDO logging. We are in server start mode and fixing
UNDO tablespace even before REDO log is read. Let's say we
do REDO logging here then this REDO log record will be applied
as part of the current recovery process. We surely don't need
that as this is fix-up action parallel to REDO logging. */
mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO);
buf_block_t* sys_header = trx_sysf_get(&mtr);
if (!sys_header) {
mtr.commit();
return DB_CORRUPTION;
}
for (undo::undo_spaces_t::const_iterator it
= undo::Truncate::s_fix_up_spaces.begin();
it != undo::Truncate::s_fix_up_spaces.end();
++it) {
undo::Truncate::add_space_to_trunc_list(*it);
fil_space_t* space = fil_space_get(*it);
fsp_header_init(space,
SRV_UNDO_TABLESPACE_SIZE_IN_PAGES,
&mtr);
for (ulint i = 0; i < TRX_SYS_N_RSEGS; i++) {
if (trx_sysf_rseg_get_space(sys_header, i)
== *it) {
trx_rseg_header_create(
space, i, sys_header, &mtr);
}
}
undo::Truncate::clear_trunc_list();
}
mtr_commit(&mtr);
/* Step-2: Flush the dirty pages from the buffer pool. */
for (undo::undo_spaces_t::const_iterator it
= undo::Truncate::s_fix_up_spaces.begin();
it != undo::Truncate::s_fix_up_spaces.end();
++it) {
FlushObserver dummy(fil_system.sys_space, NULL, NULL);
buf_LRU_flush_or_remove_pages(TRX_SYS_SPACE, &dummy);
FlushObserver dummy2(fil_space_get(*it), NULL, NULL);
buf_LRU_flush_or_remove_pages(*it, &dummy2);
/* Remove the truncate redo log file. */
undo::done(*it);
}
}
return(DB_SUCCESS);
}
@ -1943,7 +1843,7 @@ files_checked:
ulint ibuf_root = btr_create(
DICT_CLUSTERED | DICT_IBUF, fil_system.sys_space,
DICT_IBUF_ID_MIN, dict_ind_redundant, NULL, &mtr);
DICT_IBUF_ID_MIN, dict_ind_redundant, &mtr);
mtr_commit(&mtr);
@ -1982,22 +1882,6 @@ files_checked:
return(srv_init_abort(err));
}
} else {
/* Invalidate the buffer pool to ensure that we reread
the page that we read above, during recovery.
Note that this is not as heavy weight as it seems. At
this point there will be only ONE page in the buf_LRU
and there must be no page in the buf_flush list. */
buf_pool_invalidate();
/* Scan and locate truncate log files. Parsed located files
and add table to truncate information to central vector for
truncate fix-up action post recovery. */
err = TruncateLogParser::scan_and_parse(srv_log_group_home_dir);
if (err != DB_SUCCESS) {
return(srv_init_abort(DB_ERROR));
}
/* We always try to do a recovery, even if the database had
been shut down normally: this is the normal startup path */
@ -2276,14 +2160,6 @@ files_checked:
trx_rollback_recovered(false);
}
/* Fix-up truncate of tables in the system tablespace
if server crashed while truncate was active. The non-
system tables are done after tablespace discovery. Do
this now because this procedure assumes that no pages
have changed since redo recovery. Tablespace discovery
can do updates to pages in the system tablespace.*/
err = truncate_t::fixup_tables_in_system_tablespace();
if (srv_force_recovery < SRV_FORCE_NO_IBUF_MERGE) {
/* Open or Create SYS_TABLESPACES and SYS_DATAFILES
so that tablespace names and other metadata can be
@ -2321,10 +2197,6 @@ files_checked:
dict_check_tablespaces_and_store_max_id(validate);
}
/* Fix-up truncate of table if server crashed while truncate
was active. */
err = truncate_t::fixup_tables_in_non_system_tablespace();
if (err != DB_SUCCESS) {
return(srv_init_abort(err));
}

View file

@ -177,7 +177,8 @@ void purge_sys_t::create()
hdr_offset= 0;
rw_lock_create(trx_purge_latch_key, &latch, SYNC_PURGE_LATCH);
mutex_create(LATCH_ID_PURGE_SYS_PQ, &pq_mutex);
undo_trunc.create();
truncate.current= NULL;
truncate.last= NULL;
}
/** Close the purge subsystem on shutdown. */
@ -513,309 +514,22 @@ func_exit:
goto loop;
}
/** UNDO log truncate logger. Needed to track state of truncate during crash.
An auxiliary redo log file undo_<space_id>_trunc.log will created while the
truncate of the UNDO is in progress. This file is required during recovery
to complete the truncate. */
namespace undo {
/** Magic Number to indicate truncate action is complete. */
static const ib_uint32_t s_magic = 76845412;
/** Populate log file name based on space_id
@param[in] space_id id of the undo tablespace.
@return DB_SUCCESS or error code */
static dberr_t populate_log_file_name(
ulint space_id,
char*& log_file_name)
{
static const char s_log_prefix[] = "undo_";
static const char s_log_ext[] = "trunc.log";
ulint log_file_name_sz = strlen(srv_log_group_home_dir)
+ (22 - 1 /* NUL */
+ sizeof s_log_prefix + sizeof s_log_ext);
log_file_name = new (std::nothrow) char[log_file_name_sz];
if (log_file_name == 0) {
return(DB_OUT_OF_MEMORY);
}
memset(log_file_name, 0, log_file_name_sz);
strcpy(log_file_name, srv_log_group_home_dir);
ulint log_file_name_len = strlen(log_file_name);
if (log_file_name[log_file_name_len - 1]
!= OS_PATH_SEPARATOR) {
log_file_name[log_file_name_len]
= OS_PATH_SEPARATOR;
log_file_name_len = strlen(log_file_name);
}
snprintf(log_file_name + log_file_name_len,
log_file_name_sz - log_file_name_len,
"%s" ULINTPF "_%s", s_log_prefix,
space_id, s_log_ext);
return(DB_SUCCESS);
}
/** Mark completion of undo truncate action by writing magic number to
the log file and then removing it from the disk.
If we are going to remove it from disk then why write magic number ?
This is to safeguard from unlink (file-system) anomalies that will keep
the link to the file even after unlink action is successfull and
ref-count = 0.
@param[in] space_id id of the undo tablespace to truncate.*/
void done(
ulint space_id)
{
dberr_t err;
char* log_file_name;
/* Step-1: Create the log file name using the pre-decided
prefix/suffix and table id of undo tablepsace to truncate. */
err = populate_log_file_name(space_id, log_file_name);
if (err != DB_SUCCESS) {
return;
}
/* Step-2: Open log file and write magic number to
indicate done phase. */
bool ret;
os_file_t handle =
os_file_create_simple_no_error_handling(
innodb_log_file_key, log_file_name,
OS_FILE_OPEN, OS_FILE_READ_WRITE,
srv_read_only_mode, &ret);
if (!ret) {
os_file_delete(innodb_log_file_key, log_file_name);
delete[] log_file_name;
return;
}
ulint sz = srv_page_size;
void* buf = ut_zalloc_nokey(sz + srv_page_size);
if (buf == NULL) {
os_file_close(handle);
os_file_delete(innodb_log_file_key, log_file_name);
delete[] log_file_name;
return;
}
byte* log_buf = static_cast<byte*>(
ut_align(buf, srv_page_size));
mach_write_to_4(log_buf, undo::s_magic);
IORequest request(IORequest::WRITE);
err = os_file_write(
request, log_file_name, handle, log_buf, 0, sz);
ut_ad(err == DB_SUCCESS);
os_file_flush(handle);
os_file_close(handle);
ut_free(buf);
os_file_delete(innodb_log_file_key, log_file_name);
delete[] log_file_name;
}
/** Check if TRUNCATE_DDL_LOG file exist.
@param[in] space_id id of the undo tablespace.
@return true if exist else false. */
bool is_log_present(
ulint space_id)
{
dberr_t err;
char* log_file_name;
/* Step-1: Populate log file name. */
err = populate_log_file_name(space_id, log_file_name);
if (err != DB_SUCCESS) {
return(false);
}
/* Step-2: Check for existence of the file. */
bool exist;
os_file_type_t type;
os_file_status(log_file_name, &exist, &type);
/* Step-3: If file exists, check it for presence of magic
number. If found, then delete the file and report file
doesn't exist as presence of magic number suggest that
truncate action was complete. */
if (exist) {
bool ret;
os_file_t handle =
os_file_create_simple_no_error_handling(
innodb_log_file_key, log_file_name,
OS_FILE_OPEN, OS_FILE_READ_WRITE,
srv_read_only_mode, &ret);
if (!ret) {
os_file_delete(innodb_log_file_key,
log_file_name);
delete[] log_file_name;
return(false);
}
ulint sz = srv_page_size;
void* buf = ut_zalloc_nokey(sz + srv_page_size);
if (buf == NULL) {
os_file_close(handle);
os_file_delete(innodb_log_file_key,
log_file_name);
delete[] log_file_name;
return(false);
}
byte* log_buf = static_cast<byte*>(
ut_align(buf, srv_page_size));
IORequest request(IORequest::READ);
dberr_t err;
err = os_file_read(request, handle, log_buf, 0, sz);
os_file_close(handle);
if (err != DB_SUCCESS) {
ib::info()
<< "Unable to read '"
<< log_file_name << "' : "
<< ut_strerr(err);
os_file_delete(
innodb_log_file_key, log_file_name);
ut_free(buf);
delete[] log_file_name;
return(false);
}
ulint magic_no = mach_read_from_4(log_buf);
ut_free(buf);
if (magic_no == undo::s_magic) {
/* Found magic number. */
os_file_delete(innodb_log_file_key,
log_file_name);
delete[] log_file_name;
return(false);
}
}
delete[] log_file_name;
return(exist);
}
};
/** Iterate over all the UNDO tablespaces and check if any of the UNDO
tablespace qualifies for TRUNCATE (size > threshold).
@param[in,out] undo_trunc undo truncate tracker */
static
void
trx_purge_mark_undo_for_truncate(
undo::Truncate* undo_trunc)
{
/* Step-1: If UNDO Tablespace
- already marked for truncate (OR)
- truncate disabled
return immediately else search for qualifying tablespace. */
if (undo_trunc->is_marked() || !srv_undo_log_truncate) {
return;
}
/* Step-2: Validation/Qualification checks
a. At-least 2 UNDO tablespaces so even if one UNDO tablespace
is being truncated server can continue to operate.
b. At-least 2 persistent UNDO logs (besides the default rseg-0)
b. At-least 1 UNDO tablespace size > threshold. */
if (srv_undo_tablespaces_active < 2 || srv_undo_logs < 3) {
return;
}
/* Avoid bias selection and so start the scan from immediate next
of last selected UNDO tablespace for truncate. */
ulint space_id = undo_trunc->get_scan_start();
for (ulint i = 1; i <= srv_undo_tablespaces_active; i++) {
if (fil_space_get_size(space_id)
> (srv_max_undo_log_size >> srv_page_size_shift)) {
/* Tablespace qualifies for truncate. */
undo_trunc->mark(space_id);
undo::Truncate::add_space_to_trunc_list(space_id);
break;
}
space_id = ((space_id + 1) % (srv_undo_tablespaces_active + 1));
if (space_id == 0) {
/* Note: UNDO tablespace ids starts from 1. */
++space_id;
}
}
/* Couldn't make any selection. */
if (!undo_trunc->is_marked()) {
return;
}
DBUG_LOG("undo",
"marking for truncate UNDO tablespace "
<< undo_trunc->get_marked_space_id());
/* Step-3: Iterate over all the rsegs of selected UNDO tablespace
and mark them temporarily unavailable for allocation.*/
for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) {
if (trx_rseg_t* rseg = trx_sys.rseg_array[i]) {
ut_ad(rseg->is_persistent());
if (rseg->space->id
== undo_trunc->get_marked_space_id()) {
/* Once set this rseg will not be allocated
to new booting transaction but we will wait
for existing active transaction to finish. */
rseg->skip_allocation = true;
undo_trunc->add_rseg_to_trunc(rseg);
}
}
}
}
undo::undo_spaces_t undo::Truncate::s_spaces_to_truncate;
/** Cleanse purge queue to remove the rseg that reside in undo-tablespace
marked for truncate.
@param[in,out] undo_trunc undo truncate tracker */
static
void
trx_purge_cleanse_purge_queue(
undo::Truncate* undo_trunc)
@param[in] space undo tablespace being truncated */
static void trx_purge_cleanse_purge_queue(const fil_space_t& space)
{
mutex_enter(&purge_sys.pq_mutex);
typedef std::vector<TrxUndoRsegs> purge_elem_list_t;
purge_elem_list_t purge_elem_list;
mutex_enter(&purge_sys.pq_mutex);
/* Remove rseg instances that are in the purge queue before we start
truncate of corresponding UNDO truncate. */
while (!purge_sys.purge_queue.empty()) {
purge_elem_list.push_back(purge_sys.purge_queue.top());
purge_sys.purge_queue.pop();
}
ut_ad(purge_sys.purge_queue.empty());
for (purge_elem_list_t::iterator it = purge_elem_list.begin();
it != purge_elem_list.end();
@ -824,9 +538,7 @@ trx_purge_cleanse_purge_queue(
for (TrxUndoRsegs::iterator it2 = it->begin();
it2 != it->end();
++it2) {
if ((*it2)->space->id
== undo_trunc->get_marked_space_id()) {
if ((*it2)->space == &space) {
it->erase(it2);
break;
}
@ -836,251 +548,10 @@ trx_purge_cleanse_purge_queue(
purge_sys.purge_queue.push(*it);
}
}
mutex_exit(&purge_sys.pq_mutex);
}
/** Iterate over selected UNDO tablespace and check if all the rsegs
that resides in the tablespace are free.
@param[in] limit truncate_limit
@param[in,out] undo_trunc undo truncate tracker */
static
void
trx_purge_initiate_truncate(
const purge_sys_t::iterator& limit,
undo::Truncate* undo_trunc)
{
/* Step-1: Early check to findout if any of the the UNDO tablespace
is marked for truncate. */
if (!undo_trunc->is_marked()) {
/* No tablespace marked for truncate yet. */
return;
}
/* Step-2: Scan over each rseg and ensure that it doesn't hold any
active undo records. */
bool all_free = true;
for (ulint i = 0; i < undo_trunc->rsegs_size() && all_free; ++i) {
trx_rseg_t* rseg = undo_trunc->get_ith_rseg(i);
mutex_enter(&rseg->mutex);
if (rseg->trx_ref_count > 0) {
/* This rseg is still being held by an active
transaction. */
all_free = false;
mutex_exit(&rseg->mutex);
continue;
}
ut_ad(rseg->trx_ref_count == 0);
ut_ad(rseg->skip_allocation);
ulint size_of_rsegs = rseg->curr_size;
if (size_of_rsegs == 1) {
mutex_exit(&rseg->mutex);
continue;
} else {
/* There could be cached undo segment. Check if records
in these segments can be purged. Normal purge history
will not touch these cached segment. */
ulint cached_undo_size = 0;
for (trx_undo_t* undo =
UT_LIST_GET_FIRST(rseg->undo_cached);
undo != NULL && all_free;
undo = UT_LIST_GET_NEXT(undo_list, undo)) {
if (limit.trx_no() < undo->trx_id) {
all_free = false;
} else {
cached_undo_size += undo->size;
}
}
ut_ad(size_of_rsegs >= (cached_undo_size + 1));
if (size_of_rsegs > (cached_undo_size + 1)) {
/* There are pages besides cached pages that
still hold active data. */
all_free = false;
}
}
mutex_exit(&rseg->mutex);
}
if (!all_free) {
/* rseg still holds active data.*/
return;
}
/* Step-3: Start the actual truncate.
a. Remove rseg instance if added to purge queue before we
initiate truncate.
b. Execute actual truncate */
const ulint space_id = undo_trunc->get_marked_space_id();
ib::info() << "Truncating UNDO tablespace " << space_id;
trx_purge_cleanse_purge_queue(undo_trunc);
ut_a(srv_is_undo_tablespace(space_id));
fil_space_t* space = fil_space_get(space_id);
if (!space) {
not_found:
ib::error() << "Failed to find UNDO tablespace " << space_id;
return;
}
/* Flush all to-be-discarded pages of the tablespace.
During truncation, we do not want any writes to the
to-be-discarded area, because we must set the space->size
early in order to have deterministic page allocation.
If a log checkpoint was completed at LSN earlier than our
mini-transaction commit and the server was killed, then
discarding the to-be-trimmed pages without flushing would
break crash recovery. So, we cannot avoid the write. */
{
FlushObserver observer(
space,
UT_LIST_GET_FIRST(purge_sys.query->thrs)->graph->trx,
NULL);
buf_LRU_flush_or_remove_pages(space_id, &observer);
}
log_free_check();
/* Adjust the tablespace metadata. */
space = fil_truncate_prepare(space_id);
if (!space) {
goto not_found;
}
/* Undo tablespace always are a single file. */
ut_a(UT_LIST_GET_LEN(space->chain) == 1);
fil_node_t* file = UT_LIST_GET_FIRST(space->chain);
/* The undo tablespace files are never closed. */
ut_ad(file->is_open());
/* Re-initialize tablespace, in a single mini-transaction. */
mtr_t mtr;
const ulint size = SRV_UNDO_TABLESPACE_SIZE_IN_PAGES;
mtr.start();
mtr_x_lock(&space->latch, &mtr);
fil_truncate_log(space, size, &mtr);
fsp_header_init(space, size, &mtr);
mutex_enter(&fil_system.mutex);
space->size = file->size = size;
mutex_exit(&fil_system.mutex);
buf_block_t* sys_header = trx_sysf_get(&mtr);
for (ulint i = 0; i < undo_trunc->rsegs_size(); ++i) {
trx_rsegf_t* rseg_header;
trx_rseg_t* rseg = undo_trunc->get_ith_rseg(i);
rseg->page_no = trx_rseg_header_create(
space, rseg->id, sys_header, &mtr);
rseg_header = trx_rsegf_get_new(
space_id, rseg->page_no, &mtr);
/* Before re-initialization ensure that we free the existing
structure. There can't be any active transactions. */
ut_a(UT_LIST_GET_LEN(rseg->undo_list) == 0);
ut_a(UT_LIST_GET_LEN(rseg->old_insert_list) == 0);
trx_undo_t* next_undo;
for (trx_undo_t* undo = UT_LIST_GET_FIRST(rseg->undo_cached);
undo != NULL;
undo = next_undo) {
next_undo = UT_LIST_GET_NEXT(undo_list, undo);
UT_LIST_REMOVE(rseg->undo_cached, undo);
MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED);
ut_free(undo);
}
UT_LIST_INIT(rseg->undo_list, &trx_undo_t::undo_list);
UT_LIST_INIT(rseg->undo_cached, &trx_undo_t::undo_list);
UT_LIST_INIT(rseg->old_insert_list, &trx_undo_t::undo_list);
/* These were written by trx_rseg_header_create(). */
ut_ad(!mach_read_from_4(rseg_header + TRX_RSEG_FORMAT));
ut_ad(!mach_read_from_4(rseg_header + TRX_RSEG_HISTORY_SIZE));
/* Initialize the undo log lists according to the rseg header */
rseg->curr_size = 1;
rseg->trx_ref_count = 0;
rseg->last_page_no = FIL_NULL;
rseg->last_offset = 0;
rseg->last_commit = 0;
rseg->needs_purge = false;
}
mtr.commit();
/* Write-ahead the redo log record. */
log_write_up_to(mtr.commit_lsn(), true);
/* 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_coordinator_thread. */
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
truncated then move to next rseg element.
Note: Ideally purge_sys.rseg should be NULL because purge
should complete processing of all the records but there is
purge_batch_size that can force the purge loop to exit before
all the records are purged and in this case purge_sys.rseg
could point to a valid rseg waiting for next purge cycle. */
purge_sys.next_stored = false;
purge_sys.rseg = NULL;
}
DBUG_EXECUTE_IF("ib_undo_trunc",
ib::info() << "ib_undo_trunc";
log_write_up_to(LSN_MAX, true);
DBUG_SUICIDE(););
/* Completed truncate. Now it is safe to re-use the tablespace. */
for (ulint i = 0; i < undo_trunc->rsegs_size(); ++i) {
trx_rseg_t* rseg = undo_trunc->get_ith_rseg(i);
rseg->skip_allocation = false;
}
ib::info() << "Truncated UNDO tablespace " << space_id;
undo_trunc->reset();
undo::Truncate::clear_trunc_list();
}
/**
Removes unnecessary history data from rollback segments. NOTE that when this
function is called, the caller must not have any latches on undo log pages!
@ -1104,12 +575,258 @@ static void trx_purge_truncate_history()
}
}
/* UNDO tablespace truncate. We will try to truncate as much as we
can (greedy approach). This will ensure when the server is idle we
try and truncate all the UNDO tablespaces. */
for (ulint i = srv_undo_tablespaces_active; i--; ) {
trx_purge_mark_undo_for_truncate(&purge_sys.undo_trunc);
trx_purge_initiate_truncate(head, &purge_sys.undo_trunc);
if (srv_undo_tablespaces_active < 2) {
return;
}
while (srv_undo_log_truncate && srv_undo_logs >= 3) {
if (!purge_sys.truncate.current) {
const ulint threshold = ulint(srv_max_undo_log_size
>> srv_page_size_shift);
for (ulint i = purge_sys.truncate.last
? purge_sys.truncate.last->id
- srv_undo_space_id_start
: 0, j = i;; ) {
ulint space_id = srv_undo_space_id_start + i;
ut_ad(srv_is_undo_tablespace(space_id));
if (fil_space_get_size(space_id)
> threshold) {
purge_sys.truncate.current
= fil_space_get(space_id);
break;
}
++i;
i %= srv_undo_tablespaces_active;
if (i == j) {
break;
}
}
}
if (!purge_sys.truncate.current) {
return;
}
const fil_space_t& space = *purge_sys.truncate.current;
/* Undo tablespace always are a single file. */
ut_a(UT_LIST_GET_LEN(space.chain) == 1);
fil_node_t* file = UT_LIST_GET_FIRST(space.chain);
/* The undo tablespace files are never closed. */
ut_ad(file->is_open());
DBUG_LOG("undo", "marking for truncate: " << file->name);
for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) {
if (trx_rseg_t* rseg = trx_sys.rseg_array[i]) {
ut_ad(rseg->is_persistent());
if (rseg->space == &space) {
/* Once set, this rseg will
not be allocated to subsequent
transactions, but we will wait
for existing active
transactions to finish. */
rseg->skip_allocation = true;
}
}
}
for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) {
trx_rseg_t* rseg = trx_sys.rseg_array[i];
if (!rseg || rseg->space != &space) {
continue;
}
mutex_enter(&rseg->mutex);
ut_ad(rseg->skip_allocation);
if (rseg->trx_ref_count) {
not_free:
mutex_exit(&rseg->mutex);
return;
}
if (rseg->curr_size != 1) {
/* Check if all segments are
cached and safe to remove. */
ulint cached = 0;
for (trx_undo_t* undo = UT_LIST_GET_FIRST(
rseg->undo_cached);
undo;
undo = UT_LIST_GET_NEXT(undo_list,
undo)) {
if (head.trx_no() < undo->trx_id) {
goto not_free;
} else {
cached += undo->size;
}
}
ut_ad(rseg->curr_size > cached);
if (rseg->curr_size > cached + 1) {
goto not_free;
}
}
mutex_exit(&rseg->mutex);
}
ib::info() << "Truncating " << file->name;
trx_purge_cleanse_purge_queue(space);
/* Flush all to-be-discarded pages of the tablespace.
During truncation, we do not want any writes to the
to-be-discarded area, because we must set the space.size
early in order to have deterministic page allocation.
If a log checkpoint was completed at LSN earlier than our
mini-transaction commit and the server was killed, then
discarding the to-be-trimmed pages without flushing would
break crash recovery. So, we cannot avoid the write. */
{
FlushObserver observer(
purge_sys.truncate.current,
UT_LIST_GET_FIRST(purge_sys.query->thrs)
->graph->trx,
NULL);
buf_LRU_flush_or_remove_pages(space.id, &observer);
}
log_free_check();
/* Adjust the tablespace metadata. */
if (!fil_truncate_prepare(space.id)) {
ib::error() << "Failed to find UNDO tablespace "
<< file->name;
return;
}
/* Re-initialize tablespace, in a single mini-transaction. */
mtr_t mtr;
const ulint size = SRV_UNDO_TABLESPACE_SIZE_IN_PAGES;
mtr.start();
mtr_x_lock(&purge_sys.truncate.current->latch, &mtr);
fil_truncate_log(purge_sys.truncate.current, size, &mtr);
fsp_header_init(purge_sys.truncate.current, size, &mtr);
mutex_enter(&fil_system.mutex);
purge_sys.truncate.current->size = file->size = size;
mutex_exit(&fil_system.mutex);
buf_block_t* sys_header = trx_sysf_get(&mtr);
for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) {
trx_rseg_t* rseg = trx_sys.rseg_array[i];
if (!rseg || rseg->space != &space) {
continue;
}
ut_ad(rseg->is_persistent());
ut_d(const ulint old_page = rseg->page_no);
rseg->page_no = trx_rseg_header_create(
purge_sys.truncate.current,
rseg->id, sys_header, &mtr);
ut_ad(old_page == rseg->page_no);
trx_rsegf_t* rseg_header = trx_rsegf_get_new(
space.id, rseg->page_no, &mtr);
/* Before re-initialization ensure that we
free the existing structure. There can't be
any active transactions. */
ut_a(UT_LIST_GET_LEN(rseg->undo_list) == 0);
ut_a(UT_LIST_GET_LEN(rseg->old_insert_list) == 0);
trx_undo_t* next_undo;
for (trx_undo_t* undo = UT_LIST_GET_FIRST(
rseg->undo_cached);
undo; undo = next_undo) {
next_undo = UT_LIST_GET_NEXT(undo_list, undo);
UT_LIST_REMOVE(rseg->undo_cached, undo);
MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED);
ut_free(undo);
}
UT_LIST_INIT(rseg->undo_list,
&trx_undo_t::undo_list);
UT_LIST_INIT(rseg->undo_cached,
&trx_undo_t::undo_list);
UT_LIST_INIT(rseg->old_insert_list,
&trx_undo_t::undo_list);
/* These were written by trx_rseg_header_create(). */
ut_ad(!mach_read_from_4(rseg_header
+ TRX_RSEG_FORMAT));
ut_ad(!mach_read_from_4(rseg_header
+ TRX_RSEG_HISTORY_SIZE));
/* Initialize the undo log lists according to
the rseg header */
rseg->curr_size = 1;
rseg->trx_ref_count = 0;
rseg->last_page_no = FIL_NULL;
rseg->last_offset = 0;
rseg->last_commit = 0;
rseg->needs_purge = false;
}
mtr.commit();
/* Write-ahead the redo log record. */
log_write_up_to(mtr.commit_lsn(), true);
/* 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_coordinator_thread. */
export_vars.innodb_undo_truncations++;
/* TODO: PUNCH_HOLE the garbage (with write-ahead logging) */
mutex_enter(&fil_system.mutex);
ut_ad(&space == purge_sys.truncate.current);
ut_ad(space.stop_new_ops);
ut_ad(space.is_being_truncated);
purge_sys.truncate.current->stop_new_ops = false;
purge_sys.truncate.current->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 truncated then move to next rseg
element. Note: Ideally purge_sys.rseg should
be NULL because purge should complete
processing of all the records but there is
purge_batch_size that can force the purge loop
to exit before all the records are purged and
in this case purge_sys.rseg could point to a
valid rseg waiting for next purge cycle. */
purge_sys.next_stored = false;
purge_sys.rseg = NULL;
}
DBUG_EXECUTE_IF("ib_undo_trunc",
ib::info() << "ib_undo_trunc";
log_write_up_to(LSN_MAX, true);
DBUG_SUICIDE(););
for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) {
if (trx_rseg_t* rseg = trx_sys.rseg_array[i]) {
ut_ad(rseg->is_persistent());
if (rseg->space == &space) {
rseg->skip_allocation = false;
}
}
}
ib::info() << "Truncated " << file->name;
purge_sys.truncate.last = purge_sys.truncate.current;
purge_sys.truncate.current = NULL;
}
}

View file

@ -150,7 +150,6 @@ ut_new_boot()
"row0merge",
"row0mysql",
"row0sel",
"row0trunc",
"srv0conc",
"srv0srv",
"srv0start",

View file

@ -589,8 +589,6 @@ ut_strerr(
return("Tablespace already exists");
case DB_TABLESPACE_DELETED:
return("Tablespace deleted or being deleted");
case DB_TABLESPACE_TRUNCATED:
return("Tablespace was truncated");
case DB_TABLESPACE_NOT_FOUND:
return("Tablespace not found");
case DB_LOCK_TABLE_FULL: