mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 02:05:57 +01:00
MDEV-35394 Innochecksum misinterprets freed pages
- Innochecksum misinterprets the freed pages as active one. This leads the user to think there are too many valid pages exist. - To avoid this confusion, innochecksum introduced one more option --skip-freed-pages and -r to avoid the freed pages while dumping or printing the summary of the tablespace. - Innochecksum can safely assume the page is freed if the respective extent doesn't belong to a segment and marked as freed in XDES_BITMAP in extent descriptor page. - Innochecksum shouldn't assume that zero-filled page as extent descriptor page. Reviewed-by: Marko Mäkelä
This commit is contained in:
parent
2255be0395
commit
9ba18d1aa0
6 changed files with 74 additions and 21 deletions
|
@ -75,6 +75,8 @@ static my_bool do_leaf;
|
||||||
static my_bool per_page_details;
|
static my_bool per_page_details;
|
||||||
static ulint n_merge;
|
static ulint n_merge;
|
||||||
static ulint physical_page_size; /* Page size in bytes on disk. */
|
static ulint physical_page_size; /* Page size in bytes on disk. */
|
||||||
|
static ulint extent_size;
|
||||||
|
static ulint xdes_size;
|
||||||
ulong srv_page_size;
|
ulong srv_page_size;
|
||||||
ulong srv_page_size_shift;
|
ulong srv_page_size_shift;
|
||||||
/* Current page number (0 based). */
|
/* Current page number (0 based). */
|
||||||
|
@ -99,7 +101,7 @@ char* log_filename = NULL;
|
||||||
FILE* log_file = NULL;
|
FILE* log_file = NULL;
|
||||||
/* Enabled for log write option. */
|
/* Enabled for log write option. */
|
||||||
static bool is_log_enabled = false;
|
static bool is_log_enabled = false;
|
||||||
|
static bool skip_freed_pages;
|
||||||
static byte field_ref_zero_buf[UNIV_PAGE_SIZE_MAX];
|
static byte field_ref_zero_buf[UNIV_PAGE_SIZE_MAX];
|
||||||
const byte *field_ref_zero = field_ref_zero_buf;
|
const byte *field_ref_zero = field_ref_zero_buf;
|
||||||
|
|
||||||
|
@ -267,6 +269,8 @@ static void init_page_size(const byte* buf)
|
||||||
srv_page_size_shift = UNIV_ZIP_SIZE_SHIFT_MIN - 1 + ssize;
|
srv_page_size_shift = UNIV_ZIP_SIZE_SHIFT_MIN - 1 + ssize;
|
||||||
srv_page_size = 512U << ssize;
|
srv_page_size = 512U << ssize;
|
||||||
physical_page_size = srv_page_size;
|
physical_page_size = srv_page_size;
|
||||||
|
extent_size = FSP_EXTENT_SIZE;
|
||||||
|
xdes_size = XDES_SIZE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,6 +282,8 @@ static void init_page_size(const byte* buf)
|
||||||
|
|
||||||
srv_page_size = fil_space_t::logical_size(flags);
|
srv_page_size = fil_space_t::logical_size(flags);
|
||||||
physical_page_size = fil_space_t::physical_size(flags);
|
physical_page_size = fil_space_t::physical_size(flags);
|
||||||
|
extent_size = FSP_EXTENT_SIZE;
|
||||||
|
xdes_size = XDES_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -555,8 +561,8 @@ bool
|
||||||
is_page_doublewritebuffer(
|
is_page_doublewritebuffer(
|
||||||
const byte* page)
|
const byte* page)
|
||||||
{
|
{
|
||||||
if ((cur_page_num >= FSP_EXTENT_SIZE)
|
if ((cur_page_num >= extent_size)
|
||||||
&& (cur_page_num < FSP_EXTENT_SIZE * 3)) {
|
&& (cur_page_num < extent_size * 3)) {
|
||||||
/* page is doublewrite buffer. */
|
/* page is doublewrite buffer. */
|
||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
|
@ -757,8 +763,8 @@ static inline bool is_page_free(const byte *xdes, ulint physical_page_size,
|
||||||
{
|
{
|
||||||
const byte *des=
|
const byte *des=
|
||||||
xdes + XDES_ARR_OFFSET +
|
xdes + XDES_ARR_OFFSET +
|
||||||
XDES_SIZE * ((page_no & (physical_page_size - 1)) / FSP_EXTENT_SIZE);
|
xdes_size * ((page_no & (physical_page_size - 1)) / extent_size);
|
||||||
return xdes_is_free(des, page_no % FSP_EXTENT_SIZE);
|
return xdes_is_free(des, page_no % extent_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -786,6 +792,16 @@ parse_page(
|
||||||
|
|
||||||
/* Check whether page is doublewrite buffer. */
|
/* Check whether page is doublewrite buffer. */
|
||||||
str = skip_page ? "Double_write_buffer" : "-";
|
str = skip_page ? "Double_write_buffer" : "-";
|
||||||
|
page_no = mach_read_from_4(page + FIL_PAGE_OFFSET);
|
||||||
|
if (skip_freed_pages) {
|
||||||
|
const byte *des= xdes + XDES_ARR_OFFSET +
|
||||||
|
xdes_size * ((page_no & (physical_page_size - 1))
|
||||||
|
/ extent_size);
|
||||||
|
if (mach_read_from_4(des) != XDES_FSEG &&
|
||||||
|
xdes_is_free(des, page_no % extent_size)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (fil_page_get_type(page)) {
|
switch (fil_page_get_type(page)) {
|
||||||
|
|
||||||
|
@ -1211,6 +1227,9 @@ static struct my_option innochecksum_options[] = {
|
||||||
&do_leaf, &do_leaf, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
&do_leaf, &do_leaf, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||||
{"merge", 'm', "leaf page count if merge given number of consecutive pages",
|
{"merge", 'm', "leaf page count if merge given number of consecutive pages",
|
||||||
&n_merge, &n_merge, 0, GET_ULONG, REQUIRED_ARG, 0, 0, (longlong)10L, 0, 1, 0},
|
&n_merge, &n_merge, 0, GET_ULONG, REQUIRED_ARG, 0, 0, (longlong)10L, 0, 1, 0},
|
||||||
|
{"skip-freed-pages", 'r', "skip freed pages for the tablespace",
|
||||||
|
&skip_freed_pages, &skip_freed_pages, 0, GET_BOOL, NO_ARG,
|
||||||
|
0, 0, 0, 0, 0, 0},
|
||||||
|
|
||||||
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
|
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
@ -1234,7 +1253,7 @@ static void usage(void)
|
||||||
print_version();
|
print_version();
|
||||||
puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
|
puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
|
||||||
printf("InnoDB offline file checksum utility.\n");
|
printf("InnoDB offline file checksum utility.\n");
|
||||||
printf("Usage: %s [-c] [-s <start page>] [-e <end page>] "
|
printf("Usage: %s [-c] [-r] [-s <start page>] [-e <end page>] "
|
||||||
"[-p <page>] [-i] [-v] [-a <allow mismatches>] [-n] "
|
"[-p <page>] [-i] [-v] [-a <allow mismatches>] [-n] "
|
||||||
"[-S] [-D <page type dump>] "
|
"[-S] [-D <page type dump>] "
|
||||||
"[-l <log>] [-l] [-m <merge pages>] <filename or [-]>\n", my_progname);
|
"[-l <log>] [-l] [-m <merge pages>] <filename or [-]>\n", my_progname);
|
||||||
|
@ -1247,8 +1266,8 @@ static void usage(void)
|
||||||
extern "C" my_bool
|
extern "C" my_bool
|
||||||
innochecksum_get_one_option(
|
innochecksum_get_one_option(
|
||||||
const struct my_option *opt,
|
const struct my_option *opt,
|
||||||
const char *argument MY_ATTRIBUTE((unused)),
|
const char *IF_DBUG(argument,),
|
||||||
const char *)
|
const char *)
|
||||||
{
|
{
|
||||||
switch (opt->id) {
|
switch (opt->id) {
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
|
@ -1273,15 +1292,6 @@ innochecksum_get_one_option(
|
||||||
my_end(0);
|
my_end(0);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
break;
|
break;
|
||||||
case 'n':
|
|
||||||
no_check = true;
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
case 'S':
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
do_write = true;
|
|
||||||
break;
|
|
||||||
case 'D':
|
case 'D':
|
||||||
page_type_dump = true;
|
page_type_dump = true;
|
||||||
break;
|
break;
|
||||||
|
@ -1328,8 +1338,8 @@ get_options(
|
||||||
*/
|
*/
|
||||||
static bool check_encryption(const char* filename, const byte* page)
|
static bool check_encryption(const char* filename, const byte* page)
|
||||||
{
|
{
|
||||||
ulint offset = FSP_HEADER_OFFSET + XDES_ARR_OFFSET + XDES_SIZE *
|
ulint offset = FSP_HEADER_OFFSET + XDES_ARR_OFFSET + xdes_size *
|
||||||
physical_page_size / FSP_EXTENT_SIZE;
|
physical_page_size / extent_size;
|
||||||
|
|
||||||
if (memcmp(page + offset, CRYPT_MAGIC, MAGIC_SZ) != 0) {
|
if (memcmp(page + offset, CRYPT_MAGIC, MAGIC_SZ) != 0) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1861,7 +1871,7 @@ first_non_zero:
|
||||||
printf("page " UINT32PF " ", cur_page_num);
|
printf("page " UINT32PF " ", cur_page_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page_get_page_no(buf) % physical_page_size == 0) {
|
if (cur_page_num % physical_page_size == 0) {
|
||||||
memcpy(xdes, buf, physical_page_size);
|
memcpy(xdes, buf, physical_page_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
mysql-test/suite/innodb/r/innochecksum_undo_page.result
Normal file
10
mysql-test/suite/innodb/r/innochecksum_undo_page.result
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
SET GLOBAL INNODB_FILE_PER_TABLE= 0;
|
||||||
|
CREATE TABLE t1(f1 INT NOT NULL)ENGINE=InnoDB;
|
||||||
|
INSERT INTO t1 VALUES(1);
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET GLOBAL innodb_fast_shutdown=0;
|
||||||
|
# Run the innochecksum to display undo log pages
|
||||||
|
FOUND 1 /Undo page state: 0 active, [0-9]+ cached, [0-9]+ to_purge, [0-9]+ prepared, [0-9]+ other/ in result.log
|
||||||
|
# Run the innochecksum with --skip-freed-pages
|
||||||
|
FOUND 1 /Undo page state: 0 active, 0 cached, 0 to_purge, 0 prepared, 0 other/ in result.log
|
||||||
|
# restart
|
1
mysql-test/suite/innodb/t/innochecksum_undo_page.opt
Normal file
1
mysql-test/suite/innodb/t/innochecksum_undo_page.opt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
--innodb_undo_tablespaces=0
|
27
mysql-test/suite/innodb/t/innochecksum_undo_page.test
Normal file
27
mysql-test/suite/innodb/t/innochecksum_undo_page.test
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
--source include/have_innodb.inc
|
||||||
|
--source include/not_embedded.inc
|
||||||
|
let MYSQLD_DATADIR= `SELECT @@datadir`;
|
||||||
|
|
||||||
|
SET GLOBAL INNODB_FILE_PER_TABLE= 0;
|
||||||
|
CREATE TABLE t1(f1 INT NOT NULL)ENGINE=InnoDB;
|
||||||
|
INSERT INTO t1 VALUES(1);
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET GLOBAL innodb_fast_shutdown=0;
|
||||||
|
--source include/shutdown_mysqld.inc
|
||||||
|
|
||||||
|
--echo # Run the innochecksum to display undo log pages
|
||||||
|
let $resultlog=$MYSQLTEST_VARDIR/tmp/result.log;
|
||||||
|
let SEARCH_FILE = $MYSQLTEST_VARDIR/tmp/result.log;
|
||||||
|
let SEARCH_ABORT = NOT FOUND;
|
||||||
|
exec $INNOCHECKSUM -S $MYSQLD_DATADIR/ibdata1 > $resultlog;
|
||||||
|
# Expected > 0 cached undo log pages, but can't guarantee it because
|
||||||
|
# the writes of freed pages may be optimized while flushing
|
||||||
|
let SEARCH_PATTERN= Undo page state: 0 active, [0-9]+ cached, [0-9]+ to_purge, [0-9]+ prepared, [0-9]+ other;
|
||||||
|
--source include/search_pattern_in_file.inc
|
||||||
|
|
||||||
|
--echo # Run the innochecksum with --skip-freed-pages
|
||||||
|
exec $INNOCHECKSUM -S -r $MYSQLD_DATADIR/ibdata1 > $resultlog;
|
||||||
|
let SEARCH_PATTERN= Undo page state: 0 active, 0 cached, 0 to_purge, 0 prepared, 0 other;
|
||||||
|
--source include/search_pattern_in_file.inc
|
||||||
|
--remove_file $resultlog
|
||||||
|
--source include/start_mysqld.inc
|
|
@ -34,6 +34,7 @@ per-page-details FALSE
|
||||||
log (No default value)
|
log (No default value)
|
||||||
leaf FALSE
|
leaf FALSE
|
||||||
merge 0
|
merge 0
|
||||||
|
skip-freed-pages FALSE
|
||||||
[1]:# check the both short and long options for "help"
|
[1]:# check the both short and long options for "help"
|
||||||
[2]:# Run the innochecksum when file isn't provided.
|
[2]:# Run the innochecksum when file isn't provided.
|
||||||
# It will print the innochecksum usage similar to --help option.
|
# It will print the innochecksum usage similar to --help option.
|
||||||
|
@ -41,7 +42,7 @@ innochecksum Ver #.#.#
|
||||||
Copyright (c) YEAR, YEAR , Oracle, MariaDB Corporation Ab and others.
|
Copyright (c) YEAR, YEAR , Oracle, MariaDB Corporation Ab and others.
|
||||||
|
|
||||||
InnoDB offline file checksum utility.
|
InnoDB offline file checksum utility.
|
||||||
Usage: innochecksum [-c] [-s <start page>] [-e <end page>] [-p <page>] [-i] [-v] [-a <allow mismatches>] [-n] [-S] [-D <page type dump>] [-l <log>] [-l] [-m <merge pages>] <filename or [-]>
|
Usage: innochecksum [-c] [-r] [-s <start page>] [-e <end page>] [-p <page>] [-i] [-v] [-a <allow mismatches>] [-n] [-S] [-D <page type dump>] [-l <log>] [-l] [-m <merge pages>] <filename or [-]>
|
||||||
See https://mariadb.com/kb/en/library/innochecksum/ for usage hints.
|
See https://mariadb.com/kb/en/library/innochecksum/ for usage hints.
|
||||||
-?, --help Displays this help and exits.
|
-?, --help Displays this help and exits.
|
||||||
-I, --info Synonym for --help.
|
-I, --info Synonym for --help.
|
||||||
|
@ -66,6 +67,8 @@ See https://mariadb.com/kb/en/library/innochecksum/ for usage hints.
|
||||||
-f, --leaf Examine leaf index pages
|
-f, --leaf Examine leaf index pages
|
||||||
-m, --merge=# leaf page count if merge given number of consecutive
|
-m, --merge=# leaf page count if merge given number of consecutive
|
||||||
pages
|
pages
|
||||||
|
-r, --skip-freed-pages
|
||||||
|
skip freed pages for the tablespace
|
||||||
|
|
||||||
Variables (--variable-name=value)
|
Variables (--variable-name=value)
|
||||||
and boolean options {FALSE|TRUE} Value (after reading options)
|
and boolean options {FALSE|TRUE} Value (after reading options)
|
||||||
|
@ -84,6 +87,7 @@ per-page-details FALSE
|
||||||
log (No default value)
|
log (No default value)
|
||||||
leaf FALSE
|
leaf FALSE
|
||||||
merge 0
|
merge 0
|
||||||
|
skip-freed-pages FALSE
|
||||||
[3]:# check the both short and long options for "count" and exit
|
[3]:# check the both short and long options for "count" and exit
|
||||||
Number of pages:#
|
Number of pages:#
|
||||||
Number of pages:#
|
Number of pages:#
|
||||||
|
|
|
@ -139,6 +139,7 @@ per-page-details FALSE
|
||||||
log (No default value)
|
log (No default value)
|
||||||
leaf FALSE
|
leaf FALSE
|
||||||
merge 0
|
merge 0
|
||||||
|
skip-freed-pages FALSE
|
||||||
[5]: Page type dump for with shortform for tab1.ibd
|
[5]: Page type dump for with shortform for tab1.ibd
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue