Skip to content
Permalink
Browse files

Bug#21796691 INNODB REDO LOG DOES NOT INDICATE WHEN REDO LOGGING IS S…

…KIPPED

As part of WL#7277 Bulk Load for Create Index,
InnoDB disabled redo logging of index page writes during DDL operations.

InnoDB crash recovery is not affected by this, because there will be
extra page flushes before the DDL operation is committed.

But, external hot backup tools will have no way of detecting if the
redo logging was disabled, and they can thus create a corrupted backup
without noticing it, if there was concurrent DDL.

After successfully completing the extra flush and before re-enabling
redo logging, we will issue a new redo log record
MLOG_INDEX_LOAD(index->space,index->page,index->id).

When backup tools see this record in the redo log, they can either
retry copying the tablespace (if it is small) or abort the backup,
asking the user to avoid concurrent DDL.

recv_parse_or_apply_log_rec_body(): Parse and ignore the MLOG_INDEX_LOAD
record.

row_merge_write_redo(): New function, to write the MLOG_INDEX_LOAD
record after the non-logged changes have been flushed.

RB: 10168
Reviewed-by: Shaohua Wang <shaohua.wang@oracle.com>
Reviewed-by: Jimmy Yang <jimmy.yang@oracle.com>
  • Loading branch information...
Marko Mäkelä
Marko Mäkelä committed Sep 9, 2015
1 parent d14b387 commit c8b6a6b4e0574910822945be1b041407a822f2c1
@@ -0,0 +1,15 @@
#
# Bug#21796691 INNODB REDO LOG DOES NOT INDICATE WHEN
# REDO LOGGING IS SKIPPED
#
CREATE TABLE t1 (a INT NOT NULL, b INT UNIQUE) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1,2);
ALTER TABLE t1 ADD PRIMARY KEY(a), ALGORITHM=INPLACE;
ALTER TABLE t1 DROP INDEX b, ADD INDEX (b);
# Kill the server
# restart: --debug=d,ib_log
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
# restart;
DROP TABLE t1;
@@ -0,0 +1,40 @@
--source include/have_innodb.inc
--source include/have_debug.inc

# Embedded server does not support crashing
--source include/not_embedded.inc

--echo #
--echo # Bug#21796691 INNODB REDO LOG DOES NOT INDICATE WHEN
--echo # REDO LOGGING IS SKIPPED
--echo #
CREATE TABLE t1 (a INT NOT NULL, b INT UNIQUE) ENGINE=InnoDB;
# MLOG_INDEX_LOAD will not be emitted for empty tables. Insert a row.
INSERT INTO t1 VALUES (1,2);
--source include/no_checkpoint_start.inc
# We should get two MLOG_INDEX_LOAD for this.
ALTER TABLE t1 ADD PRIMARY KEY(a), ALGORITHM=INPLACE;
# And one MLOG_INDEX_LOAD for this.
ALTER TABLE t1 DROP INDEX b, ADD INDEX (b);

--let CLEANUP_IF_CHECKPOINT=DROP TABLE t1;
--source include/no_checkpoint_end.inc

--let $restart_parameters = restart: --debug=d,ib_log
--source include/start_mysqld.inc

# Look for at least one MLOG_INDEX_LOAD in the error log.
# Theoretically, it may have been written by this test or an earlier test.
# FIXME: redirect the error log of the restart to a new file,
# and ensure that we have the exact number of records there.
let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.1.err;
let SEARCH_PATTERN=scan .*: log rec MLOG_INDEX_LOAD;
--source include/search_pattern_in_file.inc

CHECK TABLE t1;

# Remove the --debug=d,ib_log setting.
--let $restart_parameters = restart;
--source include/restart_mysqld.inc

DROP TABLE t1;
@@ -895,17 +895,21 @@ BtrBulk::insert(
return(DB_SUCCESS);
}

/** Btree bulk load finish. We commit last page in each level and copy last
page in top level to root page of the index if no error occurs.
@param[in] success whether bulk load is successful
/** Btree bulk load finish. We commit the last page in each level
and copy the last page in top level to the root page of the index
if no error occurs.
@param[in] err whether bulk load was successful until now
@return error code */
dberr_t
BtrBulk::finish(
dberr_t err)
BtrBulk::finish(dberr_t err)
{
ulint last_page_no = FIL_NULL;

ut_ad(!dict_table_is_temporary(m_index->table));

if (m_page_bulks->size() == 0) {
/* The table is empty. The root page of the index tree
is already in a consistent state. No need to flush. */
return(err);
}

@@ -977,11 +981,8 @@ BtrBulk::finish(
dict_sync_check check(true);

ut_ad(!sync_check_iterate(check));

if (err == DB_SUCCESS) {
ut_ad(btr_validate_index(m_index, NULL, false));
}
#endif /* UNIV_DEBUG */

ut_ad(err != DB_SUCCESS || btr_validate_index(m_index, NULL, false));
return(err);
}
@@ -309,10 +309,11 @@ class BtrBulk
return(insert(tuple, 0));
}

/** Finish bulk load. We commit last page in each level and copy last
page in top level to root page of the index if no error occurs.
@param[in] err error code of insert.
@return error code */
/** Btree bulk load finish. We commit the last page in each level
and copy the last page in top level to the root page of the index
if no error occurs.
@param[in] err whether bulk load was successful until now
@return error code */
dberr_t finish(dberr_t err);

