Commit 4bfdba2e authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-26672 innodb_undo_log_truncate may reset transaction ID sequence

trx_rseg_header_create(): Add a parameter for the value that is
to be written to TRX_RSEG_MAX_TRX_ID. If we omit this write, then
the updated test innodb.undo_truncate will fail for the 4k, 8k, 16k
page sizes. This was broken ever since
commit 947efe17 (MDEV-15158)
removed the writes of transaction identifiers to the TRX_SYS page.

srv_do_purge(): Truncate undo tablespaces also during slow shutdown
(innodb_fast_shutdown=0).

Thanks to Krunal Bauskar for noticing this problem.
parent b46cf33a
...@@ -2,11 +2,7 @@ SET @save_undo_logs = @@GLOBAL.innodb_undo_logs; ...@@ -2,11 +2,7 @@ SET @save_undo_logs = @@GLOBAL.innodb_undo_logs;
SET @save_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency; SET @save_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET @save_truncate = @@GLOBAL.innodb_undo_log_truncate; SET @save_truncate = @@GLOBAL.innodb_undo_log_truncate;
SET GLOBAL innodb_undo_log_truncate = 0; SET GLOBAL innodb_undo_log_truncate = 0;
SET GLOBAL innodb_undo_logs = 4;
SET GLOBAL innodb_purge_rseg_truncate_frequency = 1; SET GLOBAL innodb_purge_rseg_truncate_frequency = 1;
SET @trunc_start=
(SELECT variable_value FROM information_schema.global_status
WHERE variable_name = 'innodb_undo_truncations');
create table t1(keyc int primary key, c char(100)) engine = innodb; create table t1(keyc int primary key, c char(100)) engine = innodb;
create table t2(keyc int primary key, c char(100)) engine = innodb; create table t2(keyc int primary key, c char(100)) engine = innodb;
connect con1,localhost,root,,; connect con1,localhost,root,,;
...@@ -35,8 +31,5 @@ connection con2; ...@@ -35,8 +31,5 @@ connection con2;
commit; commit;
disconnect con2; disconnect con2;
connection default; connection default;
set global innodb_fast_shutdown=0;
drop table t1, t2; drop table t1, t2;
InnoDB 0 transactions not purged
SET GLOBAL innodb_undo_logs = @save_undo_logs;
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;
SET GLOBAL innodb_undo_log_truncate = @save_truncate;
--source include/have_innodb.inc --source include/have_innodb.inc
# With 32k, truncation could happen on shutdown after the test, --source include/innodb_page_size.inc
# and the mtr.add_suppression() would not filter out the warning.
# With 64k, no truncation seems to happen.
# --source include/innodb_page_size.inc
--source include/innodb_page_size_small.inc
--source include/have_undo_tablespaces.inc --source include/have_undo_tablespaces.inc
--source include/not_embedded.inc
--source include/have_sequence.inc --source include/have_sequence.inc
SET @save_undo_logs = @@GLOBAL.innodb_undo_logs; SET @save_undo_logs = @@GLOBAL.innodb_undo_logs;
SET @save_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency; SET @save_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET @save_truncate = @@GLOBAL.innodb_undo_log_truncate; SET @save_truncate = @@GLOBAL.innodb_undo_log_truncate;
SET GLOBAL innodb_undo_log_truncate = 0; SET GLOBAL innodb_undo_log_truncate = 0;
SET GLOBAL innodb_undo_logs = 4;
SET GLOBAL innodb_purge_rseg_truncate_frequency = 1; SET GLOBAL innodb_purge_rseg_truncate_frequency = 1;
SET @trunc_start=
(SELECT variable_value FROM information_schema.global_status
WHERE variable_name = 'innodb_undo_truncations');
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# #
# Perform DML action using multiple clients and multiple undo tablespace. # Perform DML action using multiple clients and multiple undo tablespace.
...@@ -48,21 +40,20 @@ commit; disconnect con1; ...@@ -48,21 +40,20 @@ commit; disconnect con1;
connection con2; commit; disconnect con2; connection con2; commit; disconnect con2;
connection default; connection default;
drop table t1, t2;
--source include/wait_all_purged.inc --replace_regex /.*Trx id counter ([0-9]+).*/\1/
let $trx_before= `SHOW ENGINE INNODB STATUS`;
let $trx_before= `select substr('$trx_before',9)+2`;
set global innodb_fast_shutdown=0;
--source include/restart_mysqld.inc
--replace_regex /.*Trx id counter ([0-9]+).*/\1/
let $trx_after= `SHOW ENGINE INNODB STATUS`;
let $trx_after= `select substr('$trx_after',9)`;
# Truncation will normally not occur with innodb_page_size=64k, drop table t1, t2;
# and occasionally not with innodb_page_size=32k,
# because the undo log will not grow enough. if ($trx_before != $trx_after)
if (`select @@innodb_page_size IN (4096,8192,16384)`)
{ {
let $wait_condition = (SELECT variable_value!=@trunc_start echo Transaction sequence mismatch: $trx_before != $trx_after;
FROM information_schema.global_status
WHERE variable_name = 'innodb_undo_truncations');
source include/wait_condition.inc;
} }
SET GLOBAL innodb_undo_logs = @save_undo_logs;
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;
SET GLOBAL innodb_undo_log_truncate = @save_truncate;
...@@ -71,6 +71,7 @@ trx_rsegf_undo_find_free(const trx_rsegf_t* rsegf); ...@@ -71,6 +71,7 @@ trx_rsegf_undo_find_free(const trx_rsegf_t* rsegf);
/** Create a rollback segment header. /** Create a rollback segment header.
@param[in,out] space system, undo, or temporary tablespace @param[in,out] space system, undo, or temporary tablespace
@param[in] rseg_id rollback segment identifier @param[in] rseg_id rollback segment identifier
@param[in] max_trx_id new value of TRX_RSEG_MAX_TRX_ID
@param[in,out] sys_header the TRX_SYS page (NULL for temporary rseg) @param[in,out] sys_header the TRX_SYS page (NULL for temporary rseg)
@param[in,out] mtr mini-transaction @param[in,out] mtr mini-transaction
@return the created rollback segment @return the created rollback segment
...@@ -79,6 +80,7 @@ buf_block_t* ...@@ -79,6 +80,7 @@ buf_block_t*
trx_rseg_header_create( trx_rseg_header_create(
fil_space_t* space, fil_space_t* space,
ulint rseg_id, ulint rseg_id,
trx_id_t max_trx_id,
buf_block_t* sys_header, buf_block_t* sys_header,
mtr_t* mtr); mtr_t* mtr);
......
...@@ -2640,6 +2640,8 @@ static ulint srv_do_purge(ulint* n_total_purged ...@@ -2640,6 +2640,8 @@ static ulint srv_do_purge(ulint* n_total_purged
n_pages_purged = trx_purge( n_pages_purged = trx_purge(
n_use_threads, n_use_threads,
(++count % rseg_truncate_frequency) == 0 (++count % rseg_truncate_frequency) == 0
|| (srv_shutdown_state != SRV_SHUTDOWN_NONE
&& srv_fast_shutdown == 0)
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
, slot , slot
#endif #endif
......
...@@ -1029,6 +1029,8 @@ srv_undo_tablespaces_init(bool create_new_db) ...@@ -1029,6 +1029,8 @@ srv_undo_tablespaces_init(bool create_new_db)
return DB_CORRUPTION; return DB_CORRUPTION;
} }
const trx_id_t max_trx_id = trx_sys.get_max_trx_id();
for (undo::undo_spaces_t::const_iterator it for (undo::undo_spaces_t::const_iterator it
= undo::Truncate::s_fix_up_spaces.begin(); = undo::Truncate::s_fix_up_spaces.begin();
it != undo::Truncate::s_fix_up_spaces.end(); it != undo::Truncate::s_fix_up_spaces.end();
...@@ -1046,7 +1048,8 @@ srv_undo_tablespaces_init(bool create_new_db) ...@@ -1046,7 +1048,8 @@ srv_undo_tablespaces_init(bool create_new_db)
if (trx_sysf_rseg_get_space(sys_header, i) if (trx_sysf_rseg_get_space(sys_header, i)
== *it) { == *it) {
trx_rseg_header_create( trx_rseg_header_create(
space, i, sys_header, &mtr); space, i, max_trx_id,
sys_header, &mtr);
} }
} }
......
...@@ -977,7 +977,8 @@ trx_purge_initiate_truncate( ...@@ -977,7 +977,8 @@ trx_purge_initiate_truncate(
for (ulint i = 0; i < undo_trunc->rsegs_size(); ++i) { for (ulint i = 0; i < undo_trunc->rsegs_size(); ++i) {
trx_rseg_t* rseg = undo_trunc->get_ith_rseg(i); trx_rseg_t* rseg = undo_trunc->get_ith_rseg(i);
buf_block_t* rblock = trx_rseg_header_create( buf_block_t* rblock = trx_rseg_header_create(
space, rseg->id, sys_header, &mtr); space, rseg->id, trx_sys.get_max_trx_id(),
sys_header, &mtr);
ut_ad(rblock); ut_ad(rblock);
rseg->page_no = rblock ? rblock->page.id.page_no() : FIL_NULL; rseg->page_no = rblock ? rblock->page.id.page_no() : FIL_NULL;
......
...@@ -286,6 +286,7 @@ void trx_rseg_format_upgrade(trx_rsegf_t* rseg_header, mtr_t* mtr) ...@@ -286,6 +286,7 @@ void trx_rseg_format_upgrade(trx_rsegf_t* rseg_header, mtr_t* mtr)
/** Create a rollback segment header. /** Create a rollback segment header.
@param[in,out] space system, undo, or temporary tablespace @param[in,out] space system, undo, or temporary tablespace
@param[in] rseg_id rollback segment identifier @param[in] rseg_id rollback segment identifier
@param[in] max_trx_id new value of TRX_RSEG_MAX_TRX_ID
@param[in,out] sys_header the TRX_SYS page (NULL for temporary rseg) @param[in,out] sys_header the TRX_SYS page (NULL for temporary rseg)
@param[in,out] mtr mini-transaction @param[in,out] mtr mini-transaction
@return the created rollback segment @return the created rollback segment
...@@ -294,6 +295,7 @@ buf_block_t* ...@@ -294,6 +295,7 @@ buf_block_t*
trx_rseg_header_create( trx_rseg_header_create(
fil_space_t* space, fil_space_t* space,
ulint rseg_id, ulint rseg_id,
trx_id_t max_trx_id,
buf_block_t* sys_header, buf_block_t* sys_header,
mtr_t* mtr) mtr_t* mtr)
{ {
...@@ -319,6 +321,12 @@ trx_rseg_header_create( ...@@ -319,6 +321,12 @@ trx_rseg_header_create(
mlog_write_ulint(TRX_RSEG + TRX_RSEG_HISTORY_SIZE + block->frame, 0, mlog_write_ulint(TRX_RSEG + TRX_RSEG_HISTORY_SIZE + block->frame, 0,
MLOG_4BYTES, mtr); MLOG_4BYTES, mtr);
if (max_trx_id) {
mlog_write_ull(TRX_RSEG + TRX_RSEG_MAX_TRX_ID + block->frame,
max_trx_id, mtr);
}
flst_init(TRX_RSEG + TRX_RSEG_HISTORY + block->frame, mtr); flst_init(TRX_RSEG + TRX_RSEG_HISTORY + block->frame, mtr);
trx_rsegf_t* rsegf = TRX_RSEG + block->frame; trx_rsegf_t* rsegf = TRX_RSEG + block->frame;
...@@ -684,7 +692,7 @@ trx_rseg_create(ulint space_id) ...@@ -684,7 +692,7 @@ trx_rseg_create(ulint space_id)
ulint rseg_id = trx_sys_rseg_find_free(sys_header); ulint rseg_id = trx_sys_rseg_find_free(sys_header);
if (buf_block_t* rblock = rseg_id == ULINT_UNDEFINED if (buf_block_t* rblock = rseg_id == ULINT_UNDEFINED
? NULL ? NULL
: trx_rseg_header_create(space, rseg_id, sys_header, : trx_rseg_header_create(space, rseg_id, 0, sys_header,
&mtr)) { &mtr)) {
ut_ad(trx_sysf_rseg_get_space(sys_header, rseg_id) ut_ad(trx_sysf_rseg_get_space(sys_header, rseg_id)
== space_id); == space_id);
...@@ -714,7 +722,7 @@ trx_temp_rseg_create() ...@@ -714,7 +722,7 @@ trx_temp_rseg_create()
mtr_x_lock_space(fil_system.temp_space, &mtr); mtr_x_lock_space(fil_system.temp_space, &mtr);
buf_block_t* rblock = trx_rseg_header_create( buf_block_t* rblock = trx_rseg_header_create(
fil_system.temp_space, i, NULL, &mtr); fil_system.temp_space, i, 0, NULL, &mtr);
trx_rseg_t* rseg = trx_rseg_mem_create( trx_rseg_t* rseg = trx_rseg_mem_create(
i, fil_system.temp_space, rblock->page.id.page_no()); i, fil_system.temp_space, rblock->page.id.page_no());
ut_ad(!rseg->is_persistent()); ut_ad(!rseg->is_persistent());
......
...@@ -197,7 +197,7 @@ trx_sysf_create( ...@@ -197,7 +197,7 @@ trx_sysf_create(
/* Create the first rollback segment in the SYSTEM tablespace */ /* Create the first rollback segment in the SYSTEM tablespace */
slot_no = trx_sys_rseg_find_free(block); slot_no = trx_sys_rseg_find_free(block);
buf_block_t* rblock = trx_rseg_header_create(fil_system.sys_space, buf_block_t* rblock = trx_rseg_header_create(fil_system.sys_space,
slot_no, block, mtr); slot_no, 0, block, mtr);
ut_a(slot_no == TRX_SYS_SYSTEM_RSEG_ID); ut_a(slot_no == TRX_SYS_SYSTEM_RSEG_ID);
ut_a(rblock->page.id.page_no() == FSP_FIRST_RSEG_PAGE_NO); ut_a(rblock->page.id.page_no() == FSP_FIRST_RSEG_PAGE_NO);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment