mirror of
https://github.com/MariaDB/server.git
synced 2025-04-21 06:35:31 +02:00
MDEV-34529 Shrink the system tablespace when system tablespace contains MDEV-30671 leaked undo pages
- InnoDB fails to shrink the system tablespace when it contains the leaked undo log pages caused by MDEV-30671. - InnoDB does free the unused segment in system tablespace before shrinking the tablespace. InnoDB fails to free the unused segment if XA PREPARE transaction exist or if the previous shutdown was not with innodb_fast_shutdown=0 inode_info: Structure to store the inode page and offsets. fil_space_t::garbage_collect(): Frees the system tablespace unused segment fsp_get_sys_used_segment(): Iterates through all default file segment and index segment present in system tablespace. trx_sys_t::is_xa_exist(): Returns true if the XA transaction exist in the undo logs fseg_inode_free(): Frees the extents, fragment pages for the given index node and ignores any error similar to trx_purge_free_segment() trx_sys_t::reset_page(): Retain the TRX_SYS_FSEG_HEADER value in trx_sys page while resetting the page.
This commit is contained in:
parent
66b8d32b75
commit
4a1ded61a4
20 changed files with 917 additions and 155 deletions
mysql-test/suite/innodb
r
t
storage/innobase
|
@ -1,5 +1,6 @@
|
|||
FLUSH TABLES;
|
||||
call mtr.add_suppression("Found 1 prepared XA transactions");
|
||||
call mtr.add_suppression("InnoDB: Cannot free the unused segments in system tablespace because a previous shutdown was not with innodb_fast_shutdown=0 or XA PREPARE transactions exist");
|
||||
#
|
||||
# MDEV-13797 InnoDB may hang if shutdown is initiated soon after startup
|
||||
# while rolling back recovered incomplete transactions
|
||||
|
|
|
@ -19,13 +19,20 @@ InnoDB 0 transactions not purged
|
|||
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE = 0;
|
||||
NAME FILE_SIZE
|
||||
innodb_system 540016640
|
||||
# restart: --debug_dbug=+d,shrink_buffer_pool_full
|
||||
# restart: --debug_dbug=+d,traversal_extent_fail
|
||||
FOUND 1 /\[Warning\] InnoDB: Cannot shrink the system tablespace/ in mysqld.1.err
|
||||
SELECT * FROM INFORMATION_SCHEMA.ENGINES
|
||||
WHERE engine = 'innodb'
|
||||
AND support IN ('YES', 'DEFAULT', 'ENABLED');
|
||||
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
|
||||
InnoDB YES Supports transactions, row-level locking, foreign keys and encryption for tables YES YES YES
|
||||
# restart: --debug_dbug=+d,shrink_buffer_pool_full
|
||||
FOUND 2 /\[Warning\] InnoDB: Cannot shrink the system tablespace/ in mysqld.1.err
|
||||
SELECT * FROM INFORMATION_SCHEMA.ENGINES
|
||||
WHERE engine = 'innodb'
|
||||
AND support IN ('YES', 'DEFAULT', 'ENABLED');
|
||||
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
|
||||
InnoDB YES Supports transactions, row-level locking, foreign keys and encryption for tables YES YES YES
|
||||
# restart: --debug_dbug=+d,mtr_log_max_size
|
||||
FOUND 1 /\[ERROR\] InnoDB: Cannot shrink the system tablespace/ in mysqld.1.err
|
||||
SELECT * FROM INFORMATION_SCHEMA.ENGINES
|
||||
|
|
25
mysql-test/suite/innodb/r/undo_leak.result
Normal file
25
mysql-test/suite/innodb/r/undo_leak.result
Normal file
|
@ -0,0 +1,25 @@
|
|||
# restart: --debug_dbug=d,undo_segment_leak
|
||||
SET GLOBAL INNODB_FILE_PER_TABLE=0;
|
||||
Warnings:
|
||||
Warning 1287 '@@innodb_file_per_table' is deprecated and will be removed in a future release
|
||||
CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL)ENGINE=InnoDB;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
UPDATE t1 SET f1 = f1 + 1 WHERE f1 > 1000;
|
||||
UPDATE t1 SET f2 = f2 + 1 WHERE f1 > 1000;
|
||||
UPDATE t1 SET f1 = f2 + 1 WHERE f1 > 1000;
|
||||
UPDATE t1 SET f2 = f1 + 1 WHERE f1 > 1000;
|
||||
DELETE FROM t1;
|
||||
DROP TABLE t1;
|
||||
set GLOBAL innodb_fast_shutdown=0;
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
||||
NAME FILE_SIZE
|
||||
innodb_system 79691776
|
||||
# restart
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
||||
NAME FILE_SIZE
|
||||
innodb_system 12582912
|
94
mysql-test/suite/innodb/r/undo_leak_fail.result
Normal file
94
mysql-test/suite/innodb/r/undo_leak_fail.result
Normal file
|
@ -0,0 +1,94 @@
|
|||
call mtr.add_suppression("InnoDB: Cannot free the unused segments in system tablespace because a previous shutdown was not with innodb_fast_shutdown=0");
|
||||
call mtr.add_suppression("InnoDB: :autoshrink failed to read the used segment");
|
||||
call mtr.add_suppression("InnoDB: :autoshrink failed due to .* in FSP_SEG_INODES_FULL list");
|
||||
call mtr.add_suppression("InnoDB: :autoshrink failed due to .* in FSP_SEG_INODES_FREE list");
|
||||
call mtr.add_suppression("InnoDB: :autoshrink failed to free the segment");
|
||||
call mtr.add_suppression("Found .* prepared XA transactions");
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
||||
NAME FILE_SIZE
|
||||
innodb_system 10485760
|
||||
SET GLOBAL INNODB_FILE_PER_TABLE=0;
|
||||
Warnings:
|
||||
Warning 1287 '@@innodb_file_per_table' is deprecated and will be removed in a future release
|
||||
CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL)STATS_PERSISTENT=0, ENGINE=InnoDB;
|
||||
XA START 'x';
|
||||
insert into t1 values (1, 1);
|
||||
XA END 'x';
|
||||
XA PREPARE 'x';
|
||||
set GLOBAL innodb_fast_shutdown=0;
|
||||
# restart
|
||||
# Fail to free the segment due to XA PREPARE transaction
|
||||
FOUND 2 /InnoDB: Cannot free the unused segments in system tablespace because a previous shutdown was not with innodb_fast_shutdown=0/ in mysqld.1.err
|
||||
XA COMMIT 'x';
|
||||
DROP TABLE t1;
|
||||
# restart: --debug_dbug=d,undo_segment_leak
|
||||
SET GLOBAL INNODB_FILE_PER_TABLE=0;
|
||||
Warnings:
|
||||
Warning 1287 '@@innodb_file_per_table' is deprecated and will be removed in a future release
|
||||
CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL)STATS_PERSISTENT=0, ENGINE=InnoDB;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
UPDATE t1 SET f1 = f1 + 1 WHERE f1 > 1000;
|
||||
UPDATE t1 SET f2 = f2 + 1 WHERE f1 > 1000;
|
||||
UPDATE t1 SET f1 = f2 + 1 WHERE f1 > 1000;
|
||||
UPDATE t1 SET f2 = f1 + 1 WHERE f1 > 1000;
|
||||
DELETE FROM t1;
|
||||
DROP TABLE t1;
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
||||
NAME FILE_SIZE
|
||||
innodb_system 77594624
|
||||
# restart: --debug_dbug=d,unused_undo_free_fail_1
|
||||
# Fail to free the segment due to previous shutdown
|
||||
FOUND 4 /InnoDB: Cannot free the unused segments in system tablespace because a previous shutdown was not with innodb_fast_shutdown=0/ in mysqld.1.err
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
||||
NAME FILE_SIZE
|
||||
innodb_system 15728640
|
||||
SET GLOBAL innodb_fast_shutdown= 0;
|
||||
# Fail to free the segment while finding the used segments
|
||||
# restart: --debug_dbug=d,unused_undo_free_fail_2
|
||||
SELECT IF(file_size>10485760,'ok',file_size) FROM information_schema.innodb_sys_tablespaces WHERE space=0;
|
||||
IF(file_size>10485760,'ok',file_size)
|
||||
ok
|
||||
FOUND 1 /InnoDB: :autoshrink failed to read the used segment/ in mysqld.1.err
|
||||
FOUND 1 /InnoDB: :autoshrink failed due to .* in FSP_SEG_INODES_FULL list/ in mysqld.1.err
|
||||
SET GLOBAL innodb_fast_shutdown= 0;
|
||||
# Fail to free the segment while finding the used segments
|
||||
# restart: --debug_dbug=d,unused_undo_free_fail_3
|
||||
SELECT IF(file_size>10485760,'ok',file_size) FROM information_schema.innodb_sys_tablespaces WHERE space=0;
|
||||
IF(file_size>10485760,'ok',file_size)
|
||||
ok
|
||||
FOUND 1 /InnoDB: :autoshrink failed due to .* in FSP_SEG_INODES_FREE list/ in mysqld.1.err
|
||||
SET GLOBAL innodb_fast_shutdown= 0;
|
||||
# Fail to free the segment while freeing the unused segments
|
||||
# restart: --debug_dbug=d,unused_undo_free_fail_4
|
||||
SELECT IF(file_size>10485760,'ok',file_size) FROM information_schema.innodb_sys_tablespaces WHERE space=0;
|
||||
IF(file_size>10485760,'ok',file_size)
|
||||
ok
|
||||
FOUND 1 /InnoDB: :autoshrink failed to free the segment .* in page .*/ in mysqld.1.err
|
||||
SET GLOBAL innodb_fast_shutdown= 0;
|
||||
# Fail to free the segment while freeing the used segments
|
||||
# restart: --debug_dbug=d,unused_undo_free_fail_5
|
||||
SELECT IF(file_size>10485760,'ok',file_size) FROM information_schema.innodb_sys_tablespaces WHERE space=0;
|
||||
IF(file_size>10485760,'ok',file_size)
|
||||
ok
|
||||
FOUND 1 /InnoDB: :autoshrink failed to free the segment .* in page .*/ in mysqld.1.err
|
||||
SET GLOBAL innodb_fast_shutdown= 0;
|
||||
# restart
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
||||
NAME FILE_SIZE
|
||||
innodb_system 10485760
|
||||
# Fail to reset the TRX_SYS_FSEG_HEADER during undo tablespace
|
||||
# reinitialization. garbage_collect() shouldn't free the
|
||||
# TRX_SYS_FSEG_HEADER index node
|
||||
set global innodb_fast_shutdown=0;
|
||||
# restart: --innodb_undo_tablespaces=2 --debug_dbug=d,sys_fseg_header_fail
|
||||
FOUND 1 /InnoDB: :autoshrink freed the segment .* in page .*/ in mysqld.1.err
|
||||
set global innodb_fast_shutdown=0;
|
||||
# restart
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
||||
NAME FILE_SIZE
|
||||
innodb_system 10485760
|
|
@ -6,6 +6,7 @@
|
|||
# Flush any open myisam tables from previous tests
|
||||
FLUSH TABLES;
|
||||
call mtr.add_suppression("Found 1 prepared XA transactions");
|
||||
call mtr.add_suppression("InnoDB: Cannot free the unused segments in system tablespace because a previous shutdown was not with innodb_fast_shutdown=0 or XA PREPARE transactions exist");
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-13797 InnoDB may hang if shutdown is initiated soon after startup
|
||||
|
|
|
@ -23,6 +23,18 @@ DROP TABLE t1;
|
|||
--source include/wait_all_purged.inc
|
||||
SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE = 0;
|
||||
|
||||
# Corruption during traversal of extent
|
||||
let $restart_parameters=--debug_dbug=+d,traversal_extent_fail;
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
--let SEARCH_PATTERN= \[Warning\] InnoDB: Cannot shrink the system tablespace
|
||||
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
|
||||
--source include/search_pattern_in_file.inc
|
||||
|
||||
SELECT * FROM INFORMATION_SCHEMA.ENGINES
|
||||
WHERE engine = 'innodb'
|
||||
AND support IN ('YES', 'DEFAULT', 'ENABLED');
|
||||
|
||||
# Ran out of buffer pool
|
||||
let $restart_parameters=--debug_dbug=+d,shrink_buffer_pool_full;
|
||||
--source include/restart_mysqld.inc
|
||||
|
|
2
mysql-test/suite/innodb/t/undo_leak.opt
Normal file
2
mysql-test/suite/innodb/t/undo_leak.opt
Normal file
|
@ -0,0 +1,2 @@
|
|||
--innodb_undo_tablespaces=0
|
||||
--innodb_sys_tablespaces
|
26
mysql-test/suite/innodb/t/undo_leak.test
Normal file
26
mysql-test/suite/innodb/t/undo_leak.test
Normal file
|
@ -0,0 +1,26 @@
|
|||
--source include/have_innodb.inc
|
||||
--source include/have_sequence.inc
|
||||
--source include/have_debug.inc
|
||||
--source include/not_embedded.inc
|
||||
|
||||
let $restart_parameters=--debug_dbug=d,undo_segment_leak;
|
||||
--source include/restart_mysqld.inc
|
||||
SET GLOBAL INNODB_FILE_PER_TABLE=0;
|
||||
CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL)ENGINE=InnoDB;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
UPDATE t1 SET f1 = f1 + 1 WHERE f1 > 1000;
|
||||
UPDATE t1 SET f2 = f2 + 1 WHERE f1 > 1000;
|
||||
UPDATE t1 SET f1 = f2 + 1 WHERE f1 > 1000;
|
||||
UPDATE t1 SET f2 = f1 + 1 WHERE f1 > 1000;
|
||||
DELETE FROM t1;
|
||||
DROP TABLE t1;
|
||||
set GLOBAL innodb_fast_shutdown=0;
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
||||
let $restart_parameters=;
|
||||
--source include/restart_mysqld.inc
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
3
mysql-test/suite/innodb/t/undo_leak_fail.opt
Normal file
3
mysql-test/suite/innodb/t/undo_leak_fail.opt
Normal file
|
@ -0,0 +1,3 @@
|
|||
--innodb_data_file_path=ibdata1:10M:autoextend:autoshrink
|
||||
--innodb_undo_tablespaces=0
|
||||
--innodb_sys_tablespaces
|
119
mysql-test/suite/innodb/t/undo_leak_fail.test
Normal file
119
mysql-test/suite/innodb/t/undo_leak_fail.test
Normal file
|
@ -0,0 +1,119 @@
|
|||
--source include/have_innodb.inc
|
||||
--source include/have_sequence.inc
|
||||
--source include/have_debug.inc
|
||||
--source include/not_embedded.inc
|
||||
|
||||
call mtr.add_suppression("InnoDB: Cannot free the unused segments in system tablespace because a previous shutdown was not with innodb_fast_shutdown=0");
|
||||
call mtr.add_suppression("InnoDB: :autoshrink failed to read the used segment");
|
||||
call mtr.add_suppression("InnoDB: :autoshrink failed due to .* in FSP_SEG_INODES_FULL list");
|
||||
call mtr.add_suppression("InnoDB: :autoshrink failed due to .* in FSP_SEG_INODES_FREE list");
|
||||
call mtr.add_suppression("InnoDB: :autoshrink failed to free the segment");
|
||||
call mtr.add_suppression("Found .* prepared XA transactions");
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
||||
|
||||
SET GLOBAL INNODB_FILE_PER_TABLE=0;
|
||||
CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL)STATS_PERSISTENT=0, ENGINE=InnoDB;
|
||||
XA START 'x';
|
||||
insert into t1 values (1, 1);
|
||||
XA END 'x';
|
||||
XA PREPARE 'x';
|
||||
set GLOBAL innodb_fast_shutdown=0;
|
||||
--source include/restart_mysqld.inc
|
||||
--echo # Fail to free the segment due to XA PREPARE transaction
|
||||
let SEARCH_PATTERN= InnoDB: Cannot free the unused segments in system tablespace because a previous shutdown was not with innodb_fast_shutdown=0;
|
||||
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
|
||||
--source include/search_pattern_in_file.inc
|
||||
XA COMMIT 'x';
|
||||
DROP TABLE t1;
|
||||
|
||||
let $restart_parameters=--debug_dbug=d,undo_segment_leak;
|
||||
--source include/restart_mysqld.inc
|
||||
SET GLOBAL INNODB_FILE_PER_TABLE=0;
|
||||
CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL)STATS_PERSISTENT=0, ENGINE=InnoDB;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
INSERT INTO t1 SELECT seq, seq FROM seq_1_to_4096;
|
||||
UPDATE t1 SET f1 = f1 + 1 WHERE f1 > 1000;
|
||||
UPDATE t1 SET f2 = f2 + 1 WHERE f1 > 1000;
|
||||
UPDATE t1 SET f1 = f2 + 1 WHERE f1 > 1000;
|
||||
UPDATE t1 SET f2 = f1 + 1 WHERE f1 > 1000;
|
||||
DELETE FROM t1;
|
||||
DROP TABLE t1;
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
||||
let $shutdown_timeout=0;
|
||||
let $restart_parameters=--debug_dbug=d,unused_undo_free_fail_1;
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
let $shutdown_timeout=;
|
||||
--echo # Fail to free the segment due to previous shutdown
|
||||
--let SEARCH_PATTERN= InnoDB: Cannot free the unused segments in system tablespace because a previous shutdown was not with innodb_fast_shutdown=0
|
||||
|
||||
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
|
||||
--source include/search_pattern_in_file.inc
|
||||
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
||||
SET GLOBAL innodb_fast_shutdown= 0;
|
||||
|
||||
--echo # Fail to free the segment while finding the used segments
|
||||
let $restart_parameters=--debug_dbug=d,unused_undo_free_fail_2;
|
||||
--source include/restart_mysqld.inc
|
||||
SELECT IF(file_size>10485760,'ok',file_size) FROM information_schema.innodb_sys_tablespaces WHERE space=0;
|
||||
let SEARCH_PATTERN= InnoDB: :autoshrink failed to read the used segment;
|
||||
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
|
||||
--source include/search_pattern_in_file.inc
|
||||
|
||||
let SEARCH_PATTERN= InnoDB: :autoshrink failed due to .* in FSP_SEG_INODES_FULL list;
|
||||
--source include/search_pattern_in_file.inc
|
||||
SET GLOBAL innodb_fast_shutdown= 0;
|
||||
|
||||
--echo # Fail to free the segment while finding the used segments
|
||||
let $restart_parameters=--debug_dbug=d,unused_undo_free_fail_3;
|
||||
--source include/restart_mysqld.inc
|
||||
SELECT IF(file_size>10485760,'ok',file_size) FROM information_schema.innodb_sys_tablespaces WHERE space=0;
|
||||
|
||||
let SEARCH_PATTERN= InnoDB: :autoshrink failed due to .* in FSP_SEG_INODES_FREE list;
|
||||
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
|
||||
--source include/search_pattern_in_file.inc
|
||||
SET GLOBAL innodb_fast_shutdown= 0;
|
||||
|
||||
--echo # Fail to free the segment while freeing the unused segments
|
||||
let $restart_parameters=--debug_dbug=d,unused_undo_free_fail_4;
|
||||
--source include/restart_mysqld.inc
|
||||
SELECT IF(file_size>10485760,'ok',file_size) FROM information_schema.innodb_sys_tablespaces WHERE space=0;
|
||||
|
||||
let SEARCH_PATTERN= InnoDB: :autoshrink failed to free the segment .* in page .*;
|
||||
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
|
||||
--source include/search_pattern_in_file.inc
|
||||
|
||||
SET GLOBAL innodb_fast_shutdown= 0;
|
||||
|
||||
--echo # Fail to free the segment while freeing the used segments
|
||||
let $restart_parameters=--debug_dbug=d,unused_undo_free_fail_5;
|
||||
--source include/restart_mysqld.inc
|
||||
SELECT IF(file_size>10485760,'ok',file_size) FROM information_schema.innodb_sys_tablespaces WHERE space=0;
|
||||
|
||||
let SEARCH_PATTERN= InnoDB: :autoshrink failed to free the segment .* in page .*;
|
||||
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
|
||||
--source include/search_pattern_in_file.inc
|
||||
|
||||
SET GLOBAL innodb_fast_shutdown= 0;
|
||||
let $restart_parameters=;
|
||||
--source include/restart_mysqld.inc
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
||||
|
||||
--echo # Fail to reset the TRX_SYS_FSEG_HEADER during undo tablespace
|
||||
--echo # reinitialization. garbage_collect() shouldn't free the
|
||||
--echo # TRX_SYS_FSEG_HEADER index node
|
||||
set global innodb_fast_shutdown=0;
|
||||
let $restart_parameters=--innodb_undo_tablespaces=2 --debug_dbug=d,sys_fseg_header_fail;
|
||||
--source include/restart_mysqld.inc
|
||||
let SEARCH_PATTERN= InnoDB: :autoshrink freed the segment .* in page .*;
|
||||
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
|
||||
--source include/search_pattern_in_file.inc
|
||||
let $restart_parameters=;
|
||||
set global innodb_fast_shutdown=0;
|
||||
--source include/restart_mysqld.inc
|
||||
SELECT NAME, FILE_SIZE FROM information_schema.innodb_sys_tablespaces WHERE SPACE = 0;
|
|
@ -172,36 +172,24 @@ name_of_col_is(
|
|||
}
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
/********************************************************************//**
|
||||
This function gets the next system table record as it scans the table.
|
||||
@return the next record if found, NULL if end of scan */
|
||||
static
|
||||
const rec_t*
|
||||
dict_getnext_system_low(
|
||||
/*====================*/
|
||||
btr_pcur_t* pcur, /*!< in/out: persistent cursor to the
|
||||
record*/
|
||||
mtr_t* mtr) /*!< in: the mini-transaction */
|
||||
dict_getnext_system_low(btr_pcur_t *pcur, mtr_t *mtr)
|
||||
{
|
||||
rec_t* rec = NULL;
|
||||
|
||||
while (!rec) {
|
||||
btr_pcur_move_to_next_user_rec(pcur, mtr);
|
||||
|
||||
rec = btr_pcur_get_rec(pcur);
|
||||
|
||||
if (!btr_pcur_is_on_user_rec(pcur)) {
|
||||
/* end of index */
|
||||
btr_pcur_close(pcur);
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get a record, let's save the position */
|
||||
btr_pcur_store_position(pcur, mtr);
|
||||
|
||||
return(rec);
|
||||
rec_t *rec = nullptr;
|
||||
while (!rec)
|
||||
{
|
||||
btr_pcur_move_to_next_user_rec(pcur, mtr);
|
||||
rec = btr_pcur_get_rec(pcur);
|
||||
if (!btr_pcur_is_on_user_rec(pcur))
|
||||
{
|
||||
/* end of index */
|
||||
btr_pcur_close(pcur);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
/* Get a record, let's save the position */
|
||||
btr_pcur_store_position(pcur, mtr);
|
||||
return rec;
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
|
|
|
@ -25,11 +25,8 @@ Created 11/29/1995 Heikki Tuuri
|
|||
***********************************************************************/
|
||||
|
||||
#include "fsp0fsp.h"
|
||||
#include "buf0buf.h"
|
||||
#include "fil0fil.h"
|
||||
#include "fil0crypt.h"
|
||||
#include "mtr0log.h"
|
||||
#include "ut0byte.h"
|
||||
#include "page0page.h"
|
||||
#include "srv0srv.h"
|
||||
#include "srv0start.h"
|
||||
|
@ -37,12 +34,16 @@ Created 11/29/1995 Heikki Tuuri
|
|||
#include "btr0sea.h"
|
||||
#include "dict0boot.h"
|
||||
#include "log0log.h"
|
||||
#include "dict0load.h"
|
||||
#include "dict0mem.h"
|
||||
#include "fsp0types.h"
|
||||
#include "btr0pcur.h"
|
||||
#include "trx0sys.h"
|
||||
#include "log.h"
|
||||
#ifndef DBUG_OFF
|
||||
# include "trx0purge.h"
|
||||
#endif
|
||||
#include <unordered_set>
|
||||
#include "trx0undo.h"
|
||||
|
||||
/** Returns the first extent descriptor for a segment.
|
||||
We think of the extent lists of the segment catenated in the order
|
||||
|
@ -1477,19 +1478,19 @@ MY_ATTRIBUTE((nonnull))
|
|||
@param[in,out] inode segment inode
|
||||
@param[in,out] iblock segment inode page
|
||||
@param[in,out] mtr mini-transaction */
|
||||
static void fsp_free_seg_inode(fil_space_t *space, fseg_inode_t *inode,
|
||||
buf_block_t *iblock, mtr_t *mtr)
|
||||
static dberr_t fsp_free_seg_inode(fil_space_t *space, fseg_inode_t *inode,
|
||||
buf_block_t *iblock, mtr_t *mtr)
|
||||
{
|
||||
ut_d(space->modify_check(*mtr));
|
||||
|
||||
dberr_t err;
|
||||
buf_block_t *header= fsp_get_header(space, mtr, &err);
|
||||
if (!header)
|
||||
return;
|
||||
return err;
|
||||
if (UNIV_UNLIKELY(memcmp(FSEG_MAGIC_N_BYTES, FSEG_MAGIC_N + inode, 4)))
|
||||
{
|
||||
space->set_corrupted();
|
||||
return;
|
||||
return DB_CORRUPTION;
|
||||
}
|
||||
|
||||
const ulint physical_size= space->physical_size();
|
||||
|
@ -1499,24 +1500,27 @@ static void fsp_free_seg_inode(fil_space_t *space, fseg_inode_t *inode,
|
|||
physical_size))
|
||||
{
|
||||
/* Move the page to another list */
|
||||
if (flst_remove(header, FSP_HEADER_OFFSET + FSP_SEG_INODES_FULL,
|
||||
iblock, FSEG_INODE_PAGE_NODE, limit, mtr) != DB_SUCCESS)
|
||||
return;
|
||||
if (flst_add_last(header, FSP_HEADER_OFFSET + FSP_SEG_INODES_FREE,
|
||||
iblock, FSEG_INODE_PAGE_NODE, limit, mtr) != DB_SUCCESS)
|
||||
return;
|
||||
err= flst_remove(header, FSP_HEADER_OFFSET + FSP_SEG_INODES_FULL,
|
||||
iblock, FSEG_INODE_PAGE_NODE, limit, mtr);
|
||||
if (err == DB_SUCCESS)
|
||||
err= flst_add_last(header, FSP_HEADER_OFFSET + FSP_SEG_INODES_FREE,
|
||||
iblock, FSEG_INODE_PAGE_NODE, limit, mtr);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
mtr->memset(iblock, page_offset(inode) + FSEG_ID, FSEG_INODE_SIZE, 0);
|
||||
|
||||
if (ULINT_UNDEFINED != fsp_seg_inode_page_find_used(iblock->page.frame,
|
||||
physical_size))
|
||||
return;
|
||||
return DB_SUCCESS;
|
||||
|
||||
/* There are no other used headers left on the page: free it */
|
||||
if (flst_remove(header, FSP_HEADER_OFFSET + FSP_SEG_INODES_FREE,
|
||||
iblock, FSEG_INODE_PAGE_NODE, limit, mtr) == DB_SUCCESS)
|
||||
fsp_free_page(space, iblock->page.id().page_no(), mtr);
|
||||
err= flst_remove(header, FSP_HEADER_OFFSET + FSP_SEG_INODES_FREE,
|
||||
iblock, FSEG_INODE_PAGE_NODE, limit, mtr);
|
||||
if (err != DB_SUCCESS)
|
||||
return err;
|
||||
return fsp_free_page(space, iblock->page.id().page_no(), mtr);
|
||||
}
|
||||
|
||||
MY_ATTRIBUTE((nonnull(1,4,5), warn_unused_result))
|
||||
|
@ -2510,20 +2514,21 @@ try_to_extend:
|
|||
|
||||
MY_ATTRIBUTE((nonnull, warn_unused_result))
|
||||
/** Frees a single page of a segment.
|
||||
@param[in] seg_inode segment inode
|
||||
@param[in,out] space tablespace
|
||||
@param[in] offset page number
|
||||
@param[in] seg_inode segment inode
|
||||
@param[in,out] iblock block where segment inode are kept
|
||||
@param[in,out] mtr mini-transaction
|
||||
@param[in] offset page number
|
||||
@param[in] ahi Drop adaptive hash index
|
||||
@return error code */
|
||||
static
|
||||
dberr_t
|
||||
fseg_free_page_low(
|
||||
fil_space_t* space,
|
||||
fseg_inode_t* seg_inode,
|
||||
buf_block_t* iblock,
|
||||
fil_space_t* space,
|
||||
uint32_t offset,
|
||||
mtr_t* mtr
|
||||
mtr_t* mtr,
|
||||
uint32_t offset
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
,bool ahi=false
|
||||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
|
@ -2656,7 +2661,7 @@ dberr_t fseg_free_page(fseg_header_t *seg_header, fil_space_t *space,
|
|||
{
|
||||
if (!space->full_crc32())
|
||||
fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr);
|
||||
return fseg_free_page_low(seg_inode, iblock, space, offset, mtr);
|
||||
return fseg_free_page_low(space, seg_inode, iblock, mtr, offset);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
@ -2812,6 +2817,71 @@ remove:
|
|||
return DB_SUCCESS;
|
||||
}
|
||||
|
||||
/** Free the extent and fragment page associated with
|
||||
the segment.
|
||||
@param space tablespace where segment resides
|
||||
@param inode index node information
|
||||
@param iblock page where segment header are placed
|
||||
@param mtr mini-transaction
|
||||
@param hdr_page_no segment header page number field
|
||||
@param ahi adaptive hash index
|
||||
@return DB_SUCCESS_LOCKED_REC when freeing wasn't completed
|
||||
@return DB_SUCCESS or other error code when freeing was completed */
|
||||
static
|
||||
dberr_t fseg_free_step_low(fil_space_t *space, fseg_inode_t *inode,
|
||||
buf_block_t *iblock, mtr_t *mtr,
|
||||
const byte *hdr_page_no
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
, bool ahi=false
|
||||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
)
|
||||
{
|
||||
dberr_t err= DB_SUCCESS;
|
||||
if (xdes_t *descr= fseg_get_first_extent(inode, space, mtr, &err))
|
||||
{
|
||||
err= fseg_free_extent(inode, iblock, space,
|
||||
xdes_get_offset(descr), mtr
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
, ahi
|
||||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
);
|
||||
return err == DB_SUCCESS ? DB_SUCCESS_LOCKED_REC : err;
|
||||
}
|
||||
|
||||
if (err != DB_SUCCESS)
|
||||
return err;
|
||||
|
||||
/* Free a fragment page. If there are no fragment pages
|
||||
exist in the array then free the file segment inode */
|
||||
ulint n = fseg_find_last_used_frag_page_slot(inode);
|
||||
if (UNIV_UNLIKELY(n == ULINT_UNDEFINED))
|
||||
return hdr_page_no
|
||||
? DB_SUCCESS
|
||||
: fsp_free_seg_inode(space, inode, iblock, mtr);
|
||||
|
||||
if (hdr_page_no && !memcmp_aligned<2>(hdr_page_no + FIL_PAGE_OFFSET,
|
||||
inode + FSEG_FRAG_ARR +
|
||||
n * FSEG_FRAG_SLOT_SIZE, 4))
|
||||
/* header_page_no is only passed by fseg_free_step_not_header().
|
||||
In that case, the header page must be preserved, to be freed
|
||||
when we're finally called by fseg_free_step(). */
|
||||
return DB_SUCCESS;
|
||||
|
||||
uint32_t page_no= fseg_get_nth_frag_page_no(inode, n);
|
||||
err= fseg_free_page_low(space, inode, iblock, mtr, page_no
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
, ahi
|
||||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
);
|
||||
if (err != DB_SUCCESS)
|
||||
return err;
|
||||
buf_page_free(space, page_no, mtr);
|
||||
if (!hdr_page_no &&
|
||||
fseg_find_last_used_frag_page_slot(inode) == ULINT_UNDEFINED)
|
||||
return fsp_free_seg_inode(space, inode, iblock, mtr);
|
||||
return DB_SUCCESS_LOCKED_REC;
|
||||
}
|
||||
|
||||
/** Frees part of a segment. This function can be used to free
|
||||
a segment by repeatedly calling this function in different
|
||||
mini-transactions. Doing the freeing in a single mini-transaction
|
||||
|
@ -2831,7 +2901,6 @@ fseg_free_step(
|
|||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
)
|
||||
{
|
||||
ulint n;
|
||||
fseg_inode_t* inode;
|
||||
|
||||
const uint32_t space_id = page_get_space_id(page_align(header));
|
||||
|
@ -2864,54 +2933,11 @@ fseg_free_step(
|
|||
fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr);
|
||||
}
|
||||
|
||||
dberr_t err;
|
||||
descr = fseg_get_first_extent(inode, space, mtr, &err);
|
||||
|
||||
if (descr) {
|
||||
/* Free the extent held by the segment */
|
||||
return fseg_free_extent(inode, iblock, space,
|
||||
xdes_get_offset(descr), mtr
|
||||
return fseg_free_step_low(space, inode, iblock, mtr, nullptr
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
, ahi
|
||||
, ahi
|
||||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
) != DB_SUCCESS;
|
||||
}
|
||||
|
||||
if (err != DB_SUCCESS || space->is_stopping()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Free a frag page */
|
||||
n = fseg_find_last_used_frag_page_slot(inode);
|
||||
|
||||
if (n == ULINT_UNDEFINED) {
|
||||
/* Freeing completed: free the segment inode */
|
||||
fsp_free_seg_inode(space, inode, iblock, mtr);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t page_no = fseg_get_nth_frag_page_no(inode, n);
|
||||
|
||||
if (fseg_free_page_low(inode, iblock, space, page_no, mtr
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
, ahi
|
||||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
) != DB_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
buf_page_free(space, page_no, mtr);
|
||||
|
||||
n = fseg_find_last_used_frag_page_slot(inode);
|
||||
|
||||
if (n == ULINT_UNDEFINED) {
|
||||
/* Freeing completed: free the segment inode */
|
||||
fsp_free_seg_inode(space, inode, iblock, mtr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
) != DB_SUCCESS_LOCKED_REC;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -2923,16 +2949,16 @@ fseg_free_step_not_header(
|
|||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
)
|
||||
{
|
||||
fseg_inode_t* inode;
|
||||
|
||||
const uint32_t space_id = page_get_space_id(page_align(header));
|
||||
const page_t *page = page_align(header);
|
||||
const uint32_t space_id = page_get_space_id(page);
|
||||
ut_ad(mtr->is_named_space(space_id));
|
||||
|
||||
fil_space_t* space = mtr->x_lock_space(space_id);
|
||||
buf_block_t* iblock;
|
||||
|
||||
inode = fseg_inode_try_get(header, space_id, space->zip_size(),
|
||||
mtr, &iblock);
|
||||
fseg_inode_t *inode = fseg_inode_try_get(header, space_id,
|
||||
space->zip_size(),
|
||||
mtr, &iblock);
|
||||
if (space->is_stopping()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -2940,7 +2966,7 @@ fseg_free_step_not_header(
|
|||
if (!inode) {
|
||||
ib::warn() << "Double free of "
|
||||
<< page_id_t(space_id,
|
||||
page_get_page_no(page_align(header)));
|
||||
page_get_page_no(page));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2948,43 +2974,11 @@ fseg_free_step_not_header(
|
|||
fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr);
|
||||
}
|
||||
|
||||
dberr_t err;
|
||||
if (xdes_t* descr = fseg_get_first_extent(inode, space, mtr, &err)) {
|
||||
/* Free the extent held by the segment */
|
||||
return fseg_free_extent(inode, iblock, space,
|
||||
xdes_get_offset(descr),
|
||||
mtr
|
||||
return fseg_free_step_low(space, inode, iblock, mtr, page
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
, ahi
|
||||
, ahi
|
||||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
) != DB_SUCCESS;
|
||||
} else if (err != DB_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Free a frag page */
|
||||
|
||||
ulint n = fseg_find_last_used_frag_page_slot(inode);
|
||||
|
||||
if (UNIV_UNLIKELY(n == ULINT_UNDEFINED)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t page_no = fseg_get_nth_frag_page_no(inode, n);
|
||||
|
||||
if (page_no == page_get_page_no(page_align(header))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fseg_free_page_low(inode, iblock, space, page_no, mtr
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
, ahi
|
||||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
) != DB_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
buf_page_free(space, page_no, mtr);
|
||||
return false;
|
||||
) != DB_SUCCESS_LOCKED_REC;
|
||||
}
|
||||
|
||||
/** Returns the first extent descriptor for a segment.
|
||||
|
@ -3577,22 +3571,463 @@ dberr_t fsp_sys_tablespace_validate()
|
|||
}
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
void fsp_system_tablespace_truncate()
|
||||
/** Store the inode information which basically stores
|
||||
the page and offset */
|
||||
struct inode_info : private std::unordered_set<uint64_t>
|
||||
{
|
||||
public:
|
||||
/** Register an inode
|
||||
@param page index node page
|
||||
@param offset index node offset within the page
|
||||
@retval true in case of successful registeration
|
||||
@retval false in case of invalid entry or already inserted inode */
|
||||
__attribute__((warn_unused_result))
|
||||
bool insert_inode(uint32_t page, uint16_t offset)
|
||||
{
|
||||
return page < fil_system.sys_space->free_limit &&
|
||||
offset >= FIL_PAGE_DATA && offset < srv_page_size - FIL_PAGE_DATA_END &&
|
||||
emplace(uint64_t{page} << 32 | offset).second;
|
||||
}
|
||||
|
||||
/** Register an inode
|
||||
@param inode index node information
|
||||
@retval true in case of successful registeration
|
||||
@retval false in case of invalid entry or already inserted inode */
|
||||
__attribute__((warn_unused_result))
|
||||
bool insert_seg(const byte *inode)
|
||||
{
|
||||
return insert_inode(mach_read_from_4(inode + 4),
|
||||
mach_read_from_2(inode + 8));
|
||||
}
|
||||
|
||||
__attribute__((warn_unused_result))
|
||||
bool find(uint32_t page, uint16_t offset) const
|
||||
{
|
||||
return std::unordered_set<uint64_t>::find(uint64_t{page} << 32 |
|
||||
offset) != end();
|
||||
}
|
||||
|
||||
/** Get the unused inode segment header from the list of index
|
||||
node pages.
|
||||
@param boffset offset for the FSP_SEG_INODES_FULL
|
||||
or FSP_SEG_INODES_FREE list in fsp header page
|
||||
@param unused store the unused information
|
||||
@return error code */
|
||||
dberr_t get_unused(uint16_t boffset, inode_info *unused) const
|
||||
{
|
||||
dberr_t err= DB_SUCCESS;
|
||||
buf_block_t *block= buf_pool.page_fix(page_id_t{0, 0}, &err,
|
||||
buf_pool_t::FIX_WAIT_READ);
|
||||
if (!block)
|
||||
return err;
|
||||
buf_block_t *header= block;
|
||||
const uint32_t len= flst_get_len(block->page.frame + boffset);
|
||||
fil_addr_t addr= flst_get_first(block->page.frame + boffset);
|
||||
ulint n_inode_per_page=
|
||||
FSP_SEG_INODES_PER_PAGE(fil_system.sys_space->physical_size());
|
||||
for (uint32_t i= len; i--; )
|
||||
{
|
||||
if (addr.boffset < FIL_PAGE_DATA ||
|
||||
addr.boffset >= block->physical_size() - FIL_PAGE_DATA_END)
|
||||
{
|
||||
err= DB_CORRUPTION;
|
||||
break;
|
||||
}
|
||||
|
||||
block= buf_pool.page_fix(page_id_t{0, addr.page}, &err,
|
||||
buf_pool_t::FIX_WAIT_READ);
|
||||
if (!block)
|
||||
break;
|
||||
|
||||
fil_addr_t next_addr= flst_get_next_addr(block->page.frame +
|
||||
addr.boffset);
|
||||
for (uint32_t i= 0; i < n_inode_per_page; i++)
|
||||
{
|
||||
const fseg_inode_t *inode=
|
||||
fsp_seg_inode_page_get_nth_inode(block->page.frame, i);
|
||||
ulint seg_id= mach_read_from_8(FSEG_ID + inode);
|
||||
/* Consider TRX_SYS_FSEG_HEADER as used segment.
|
||||
While reinitializing the undo tablespace, InnoDB
|
||||
fail to reset the value of TRX_SYS_FSEG_HEADER
|
||||
in TRX_SYS page. so InnoDB shouldn't consider
|
||||
this segment as unused one */
|
||||
if (seg_id == 0 || seg_id == 2)
|
||||
continue;
|
||||
uint16_t offset= uint16_t(inode - block->page.frame);
|
||||
if (offset < FIL_PAGE_DATA ||
|
||||
offset >= block->physical_size() - FIL_PAGE_DATA_END)
|
||||
{
|
||||
err= DB_CORRUPTION;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!find(addr.page, offset) &&
|
||||
!unused->insert_inode(addr.page, offset))
|
||||
{
|
||||
err= DB_DUPLICATE_KEY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
addr= next_addr;
|
||||
block->page.unfix();
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
ut_ad(addr.page == FIL_NULL || err != DB_SUCCESS);
|
||||
header->page.unfix();
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Free the segment information present in the set
|
||||
@return error code */
|
||||
dberr_t free_segs();
|
||||
};
|
||||
|
||||
/** Get the file segments from root page
|
||||
@param inodes store the index nodes information
|
||||
@param root root page
|
||||
@return error code */
|
||||
static dberr_t fsp_table_inodes_root(inode_info *inodes, uint32_t root)
|
||||
{
|
||||
if (root == FIL_NULL)
|
||||
return DB_SUCCESS;
|
||||
|
||||
dberr_t err= DB_SUCCESS;
|
||||
buf_block_t *block= buf_pool.page_fix(page_id_t{0, root}, &err,
|
||||
buf_pool_t::FIX_WAIT_READ);
|
||||
if (!block)
|
||||
return err;
|
||||
|
||||
if (!inodes->insert_seg(block->page.frame + PAGE_HEADER + PAGE_BTR_SEG_TOP))
|
||||
err= DB_CORRUPTION;
|
||||
|
||||
if (!inodes->insert_seg(block->page.frame + PAGE_HEADER + PAGE_BTR_SEG_LEAF))
|
||||
err= DB_CORRUPTION;
|
||||
|
||||
block->page.unfix();
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Add the file segment of all root pages in table
|
||||
@param inodes store the index nodes information
|
||||
@param table table to be read
|
||||
@return error code */
|
||||
static dberr_t add_index_root_pages(inode_info *inodes, dict_table_t *table)
|
||||
{
|
||||
dberr_t err= DB_SUCCESS;
|
||||
for (auto i= UT_LIST_GET_FIRST(table->indexes);
|
||||
i != nullptr && err == DB_SUCCESS; i= UT_LIST_GET_NEXT(indexes, i))
|
||||
err= fsp_table_inodes_root(inodes, i->page);
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Determine the inodes used by tables in the system tablespace.
|
||||
@param inodes store the index nodes information
|
||||
@param mtr mini-transaction
|
||||
@return error code */
|
||||
static dberr_t fsp_table_inodes(inode_info *inodes, mtr_t *mtr)
|
||||
{
|
||||
btr_pcur_t pcur;
|
||||
ulint len;
|
||||
const auto savepoint= mtr->get_savepoint();
|
||||
dberr_t err= DB_SUCCESS;
|
||||
dict_sys.freeze(SRW_LOCK_CALL);
|
||||
for (const rec_t *rec= dict_startscan_system(&pcur, mtr,
|
||||
dict_sys.sys_indexes);
|
||||
rec; rec= dict_getnext_system_low(&pcur, mtr))
|
||||
{
|
||||
const byte *field=
|
||||
rec_get_nth_field_old(rec, DICT_FLD__SYS_INDEXES__SPACE, &len);
|
||||
if (len != 4)
|
||||
{
|
||||
err= DB_CORRUPTION;
|
||||
break;
|
||||
}
|
||||
uint32_t space= mach_read_from_4(field);
|
||||
if (space > 0) continue;
|
||||
|
||||
field= rec_get_nth_field_old(rec, DICT_FLD__SYS_INDEXES__PAGE_NO, &len);
|
||||
if (len != 4)
|
||||
{
|
||||
err= DB_CORRUPTION;
|
||||
break;
|
||||
}
|
||||
err= fsp_table_inodes_root(inodes, mach_read_from_4(field));
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
mtr->rollback_to_savepoint(savepoint);
|
||||
dict_sys.unfreeze();
|
||||
|
||||
if (err == DB_SUCCESS)
|
||||
err= add_index_root_pages(inodes, dict_sys.sys_tables);
|
||||
if (err == DB_SUCCESS)
|
||||
err= add_index_root_pages(inodes, dict_sys.sys_indexes);
|
||||
if (err == DB_SUCCESS)
|
||||
err= add_index_root_pages(inodes, dict_sys.sys_columns);
|
||||
if (err == DB_SUCCESS)
|
||||
err= add_index_root_pages(inodes, dict_sys.sys_fields);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Get the used inode from the system tablespace
|
||||
@param inodes inode information used found in system tablespace
|
||||
@param mtr mini-transaction
|
||||
@return error code */
|
||||
static dberr_t fsp_get_sys_used_segment(inode_info *inodes, mtr_t *mtr)
|
||||
{
|
||||
dberr_t err= DB_SUCCESS;
|
||||
buf_block_t *block= nullptr;
|
||||
/* Get TRX_SYS_FSEG_HEADER, TRX_SYS_DOUBLEWRITE_FSEG from
|
||||
TRX_SYS_PAGE */
|
||||
block= buf_pool.page_fix(page_id_t{0, TRX_SYS_PAGE_NO}, &err,
|
||||
buf_pool_t::FIX_WAIT_READ);
|
||||
if (!block)
|
||||
return err;
|
||||
|
||||
fil_addr_t sys_fseg_addr= flst_read_addr(block->page.frame +
|
||||
TRX_SYS + TRX_SYS_FSEG_HEADER + 4);
|
||||
if (sys_fseg_addr.page == 0 && sys_fseg_addr.boffset == 0)
|
||||
{
|
||||
/* While reinitializing the undo tablespace, InnoDB fail
|
||||
to reset the TRX_SYS_FSEG_HEADER offset in TRX_SYS page */
|
||||
}
|
||||
else if (!inodes->insert_inode(sys_fseg_addr.page, sys_fseg_addr.boffset))
|
||||
err= DB_CORRUPTION;
|
||||
|
||||
if (!inodes->insert_seg(block->page.frame + TRX_SYS_DOUBLEWRITE +
|
||||
TRX_SYS_DOUBLEWRITE_FSEG))
|
||||
err= DB_CORRUPTION;
|
||||
|
||||
block->page.unfix();
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
block= buf_pool.page_fix(page_id_t{0, DICT_HDR_PAGE_NO}, &err,
|
||||
buf_pool_t::FIX_WAIT_READ);
|
||||
if (!block)
|
||||
return err;
|
||||
|
||||
if (!inodes->insert_seg(block->page.frame + DICT_HDR + DICT_HDR_FSEG_HEADER))
|
||||
err= DB_CORRUPTION;
|
||||
|
||||
block->page.unfix();
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
block= buf_pool.page_fix(page_id_t{0, FSP_IBUF_HEADER_PAGE_NO},
|
||||
&err, buf_pool_t::FIX_WAIT_READ);
|
||||
if (!block)
|
||||
return err;
|
||||
if (!inodes->insert_seg(block->page.frame + PAGE_DATA))
|
||||
err= DB_CORRUPTION;
|
||||
|
||||
block->page.unfix();
|
||||
|
||||
/* Get rollback segment header page */
|
||||
for (ulint rseg_id= 0; rseg_id < TRX_SYS_N_RSEGS && err == DB_SUCCESS;
|
||||
rseg_id++)
|
||||
{
|
||||
trx_rseg_t *rseg= &trx_sys.rseg_array[rseg_id];
|
||||
if (rseg->space->id == 0)
|
||||
{
|
||||
block= buf_pool.page_fix(rseg->page_id(), &err,
|
||||
buf_pool_t::FIX_WAIT_READ);
|
||||
if (!block)
|
||||
break;
|
||||
if (!inodes->insert_seg(block->page.frame + TRX_RSEG +
|
||||
TRX_RSEG_FSEG_HEADER))
|
||||
err= DB_CORRUPTION;
|
||||
block->page.unfix();
|
||||
|
||||
/* Even after slow shutdown, there is a possiblity that
|
||||
cached undo log can exist. So store the segment as used one */
|
||||
for (trx_undo_t *undo= UT_LIST_GET_FIRST(rseg->undo_cached);
|
||||
undo && err == DB_SUCCESS;
|
||||
undo= UT_LIST_GET_NEXT(undo_list, undo))
|
||||
{
|
||||
block= buf_pool.page_fix(page_id_t{0, undo->hdr_page_no}, &err,
|
||||
buf_pool_t::FIX_WAIT_READ);
|
||||
if (!block)
|
||||
return err;
|
||||
if (!inodes->insert_seg(block->page.frame + TRX_UNDO_SEG_HDR +
|
||||
TRX_UNDO_FSEG_HEADER))
|
||||
err= DB_CORRUPTION;
|
||||
block->page.unfix();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (err == DB_SUCCESS)
|
||||
err= fsp_table_inodes(inodes, mtr);
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Free the extents, fragment page from the given inode
|
||||
@param page_no index node page number
|
||||
@param offset index node offset within page
|
||||
@return error code */
|
||||
static dberr_t fseg_inode_free(uint32_t page_no, uint16_t offset)
|
||||
{
|
||||
fil_space_t *space= fil_system.sys_space;
|
||||
dberr_t err= DB_SUCCESS;
|
||||
mtr_t mtr;
|
||||
mtr.start();
|
||||
mtr.x_lock_space(space);
|
||||
buf_block_t *iblock= buf_page_get_gen(page_id_t{0, page_no}, 0,
|
||||
RW_X_LATCH, nullptr, BUF_GET,
|
||||
&mtr, &err);
|
||||
|
||||
fseg_inode_t *inode= nullptr;
|
||||
DBUG_EXECUTE_IF("unused_undo_free_fail_4",
|
||||
iblock= nullptr; err= DB_CORRUPTION;);
|
||||
if (!iblock)
|
||||
goto func_exit;
|
||||
|
||||
inode= iblock->page.frame + offset;
|
||||
while ((err= fseg_free_step_low(space, inode, iblock,
|
||||
&mtr, nullptr)) == DB_SUCCESS_LOCKED_REC)
|
||||
{
|
||||
DBUG_EXECUTE_IF("unused_undo_free_fail_5",
|
||||
err= DB_CORRUPTION;
|
||||
goto func_exit;);
|
||||
iblock->fix();
|
||||
mtr.commit();
|
||||
|
||||
mtr.start();
|
||||
mtr.x_lock_space(space);
|
||||
iblock->page.lock.x_lock();
|
||||
mtr.memo_push(iblock, MTR_MEMO_PAGE_X_FIX);
|
||||
}
|
||||
/* These are all leaked undo log segments. That means there is no
|
||||
way to access these undo log segments other than traversing
|
||||
the index node page. Above fseg_free_step_low() clears
|
||||
the undo segment header page as well. */
|
||||
func_exit:
|
||||
mtr.commit();
|
||||
return err;
|
||||
}
|
||||
|
||||
/** Free the unused segment
|
||||
@return error code */
|
||||
dberr_t inode_info::free_segs()
|
||||
{
|
||||
for (auto i : *this)
|
||||
{
|
||||
uint32_t page= uint32_t(i >> 32);
|
||||
uint16_t offset= uint16_t(i);
|
||||
if (dberr_t err= fseg_inode_free(page, offset))
|
||||
{
|
||||
sql_print_error("InnoDB: :autoshrink failed to free the "
|
||||
"segment %u in page " UINT32PF, unsigned{offset},
|
||||
page);
|
||||
return err;
|
||||
}
|
||||
sql_print_information("InnoDB: :autoshrink freed the segment "
|
||||
"%u in page " UINT32PF, unsigned{offset}, page);
|
||||
}
|
||||
return DB_SUCCESS;
|
||||
}
|
||||
|
||||
bool trx_sys_t::is_xa_exist() noexcept
|
||||
{
|
||||
for (const trx_rseg_t &rseg : trx_sys.rseg_array)
|
||||
{
|
||||
if (rseg.page_no == FIL_NULL)
|
||||
continue;
|
||||
const trx_undo_t *undo= UT_LIST_GET_FIRST(rseg.undo_list);
|
||||
while (undo)
|
||||
{
|
||||
if (undo->state == TRX_UNDO_PREPARED)
|
||||
return true;
|
||||
undo= UT_LIST_GET_NEXT(undo_list, undo);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Remove the unused segment in tablespace. This function
|
||||
used only during shrinking of system tablespace
|
||||
@param shutdown called during slow shutdown
|
||||
@return error code */
|
||||
dberr_t fil_space_t::garbage_collect(bool shutdown)
|
||||
{
|
||||
if ((shutdown && trx_sys_t::is_xa_exist()) ||
|
||||
(!shutdown && !trx_sys.is_undo_empty()))
|
||||
{
|
||||
sql_print_warning("InnoDB: Cannot free the unused segments"
|
||||
" in system tablespace because a previous"
|
||||
" shutdown was not with innodb_fast_shutdown=0"
|
||||
" or XA PREPARE transactions exist");
|
||||
return DB_SUCCESS;
|
||||
}
|
||||
|
||||
ut_a(id == 0);
|
||||
/* Collect all the used segment inode entries */
|
||||
mtr_t mtr;
|
||||
mtr.start();
|
||||
inode_info used_inodes, unused_inodes;
|
||||
dberr_t err= fsp_get_sys_used_segment(&used_inodes, &mtr);
|
||||
DBUG_EXECUTE_IF("unused_undo_free_fail_1", err= DB_CORRUPTION;);
|
||||
if (err)
|
||||
{
|
||||
sql_print_error("InnoDB: :autoshrink failed to read the "
|
||||
"used segment due to %s", ut_strerr(err));
|
||||
mtr.commit();
|
||||
return err;
|
||||
}
|
||||
|
||||
const char *ctx= "in FSP_SEG_INODES_FULL list";
|
||||
err= used_inodes.get_unused(FSP_HEADER_OFFSET + FSP_SEG_INODES_FULL,
|
||||
&unused_inodes);
|
||||
DBUG_EXECUTE_IF("unused_undo_free_fail_2", err= DB_CORRUPTION;);
|
||||
|
||||
if (err == DB_SUCCESS)
|
||||
{
|
||||
ctx= "in FSP_SEG_INODES_FREE list";
|
||||
err= used_inodes.get_unused(FSP_HEADER_OFFSET + FSP_SEG_INODES_FREE,
|
||||
&unused_inodes);
|
||||
DBUG_EXECUTE_IF("unused_undo_free_fail_3", err= DB_CORRUPTION;);
|
||||
}
|
||||
|
||||
mtr.commit();
|
||||
if (err)
|
||||
{
|
||||
sql_print_error("InnoDB: :autoshrink failed due to "
|
||||
"%s %s ", ut_strerr(err), ctx);
|
||||
return err;
|
||||
}
|
||||
|
||||
return unused_inodes.free_segs();
|
||||
}
|
||||
|
||||
void fsp_system_tablespace_truncate(bool shutdown)
|
||||
{
|
||||
ut_ad(!purge_sys.enabled());
|
||||
ut_ad(!srv_undo_sources);
|
||||
uint32_t last_used_extent= 0;
|
||||
fil_space_t *space= fil_system.sys_space;
|
||||
dberr_t err= space->garbage_collect(shutdown);
|
||||
if (err)
|
||||
{
|
||||
srv_sys_space.set_shrink_fail();
|
||||
return;
|
||||
}
|
||||
|
||||
mtr_t mtr;
|
||||
mtr.start();
|
||||
mtr.x_lock_space(space);
|
||||
dberr_t err= fsp_traverse_extents(space, &last_used_extent, &mtr);
|
||||
err= fsp_traverse_extents(space, &last_used_extent, &mtr);
|
||||
DBUG_EXECUTE_IF("traversal_extent_fail", err= DB_CORRUPTION;);
|
||||
if (err != DB_SUCCESS)
|
||||
{
|
||||
func_exit:
|
||||
err_exit:
|
||||
mtr.commit();
|
||||
sql_print_warning("InnoDB: Cannot shrink the system tablespace "
|
||||
"due to %s", ut_strerr(err));
|
||||
mtr.commit();
|
||||
srv_sys_space.set_shrink_fail();
|
||||
return;
|
||||
}
|
||||
uint32_t fixed_size= srv_sys_space.get_min_size(),
|
||||
|
@ -3643,7 +4078,7 @@ func_exit:
|
|||
|
||||
header= fsp_get_latched_xdes_page(0, &mtr, &err);
|
||||
if (!header)
|
||||
goto func_exit;
|
||||
goto err_exit;
|
||||
|
||||
mtr.write<4, mtr_t::FORCED>(
|
||||
*header, FSP_HEADER_OFFSET + FSP_SIZE + header->page.frame,
|
||||
|
@ -3656,16 +4091,16 @@ func_exit:
|
|||
err= fsp_shrink_list(
|
||||
header, FSP_HEADER_OFFSET + FSP_FREE, last_used_extent, &mtr);
|
||||
if (err != DB_SUCCESS)
|
||||
goto func_exit;
|
||||
goto err_exit;
|
||||
|
||||
err= fsp_shrink_list(
|
||||
header, FSP_HEADER_OFFSET + FSP_FREE_FRAG, last_used_extent, &mtr);
|
||||
if (err != DB_SUCCESS)
|
||||
goto func_exit;
|
||||
goto err_exit;
|
||||
|
||||
err= fsp_xdes_reset(space, last_used_extent, &mtr);
|
||||
if (err != DB_SUCCESS)
|
||||
goto func_exit;
|
||||
goto err_exit;
|
||||
|
||||
mtr.trim_pages(page_id_t(0, last_used_extent));
|
||||
size_t shrink_redo_size= mtr.get_log_size();
|
||||
|
|
|
@ -213,4 +213,12 @@ dict_process_sys_foreign_col_rec(
|
|||
in referenced table */
|
||||
ulint* pos); /*!< out: column position */
|
||||
|
||||
/** This function gets the next system table record as it scans
|
||||
the table.
|
||||
@param pcur persistent cursor
|
||||
@param mtr mini-transaction
|
||||
@return the next record if found
|
||||
@retval nullptr at the end of the table */
|
||||
const rec_t*
|
||||
dict_getnext_system_low(btr_pcur_t *pcur, mtr_t *mtr);
|
||||
#endif
|
||||
|
|
|
@ -988,6 +988,10 @@ public:
|
|||
/** Update the data structures on write completion */
|
||||
void complete_write();
|
||||
|
||||
/** Free the unused segment for the tablespace
|
||||
@param shutdown called during slow shutdown
|
||||
@return error code */
|
||||
dberr_t garbage_collect(bool shutdown);
|
||||
private:
|
||||
/** @return whether the file is usable for io() */
|
||||
ATTRIBUTE_COLD bool prepare_acquired();
|
||||
|
|
|
@ -555,8 +555,9 @@ inline void fsp_init_file_page(
|
|||
mtr->init(block);
|
||||
}
|
||||
|
||||
/** Truncate the system tablespace */
|
||||
void fsp_system_tablespace_truncate();
|
||||
/** Truncate the system tablespace
|
||||
@param shutdown Called during shutdown */
|
||||
void fsp_system_tablespace_truncate(bool shutdown);
|
||||
|
||||
#ifndef UNIV_DEBUG
|
||||
# define fsp_init_file_page(space, block, mtr) fsp_init_file_page(block, mtr)
|
||||
|
|
|
@ -174,6 +174,11 @@ public:
|
|||
ulint* sum_new_sizes)
|
||||
MY_ATTRIBUTE((warn_unused_result));
|
||||
|
||||
/** @return whether shrinking failed during
|
||||
previous attempt of system tablespace shrinking */
|
||||
bool is_shrink_fail() noexcept { return m_auto_shrink_fail; }
|
||||
|
||||
void set_shrink_fail() noexcept { m_auto_shrink_fail= true; }
|
||||
private:
|
||||
/** Check the tablespace header for this tablespace.
|
||||
@return DB_SUCCESS or error code */
|
||||
|
@ -271,6 +276,10 @@ private:
|
|||
/** Shrink the system tablespace if the value is
|
||||
enabled */
|
||||
bool m_auto_shrink;
|
||||
|
||||
/** Set to true only when InnoDB system tablespace
|
||||
shrink fails during startup */
|
||||
bool m_auto_shrink_fail;
|
||||
};
|
||||
|
||||
/* GLOBAL OBJECTS */
|
||||
|
|
|
@ -1202,6 +1202,9 @@ public:
|
|||
/** Get the undo log empty value */
|
||||
bool is_undo_empty() const { return !undo_log_nonempty; }
|
||||
|
||||
/** @return whether XA transaction is in PREPARED state */
|
||||
static bool is_xa_exist() noexcept;
|
||||
|
||||
/* Reset the trx_sys page and retain the dblwr information,
|
||||
system rollback segment header page
|
||||
@return error code */
|
||||
|
|
|
@ -1578,7 +1578,8 @@ void srv_purge_shutdown()
|
|||
purge_sys.coordinator_shutdown();
|
||||
srv_shutdown_purge_tasks();
|
||||
if (!srv_fast_shutdown && !high_level_read_only && srv_was_started &&
|
||||
!opt_bootstrap && srv_operation == SRV_OPERATION_NORMAL)
|
||||
fsp_system_tablespace_truncate();
|
||||
!opt_bootstrap && srv_operation == SRV_OPERATION_NORMAL &&
|
||||
!srv_sys_space.is_shrink_fail())
|
||||
fsp_system_tablespace_truncate(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -333,6 +333,15 @@ inline dberr_t trx_sys_t::reset_page(mtr_t *mtr)
|
|||
|
||||
if (!sys_header) return err;
|
||||
|
||||
if (mach_read_from_4(sys_header->page.frame + TRX_SYS +
|
||||
TRX_SYS_FSEG_HEADER) != TRX_SYS_SPACE)
|
||||
return DB_CORRUPTION;
|
||||
|
||||
/* Store the TRX_SYS_FSEG_HEADER page, offset */
|
||||
char fseg_addr[6];
|
||||
memcpy(fseg_addr,
|
||||
sys_header->page.frame + TRX_SYS + TRX_SYS_FSEG_HEADER + 4, 6);
|
||||
|
||||
const bool dblwr_enabled=
|
||||
mach_read_from_4(TRX_SYS_DOUBLEWRITE_MAGIC + TRX_SYS_DOUBLEWRITE +
|
||||
sys_header->page.frame)
|
||||
|
@ -347,6 +356,17 @@ inline dberr_t trx_sys_t::reset_page(mtr_t *mtr)
|
|||
mtr->write<2>(*sys_header, FIL_PAGE_TYPE + sys_header->page.frame,
|
||||
FIL_PAGE_TYPE_TRX_SYS);
|
||||
|
||||
DBUG_EXECUTE_IF("sys_fseg_header_fail",
|
||||
{
|
||||
fseg_addr[4]= 0;
|
||||
fseg_addr[5]= 0;
|
||||
});
|
||||
|
||||
/** Write the TRX_SYS_FSEG_HEADER only if it is not zero-filled */
|
||||
if (!memcmp(fseg_addr + 4, "00", 2))
|
||||
mtr->memcpy(*sys_header,
|
||||
sys_header->page.frame + TRX_SYS + TRX_SYS_FSEG_HEADER + 4,
|
||||
fseg_addr, 6);
|
||||
mtr->write<4>(*sys_header,
|
||||
TRX_SYS + TRX_SYS_RSEGS + TRX_SYS_RSEG_PAGE_NO +
|
||||
sys_header->page.frame, FSP_FIRST_RSEG_PAGE_NO);
|
||||
|
@ -1781,7 +1801,7 @@ dberr_t srv_start(bool create_new_db)
|
|||
|
||||
if (!high_level_read_only
|
||||
&& srv_sys_space.can_auto_shrink()) {
|
||||
fsp_system_tablespace_truncate();
|
||||
fsp_system_tablespace_truncate(false);
|
||||
DBUG_EXECUTE_IF("crash_after_sys_truncate",
|
||||
return srv_init_abort(DB_ERROR););
|
||||
}
|
||||
|
|
|
@ -482,6 +482,7 @@ loop:
|
|||
free_segment:
|
||||
ut_ad(rseg.curr_size >= seg_size);
|
||||
rseg.curr_size-= seg_size;
|
||||
DBUG_EXECUTE_IF("undo_segment_leak", goto skip_purge_free;);
|
||||
trx_purge_free_segment(rseg_hdr, b, mtr);
|
||||
break;
|
||||
case TRX_UNDO_CACHED:
|
||||
|
@ -509,7 +510,9 @@ loop:
|
|||
goto free_segment;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
skip_purge_free:
|
||||
#endif /* !DBUG_OFF */
|
||||
hdr_addr= prev_hdr_addr;
|
||||
|
||||
mtr.commit();
|
||||
|
|
Loading…
Add table
Reference in a new issue