/** Release all latches */
@@ -185,6 +185,7 @@ mlog_write_initial_log_record_low(
ut_ad(type == MLOG_FILE_NAME
|| type == MLOG_FILE_DELETE
|| type == MLOG_FILE_RENAME2
|| type == MLOG_INDEX_LOAD
|| type == MLOG_TRUNCATE
|| mtr->is_named_space(space_id));

@@ -235,8 +235,12 @@ enum mlog_id_t {
/** Table is being truncated. (Marked only for file-per-table) */
MLOG_TRUNCATE = 60,

/** notify that an index tree is being loaded without writing
redo log about individual pages */
MLOG_INDEX_LOAD = 61,

/** biggest value (used in assertions) */
MLOG_BIGGEST_TYPE = MLOG_TRUNCATE
MLOG_BIGGEST_TYPE = MLOG_INDEX_LOAD
};

/* @} */
@@ -1247,6 +1247,11 @@ recv_parse_or_apply_log_rec_body(
before applying any log records. */
return(fil_name_parse(ptr, end_ptr, space_id, page_no, type,
apply));
case MLOG_INDEX_LOAD:
if (end_ptr < ptr + 8) {
return(NULL);
}
return(ptr + 8);
case MLOG_TRUNCATE:
return(truncate_t::parse_redo_entry(ptr, end_ptr, space_id));
default:
@@ -1702,6 +1707,8 @@ recv_add_to_hash_table(
ut_ad(type != MLOG_FILE_NAME);
ut_ad(type != MLOG_DUMMY_RECORD);
ut_ad(type != MLOG_CHECKPOINT);
ut_ad(type != MLOG_INDEX_LOAD);
ut_ad(type != MLOG_TRUNCATE);

len = rec_end - body;

@@ -2735,6 +2742,7 @@ recv_parse_log_recs(
case MLOG_FILE_NAME:
case MLOG_FILE_RENAME2:
case MLOG_FILE_DELETE:
case MLOG_TRUNCATE:
/* These were already handled by
recv_parse_log_rec() and
recv_parse_or_apply_log_rec_body(). */
@@ -2747,13 +2755,6 @@ recv_parse_log_recs(
break;
#endif /* UNIV_LOG_LSN_DEBUG */
default:
DBUG_PRINT("ib_log",
("scan " LSN_PF ": log rec %s"
" len " ULINTPF
" page " ULINTPF ":" ULINTPF,
old_lsn, get_mlog_string(type),
len, space, page_no));

switch (store) {
case STORE_NO:
break;
@@ -2769,6 +2770,14 @@ recv_parse_log_recs(
ptr + len, old_lsn,
recv_sys->recovered_lsn);
}
/* fall through */
case MLOG_INDEX_LOAD:
DBUG_PRINT("ib_log",
("scan " LSN_PF ": log rec %s"
" len " ULINTPF
" page " ULINTPF ":" ULINTPF,
old_lsn, get_mlog_string(type),
len, space, page_no));
}
} else {
/* Check that all the records associated with the single mtr
@@ -2884,6 +2893,8 @@ recv_parse_log_recs(
case MLOG_FILE_NAME:
case MLOG_FILE_RENAME2:
case MLOG_FILE_DELETE:
case MLOG_INDEX_LOAD:
case MLOG_TRUNCATE:
/* These were already handled by
recv_parse_log_rec() and
recv_parse_or_apply_log_rec_body(). */
@@ -4175,6 +4186,9 @@ get_mlog_string(mlog_id_t type)
case MLOG_INIT_FILE_PAGE2:
return("MLOG_INIT_FILE_PAGE2");

case MLOG_INDEX_LOAD:
return("MLOG_INDEX_LOAD");

case MLOG_TRUNCATE:
return("MLOG_TRUNCATE");
}
@@ -1590,7 +1590,6 @@ row_geo_field_is_valid(
return(true);
}


/** Reads clustered index of the table and create temporary files
containing the index entries for the indexes to be built.
@param[in] trx transaction
@@ -3207,7 +3206,7 @@ row_merge_insert_index_tuples(
stage->inc();
}

if (row_buf != NULL) {
if (row_buf != NULL) {
if (n_rows >= row_buf->n_tuples) {
break;
}
@@ -4234,6 +4233,29 @@ row_merge_drop_table(
trx, false, false));
}

/** Write an MLOG_INDEX_LOAD record to indicate in the redo-log
that redo-logging of individual index pages was disabled, and
the flushing of such pages to the data files was completed.
@param[in] index an index tree on which redo logging was disabled */
static
void
row_merge_write_redo(
const dict_index_t* index)
{
mtr_t mtr;
byte* log_ptr;

ut_ad(!dict_table_is_temporary(index->table));
mtr.start();
log_ptr = mlog_open(&mtr, 11 + 8);
log_ptr = mlog_write_initial_log_record_low(
MLOG_INDEX_LOAD,
index->space, index->page, log_ptr, &mtr);
mach_write_to_8(log_ptr, index->id);
mlog_close(&mtr, log_ptr + 8);
mtr.commit();
}

/** Build indexes on a table by reading a clustered index, creating a temporary
file containing index entries, merge sorting these index entries and inserting
sorted index entries to indexes.
@@ -4513,6 +4535,7 @@ row_merge_build_indexes(
ut_ad(need_flush_observer);

flush_observer->flush();
row_merge_write_redo(indexes[i]);

DEBUG_SYNC_C("row_log_apply_before");
error = row_log_apply(trx, sort_idx, table, stage);
@@ -4615,6 +4638,15 @@ row_merge_build_indexes(
if (trx_is_interrupted(trx)) {
error = DB_INTERRUPTED;
}

if (error == DB_SUCCESS && old_table != new_table) {
for (const dict_index_t* index
= dict_table_get_first_index(new_table);
index != NULL;
index = dict_table_get_next_index(index)) {
row_merge_write_redo(index);
}
}
}

DBUG_RETURN(error);

0 comments on commit c8b6a6b

Please sign in to comment.
You can’t perform that action at this time.