mirror of
https://github.com/MariaDB/server.git
synced 2026-04-19 14:55:32 +02:00
MDEV-35830 Fix innodb_undo_log_truncate in backup
recv_sys_t::parse(): Correctly handle the storing==BACKUP case, and simplify some logic around storing==YES as well. The added test mariabackup.undo_truncate is based on an idea of Thirunarayanan Balathandayuthapani. It nondeterministically (not on every run) covers this logic, including the function backup_undo_trunc(), for both innodb_encrypt_log=ON and innodb_encrypt_log=OFF. Reviewed by: Debarun Banerjee
This commit is contained in:
parent
aa35f62f1c
commit
46aaf328ce
5 changed files with 157 additions and 51 deletions
4
mysql-test/suite/mariabackup/undo_truncate.combinations
Normal file
4
mysql-test/suite/mariabackup/undo_truncate.combinations
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
[clear]
|
||||||
|
--innodb-encrypt-log=OFF
|
||||||
|
[crypt]
|
||||||
|
--innodb-encrypt-log=ON
|
||||||
6
mysql-test/suite/mariabackup/undo_truncate.opt
Normal file
6
mysql-test/suite/mariabackup/undo_truncate.opt
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
--innodb-undo-tablespaces=2
|
||||||
|
--plugin-load-add=$FILE_KEY_MANAGEMENT_SO
|
||||||
|
--loose-file-key-management
|
||||||
|
--loose-file-key-management-filekey=FILE:$MTR_SUITE_DIR/filekeys-data.key
|
||||||
|
--loose-file-key-management-filename=$MTR_SUITE_DIR/filekeys-data.enc
|
||||||
|
--loose-file-key-management-encryption-algorithm=aes_cbc
|
||||||
39
mysql-test/suite/mariabackup/undo_truncate.result
Normal file
39
mysql-test/suite/mariabackup/undo_truncate.result
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
SET GLOBAL innodb_undo_log_truncate = 0;
|
||||||
|
create table t1 (keyc int primary key default 0, c char(6)) engine=innodb;
|
||||||
|
create table t2 (keyc int primary key default 0, c char(6)) engine=innodb;
|
||||||
|
CREATE PROCEDURE p(t VARCHAR(64))
|
||||||
|
BEGIN
|
||||||
|
DECLARE i TEXT DEFAULT 'insert into t1 select seq,repeat(chr(48),6)
|
||||||
|
from seq_1_to_20000';
|
||||||
|
DECLARE u1 TEXT DEFAULT 'update t1 set c=repeat(chr(32),6)';
|
||||||
|
DECLARE u2 TEXT DEFAULT 'update t1 set c=repeat(chr(64),6)';
|
||||||
|
EXECUTE IMMEDIATE REPLACE(i,'t1', t);
|
||||||
|
EXECUTE IMMEDIATE REPLACE(u1,'t1', t);
|
||||||
|
EXECUTE IMMEDIATE REPLACE(u2,'t1', t);
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
connect con1,localhost,root,,;
|
||||||
|
begin;
|
||||||
|
call p('t1');
|
||||||
|
connection default;
|
||||||
|
call p('t2');
|
||||||
|
connection con1;
|
||||||
|
commit;
|
||||||
|
disconnect con1;
|
||||||
|
connection default;
|
||||||
|
DROP PROCEDURE p;
|
||||||
|
SET GLOBAL innodb_undo_log_truncate = 1;
|
||||||
|
SET GLOBAL innodb_max_undo_log_size=DEFAULT;
|
||||||
|
SET GLOBAL innodb_max_purge_lag_wait=0;
|
||||||
|
# Prepare full backup
|
||||||
|
# shutdown server
|
||||||
|
# remove datadir
|
||||||
|
# xtrabackup move back
|
||||||
|
# restart
|
||||||
|
select count(*) from t1;
|
||||||
|
count(*)
|
||||||
|
20000
|
||||||
|
select count(*) from t2;
|
||||||
|
count(*)
|
||||||
|
20000
|
||||||
|
DROP TABLE t1,t2;
|
||||||
59
mysql-test/suite/mariabackup/undo_truncate.test
Normal file
59
mysql-test/suite/mariabackup/undo_truncate.test
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
--source include/have_innodb.inc
|
||||||
|
--source include/not_embedded.inc
|
||||||
|
--source include/have_sequence.inc
|
||||||
|
--source include/have_file_key_management.inc
|
||||||
|
|
||||||
|
SET GLOBAL innodb_undo_log_truncate = 0;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Perform DML action using multiple clients and multiple undo tablespace.
|
||||||
|
#
|
||||||
|
|
||||||
|
create table t1 (keyc int primary key default 0, c char(6)) engine=innodb;
|
||||||
|
create table t2 (keyc int primary key default 0, c char(6)) engine=innodb;
|
||||||
|
|
||||||
|
DELIMITER $$;
|
||||||
|
CREATE PROCEDURE p(t VARCHAR(64))
|
||||||
|
BEGIN
|
||||||
|
DECLARE i TEXT DEFAULT 'insert into t1 select seq,repeat(chr(48),6)
|
||||||
|
from seq_1_to_20000';
|
||||||
|
DECLARE u1 TEXT DEFAULT 'update t1 set c=repeat(chr(32),6)';
|
||||||
|
DECLARE u2 TEXT DEFAULT 'update t1 set c=repeat(chr(64),6)';
|
||||||
|
EXECUTE IMMEDIATE REPLACE(i,'t1', t);
|
||||||
|
EXECUTE IMMEDIATE REPLACE(u1,'t1', t);
|
||||||
|
EXECUTE IMMEDIATE REPLACE(u2,'t1', t);
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;$$
|
||||||
|
|
||||||
|
connect (con1,localhost,root,,);
|
||||||
|
begin;
|
||||||
|
send call p('t1');
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
call p('t2');
|
||||||
|
|
||||||
|
connection con1;
|
||||||
|
reap;
|
||||||
|
commit;
|
||||||
|
disconnect con1;
|
||||||
|
connection default;
|
||||||
|
DROP PROCEDURE p;
|
||||||
|
|
||||||
|
SET GLOBAL innodb_undo_log_truncate = 1;
|
||||||
|
SET GLOBAL innodb_max_undo_log_size=DEFAULT;
|
||||||
|
SET GLOBAL innodb_max_purge_lag_wait=0;
|
||||||
|
let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
|
||||||
|
|
||||||
|
--disable_result_log
|
||||||
|
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --parallel=10 --target-dir=$targetdir --throttle=1000;
|
||||||
|
--echo # Prepare full backup
|
||||||
|
exec $XTRABACKUP --prepare --target-dir=$targetdir;
|
||||||
|
--enable_result_log
|
||||||
|
|
||||||
|
source include/restart_and_restore.inc;
|
||||||
|
select count(*) from t1;
|
||||||
|
select count(*) from t2;
|
||||||
|
# Cleanup
|
||||||
|
rmdir $targetdir;
|
||||||
|
DROP TABLE t1,t2;
|
||||||
|
|
@ -2698,43 +2698,57 @@ restart:
|
||||||
mach_write_to_4(iv + 8, space_id);
|
mach_write_to_4(iv + 8, space_id);
|
||||||
mach_write_to_4(iv + 12, page_no);
|
mach_write_to_4(iv + 12, page_no);
|
||||||
}
|
}
|
||||||
got_page_op= !(b & 0x80);
|
|
||||||
if (!got_page_op);
|
|
||||||
else if (storing == BACKUP && srv_operation == SRV_OPERATION_BACKUP)
|
|
||||||
{
|
|
||||||
if (page_no == 0 && (b & 0xf0) == INIT_PAGE && first_page_init)
|
|
||||||
first_page_init(space_id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (storing == YES && file_checkpoint &&
|
|
||||||
space_id != TRX_SYS_SPACE && !srv_is_undo_tablespace(space_id))
|
|
||||||
{
|
|
||||||
recv_spaces_t::iterator i= recv_spaces.lower_bound(space_id);
|
|
||||||
if (i != recv_spaces.end() && i->first == space_id);
|
|
||||||
else if (lsn < file_checkpoint)
|
|
||||||
/* We have not seen all records between the checkpoint and
|
|
||||||
FILE_CHECKPOINT. There should be a FILE_DELETE for this
|
|
||||||
tablespace later. */
|
|
||||||
recv_spaces.emplace_hint(i, space_id, file_name_t("", false));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const page_id_t id(space_id, page_no);
|
|
||||||
if (!srv_force_recovery)
|
|
||||||
{
|
|
||||||
ib::error() << "Missing FILE_DELETE or FILE_MODIFY for " << id
|
|
||||||
<< " at " << lsn
|
|
||||||
<< "; set innodb_force_recovery=1 to ignore the record.";
|
|
||||||
goto corrupted;
|
|
||||||
}
|
|
||||||
ib::warn() << "Ignoring record for " << id << " at " << lsn;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DBUG_PRINT("ib_log",
|
DBUG_PRINT("ib_log",
|
||||||
("scan " LSN_PF ": rec %x len %zu page %u:%u",
|
("scan " LSN_PF ": rec %x len %zu page %u:%u",
|
||||||
lsn, b, l - recs + rlen, space_id, page_no));
|
lsn, b, l - recs + rlen, space_id, page_no));
|
||||||
|
got_page_op= !(b & 0x80);
|
||||||
if (got_page_op)
|
if (got_page_op)
|
||||||
{
|
{
|
||||||
|
if (storing == BACKUP)
|
||||||
|
{
|
||||||
|
if (page_no == 0 && (b & 0xf0) == INIT_PAGE && first_page_init)
|
||||||
|
first_page_init(space_id);
|
||||||
|
else if (rlen == 1 && undo_space_trunc)
|
||||||
|
{
|
||||||
|
mach_write_to_4(iv + 8, space_id);
|
||||||
|
mach_write_to_4(iv + 12, page_no);
|
||||||
|
byte eb[1/*type,length*/ + 5/*space_id*/ + 5/*page_no*/ + 1/*rlen*/];
|
||||||
|
if (*l.copy_if_needed(iv, eb, recs, 1) == TRIM_PAGES)
|
||||||
|
undo_space_trunc(space_id);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (storing == YES && UNIV_LIKELY(space_id != TRX_SYS_SPACE) &&
|
||||||
|
!srv_is_undo_tablespace(space_id))
|
||||||
|
{
|
||||||
|
ut_ad(file_checkpoint != 0);
|
||||||
|
recv_spaces_t::iterator i= recv_spaces.lower_bound(space_id);
|
||||||
|
if (i != recv_spaces.end() && i->first == space_id);
|
||||||
|
else if (lsn < file_checkpoint)
|
||||||
|
/* We have not seen all records between the checkpoint and
|
||||||
|
FILE_CHECKPOINT. There should be a FILE_DELETE for this
|
||||||
|
tablespace later. */
|
||||||
|
recv_spaces.emplace_hint(i, space_id, file_name_t("", false));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!srv_force_recovery)
|
||||||
|
{
|
||||||
|
sql_print_error("InnoDB: Missing FILE_DELETE or FILE_MODIFY for "
|
||||||
|
"[page id: space=" UINT32PF
|
||||||
|
", page number=" UINT32PF "]"
|
||||||
|
" at " LSN_PF
|
||||||
|
"; set innodb_force_recovery=1 to"
|
||||||
|
" ignore the record.",
|
||||||
|
space_id, page_no, lsn);
|
||||||
|
goto corrupted;
|
||||||
|
}
|
||||||
|
sql_print_warning("InnoDB: Ignoring record for "
|
||||||
|
"[page id: space=" UINT32PF
|
||||||
|
", page number=" UINT32PF "] at " LSN_PF,
|
||||||
|
space_id, page_no, lsn);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
same_page:
|
same_page:
|
||||||
if (!rlen);
|
if (!rlen);
|
||||||
else if (UNIV_UNLIKELY(l - recs + rlen > srv_page_size))
|
else if (UNIV_UNLIKELY(l - recs + rlen > srv_page_size))
|
||||||
|
|
@ -2748,13 +2762,11 @@ restart:
|
||||||
case FREE_PAGE:
|
case FREE_PAGE:
|
||||||
ut_ad(freed.emplace(id).second);
|
ut_ad(freed.emplace(id).second);
|
||||||
/* the next record must not be same_page */
|
/* the next record must not be same_page */
|
||||||
if (storing != BACKUP) last_offset= 1;
|
last_offset= 1;
|
||||||
goto free_or_init_page;
|
goto free_or_init_page;
|
||||||
case INIT_PAGE:
|
case INIT_PAGE:
|
||||||
if (storing != BACKUP) last_offset= FIL_PAGE_TYPE;
|
last_offset= FIL_PAGE_TYPE;
|
||||||
free_or_init_page:
|
free_or_init_page:
|
||||||
if (storing == BACKUP)
|
|
||||||
continue;
|
|
||||||
if (UNIV_UNLIKELY(rlen != 0))
|
if (UNIV_UNLIKELY(rlen != 0))
|
||||||
goto record_corrupted;
|
goto record_corrupted;
|
||||||
store_freed_or_init_rec(id, (b & 0x70) == FREE_PAGE);
|
store_freed_or_init_rec(id, (b & 0x70) == FREE_PAGE);
|
||||||
|
|
@ -2799,17 +2811,6 @@ restart:
|
||||||
continue;
|
continue;
|
||||||
if (UNIV_UNLIKELY(!rlen))
|
if (UNIV_UNLIKELY(!rlen))
|
||||||
goto record_corrupted;
|
goto record_corrupted;
|
||||||
if (storing == BACKUP)
|
|
||||||
{
|
|
||||||
if (rlen == 1 && undo_space_trunc)
|
|
||||||
{
|
|
||||||
cl= l.copy_if_needed(iv, decrypt_buf, recs, rlen);
|
|
||||||
if (*cl == TRIM_PAGES)
|
|
||||||
undo_space_trunc(space_id);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
cl= l.copy_if_needed(iv, decrypt_buf, recs, rlen);
|
cl= l.copy_if_needed(iv, decrypt_buf, recs, rlen);
|
||||||
if (rlen == 1 && *cl == TRIM_PAGES)
|
if (rlen == 1 && *cl == TRIM_PAGES)
|
||||||
{
|
{
|
||||||
|
|
@ -2823,10 +2824,9 @@ restart:
|
||||||
trim({space_id, 0}, start_lsn);
|
trim({space_id, 0}, start_lsn);
|
||||||
truncated_undo_spaces[space_id - srv_undo_space_id_start]=
|
truncated_undo_spaces[space_id - srv_undo_space_id_start]=
|
||||||
{ start_lsn, page_no };
|
{ start_lsn, page_no };
|
||||||
if (storing != BACKUP)
|
/* the next record must not be same_page */
|
||||||
/* the next record must not be same_page */
|
last_offset= 1;
|
||||||
last_offset= 1;
|
if (undo_space_trunc)
|
||||||
else if (undo_space_trunc)
|
|
||||||
undo_space_trunc(space_id);
|
undo_space_trunc(space_id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -2850,8 +2850,6 @@ restart:
|
||||||
case WRITE:
|
case WRITE:
|
||||||
case MEMMOVE:
|
case MEMMOVE:
|
||||||
case MEMSET:
|
case MEMSET:
|
||||||
if (storing == BACKUP)
|
|
||||||
continue;
|
|
||||||
if (storing == NO && UNIV_LIKELY(page_no != 0))
|
if (storing == NO && UNIV_LIKELY(page_no != 0))
|
||||||
/* fil_space_set_recv_size_and_flags() is mandatory for storing==NO.
|
/* fil_space_set_recv_size_and_flags() is mandatory for storing==NO.
|
||||||
It is only applicable to page_no == 0. Other than that, we can just
|
It is only applicable to page_no == 0. Other than that, we can just
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue