Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions mysql-test/suite/innodb/r/tablespace_size_warning.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#
# MDEV-38936: Proactive handling of InnoDB tablespace full condition
#
# Test basic warning emission
SET GLOBAL innodb_tablespace_size_warning_threshold = 10485760;
SET GLOBAL innodb_tablespace_size_warning_pct = 70;
CREATE TABLE t1 (
id INT AUTO_INCREMENT PRIMARY KEY,
data LONGBLOB
) ENGINE=InnoDB;
INSERT INTO t1 (data) SELECT REPEAT('a', 1024*1024) FROM seq_1_to_10;
FOUND 1 /Tablespace 'test/t1' size 7340032 bytes reached 70% of configured threshold of 10485760 bytes/ in mysqld.1.err
FOUND 1 /Tablespace 'test/t1' size 8388608 bytes reached 80% of configured threshold of 10485760 bytes/ in mysqld.1.err
FOUND 1 /Tablespace 'test/t1' size 9437184 bytes reached 90% of configured threshold of 10485760 bytes/ in mysqld.1.err
FOUND 1 /Tablespace 'test/t1' size 10485760 bytes reached 100% of configured threshold of 10485760 bytes/ in mysqld.1.err
DROP TABLE t1;
# Test configurable warning percentage
SET GLOBAL innodb_tablespace_size_warning_threshold = 10485760;
SET GLOBAL innodb_tablespace_size_warning_pct = 80;
CREATE TABLE t1b (
id INT AUTO_INCREMENT PRIMARY KEY,
data LONGBLOB
) ENGINE=InnoDB;
INSERT INTO t1b (data) SELECT REPEAT('x', 1024*1024) FROM seq_1_to_8;
FOUND 1 /Tablespace 'test/t1b' size 8388608 bytes reached 80% of configured threshold of 10485760 bytes/ in mysqld.1.err
FOUND 1 /Tablespace 'test/t1b' size 9437184 bytes reached 90% of configured threshold of 10485760 bytes/ in mysqld.1.err
DROP TABLE t1b;
# Test threshold set to zero disables warnings
SET GLOBAL innodb_tablespace_size_warning_threshold = 0;
CREATE TABLE t2 (
id INT AUTO_INCREMENT PRIMARY KEY,
data LONGBLOB
) ENGINE=InnoDB;
INSERT INTO t2 (data) SELECT REPEAT('b', 1024*1024) FROM seq_1_to_5;
NOT FOUND /Tablespace 'test/t2' size \d+ bytes reached \d+% of configured threshold/ in mysqld.1.err
DROP TABLE t2;
# Test disable via threshold=0 then re-enable
SET GLOBAL innodb_tablespace_size_warning_threshold = 0;
SET GLOBAL innodb_tablespace_size_warning_pct = 70;
CREATE TABLE t3 (
id INT AUTO_INCREMENT PRIMARY KEY,
data LONGBLOB
) ENGINE=InnoDB;
INSERT INTO t3 (data) SELECT REPEAT('c', 1024*1024) FROM seq_1_to_10;
SET GLOBAL innodb_tablespace_size_warning_threshold = 10485760;
INSERT INTO t3 (data) VALUES (REPEAT('d', 1024*1024));
FOUND 1 /Tablespace 'test/t3' size 12582912 bytes reached 100% of configured threshold of 10485760 bytes/ in mysqld.1.err
DROP TABLE t3;
# Test TRUNCATE TABLE resets warning state
CREATE TABLE t4 (
id INT AUTO_INCREMENT PRIMARY KEY,
data LONGBLOB
) ENGINE=InnoDB;
INSERT INTO t4 (data) SELECT REPEAT('e', 1024*1024) FROM seq_1_to_10;
FOUND 1 /Tablespace 'test/t4' size 7340032 bytes reached 70% of configured threshold of 10485760 bytes/ in mysqld.1.err
FOUND 1 /Tablespace 'test/t4' size 10485760 bytes reached 100% of configured threshold of 10485760 bytes/ in mysqld.1.err
TRUNCATE TABLE t4;
INSERT INTO t4 (data) SELECT REPEAT('f', 1024*1024) FROM seq_1_to_10;
FOUND 2 /Tablespace 'test/t4' size 7340032 bytes reached 70% of configured threshold of 10485760 bytes/ in mysqld.1.err
FOUND 2 /Tablespace 'test/t4' size 10485760 bytes reached 100% of configured threshold of 10485760 bytes/ in mysqld.1.err
DROP TABLE t4;
# Test behavior when tablespace exceeds 100% of threshold
SET GLOBAL innodb_tablespace_size_warning_threshold = 5242880;
SET GLOBAL innodb_tablespace_size_warning_pct = 70;
CREATE TABLE t5 (
id INT AUTO_INCREMENT PRIMARY KEY,
data LONGBLOB
) ENGINE=InnoDB;
INSERT INTO t5 (data) SELECT REPEAT('g', 1024*1024) FROM seq_1_to_10;
FOUND 1 /Tablespace 'test/t5' size 4194304 bytes reached 80% of configured threshold of 5242880 bytes/ in mysqld.1.err
FOUND 1 /Tablespace 'test/t5' size 5242880 bytes reached 100% of configured threshold of 5242880 bytes/ in mysqld.1.err
DROP TABLE t5;
SET GLOBAL innodb_tablespace_size_warning_threshold = 0;
SET GLOBAL innodb_tablespace_size_warning_pct = 85;
134 changes: 134 additions & 0 deletions mysql-test/suite/innodb/t/tablespace_size_warning.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
--source include/have_innodb.inc
--source include/have_innodb_16k.inc
--source include/have_sequence.inc
--source include/not_embedded.inc
Comment thread
FarihaIS marked this conversation as resolved.

--disable_query_log
call mtr.add_suppression("InnoDB: Tablespace .* size .* bytes reached .* of configured threshold");
--enable_query_log

--echo #
--echo # MDEV-38936: Proactive handling of InnoDB tablespace full condition
--echo #

--echo # Test basic warning emission
SET GLOBAL innodb_tablespace_size_warning_threshold = 10485760;
SET GLOBAL innodb_tablespace_size_warning_pct = 70;

CREATE TABLE t1 (
id INT AUTO_INCREMENT PRIMARY KEY,
data LONGBLOB
) ENGINE=InnoDB;

INSERT INTO t1 (data) SELECT REPEAT('a', 1024*1024) FROM seq_1_to_10;

let SEARCH_FILE=$MYSQLTEST_VARDIR/log/mysqld.1.err;
let SEARCH_PATTERN=Tablespace 'test/t1' size 7340032 bytes reached 70% of configured threshold of 10485760 bytes;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=Tablespace 'test/t1' size 8388608 bytes reached 80% of configured threshold of 10485760 bytes;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=Tablespace 'test/t1' size 9437184 bytes reached 90% of configured threshold of 10485760 bytes;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=Tablespace 'test/t1' size 10485760 bytes reached 100% of configured threshold of 10485760 bytes;
--source include/search_pattern_in_file.inc

DROP TABLE t1;

--echo # Test configurable warning percentage
SET GLOBAL innodb_tablespace_size_warning_threshold = 10485760;
SET GLOBAL innodb_tablespace_size_warning_pct = 80;

CREATE TABLE t1b (
id INT AUTO_INCREMENT PRIMARY KEY,
data LONGBLOB
) ENGINE=InnoDB;

INSERT INTO t1b (data) SELECT REPEAT('x', 1024*1024) FROM seq_1_to_8;

let SEARCH_PATTERN=Tablespace 'test/t1b' size 8388608 bytes reached 80% of configured threshold of 10485760 bytes;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=Tablespace 'test/t1b' size 9437184 bytes reached 90% of configured threshold of 10485760 bytes;
--source include/search_pattern_in_file.inc

DROP TABLE t1b;

--echo # Test threshold set to zero disables warnings
SET GLOBAL innodb_tablespace_size_warning_threshold = 0;
Comment thread
FarihaIS marked this conversation as resolved.

CREATE TABLE t2 (
id INT AUTO_INCREMENT PRIMARY KEY,
data LONGBLOB
) ENGINE=InnoDB;

INSERT INTO t2 (data) SELECT REPEAT('b', 1024*1024) FROM seq_1_to_5;

let SEARCH_PATTERN=Tablespace 'test/t2' size \d+ bytes reached \d+% of configured threshold;
--source include/search_pattern_in_file.inc

DROP TABLE t2;

--echo # Test disable via threshold=0 then re-enable
SET GLOBAL innodb_tablespace_size_warning_threshold = 0;
SET GLOBAL innodb_tablespace_size_warning_pct = 70;

CREATE TABLE t3 (
id INT AUTO_INCREMENT PRIMARY KEY,
data LONGBLOB
) ENGINE=InnoDB;

INSERT INTO t3 (data) SELECT REPEAT('c', 1024*1024) FROM seq_1_to_10;

SET GLOBAL innodb_tablespace_size_warning_threshold = 10485760;

INSERT INTO t3 (data) VALUES (REPEAT('d', 1024*1024));

let SEARCH_PATTERN=Tablespace 'test/t3' size 12582912 bytes reached 100% of configured threshold of 10485760 bytes;
--source include/search_pattern_in_file.inc

DROP TABLE t3;

--echo # Test TRUNCATE TABLE resets warning state
CREATE TABLE t4 (
id INT AUTO_INCREMENT PRIMARY KEY,
data LONGBLOB
) ENGINE=InnoDB;

INSERT INTO t4 (data) SELECT REPEAT('e', 1024*1024) FROM seq_1_to_10;

let SEARCH_PATTERN=Tablespace 'test/t4' size 7340032 bytes reached 70% of configured threshold of 10485760 bytes;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=Tablespace 'test/t4' size 10485760 bytes reached 100% of configured threshold of 10485760 bytes;
--source include/search_pattern_in_file.inc

TRUNCATE TABLE t4;

INSERT INTO t4 (data) SELECT REPEAT('f', 1024*1024) FROM seq_1_to_10;

let SEARCH_PATTERN=Tablespace 'test/t4' size 7340032 bytes reached 70% of configured threshold of 10485760 bytes;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=Tablespace 'test/t4' size 10485760 bytes reached 100% of configured threshold of 10485760 bytes;
--source include/search_pattern_in_file.inc

DROP TABLE t4;

--echo # Test behavior when tablespace exceeds 100% of threshold
SET GLOBAL innodb_tablespace_size_warning_threshold = 5242880;
SET GLOBAL innodb_tablespace_size_warning_pct = 70;

CREATE TABLE t5 (
id INT AUTO_INCREMENT PRIMARY KEY,
data LONGBLOB
) ENGINE=InnoDB;

INSERT INTO t5 (data) SELECT REPEAT('g', 1024*1024) FROM seq_1_to_10;

let SEARCH_PATTERN=Tablespace 'test/t5' size 4194304 bytes reached 80% of configured threshold of 5242880 bytes;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN=Tablespace 'test/t5' size 5242880 bytes reached 100% of configured threshold of 5242880 bytes;
--source include/search_pattern_in_file.inc

DROP TABLE t5;

# Restore default values
SET GLOBAL innodb_tablespace_size_warning_threshold = 0;
SET GLOBAL innodb_tablespace_size_warning_pct = 85;
24 changes: 24 additions & 0 deletions mysql-test/suite/sys_vars/r/sysvars_innodb.result
Original file line number Diff line number Diff line change
Expand Up @@ -1654,6 +1654,30 @@ NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME INNODB_TABLESPACE_SIZE_WARNING_PCT
SESSION_VALUE NULL
DEFAULT_VALUE 85
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE INT UNSIGNED
VARIABLE_COMMENT Percentage at which to start emitting tablespace size warnings
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 100
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME INNODB_TABLESPACE_SIZE_WARNING_THRESHOLD
SESSION_VALUE NULL
DEFAULT_VALUE 0
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BIGINT UNSIGNED
VARIABLE_COMMENT Threshold in bytes for tablespace size warnings (0 = disabled)
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 18446744073709551615
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME INNODB_TABLE_LOCKS
SESSION_VALUE ON
DEFAULT_VALUE ON
Expand Down
117 changes: 84 additions & 33 deletions storage/innobase/fsp/fsp0fsp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -596,29 +596,15 @@ fsp_try_extend_data_file_with_pages(
buf_block_t* header,
mtr_t* mtr)
{
bool success;
ulint size;

ut_ad(!is_system_tablespace(space->id));
ut_d(space->modify_check(*mtr));

size = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SIZE
+ header->page.frame);
uint32_t size = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SIZE
+ header->page.frame);
ut_ad(size == space->size_in_header);

ut_a(page_no >= size);

success = fil_space_extend(space, page_no + 1);
/* The size may be less than we wanted if we ran out of disk space. */
/* recv_sys_t::parse() expects to find a WRITE record that
covers all 4 bytes. Therefore, we must specify mtr_t::FORCED
in order to avoid optimizing away any unchanged most
significant bytes of FSP_SIZE. */
mtr->write<4,mtr_t::FORCED>(*header, FSP_HEADER_OFFSET + FSP_SIZE
+ header->page.frame, space->size);
space->size_in_header = space->size;

return(success);
return space->extend(page_no + 1, header, mtr);
}

/** Calculate the number of physical pages in an extent for this file.
Expand Down Expand Up @@ -660,6 +646,86 @@ static uint32_t fsp_get_pages_to_extend_ibd(unsigned physical_size,
return extent_size;
}

/** Check if tablespace size exceeds warning threshold and emit warning.
@param new_size New size in pages
@param threshold nonzero warning threshold in bytes
@return true if warning was emitted */
ATTRIBUTE_COLD bool fil_space_t::check_size_warning(uint32_t new_size,
ulonglong threshold)
noexcept
{
const uint warning_pct= fil_system.tablespace_size_warning_pct;

/* Reset state if threshold or warning percentage changed */
const uint32_t threshold_pages=
uint32_t(threshold / physical_size());
if ((m_last_warning_threshold ^ threshold_pages) |
(uint{m_last_warning_pct} ^ warning_pct)) {
m_last_size_warning_pct= 0;
m_last_warning_threshold= threshold_pages;
m_last_warning_pct= uint8_t(warning_pct);
}

uint64_t current_bytes=
uint64_t(new_size) * physical_size();
/* new_size is at most 1ULL<<32 and physical_size() at most 1<<16,
so current_bytes * 100 < 1ULL<<55, well within uint64_t range. */
uint64_t current_pct=
Comment thread
FarihaIS marked this conversation as resolved.
(current_bytes * 100) / threshold;
Comment thread
FarihaIS marked this conversation as resolved.
uint8_t display_pct=
uint8_t(std::min(current_pct, uint64_t{100}));

if (display_pct < warning_pct)
return false;

if (display_pct <= m_last_size_warning_pct)
return false;

/* Warn on every 1% increase */
const auto n= name();
sql_print_warning("InnoDB: Tablespace '%.*s' size %llu bytes"
" reached %u%% of configured threshold"
" of %llu bytes",
Comment thread
FarihaIS marked this conversation as resolved.
int(n.size()), n.data(),
ulonglong{current_bytes},
unsigned{display_pct},
ulonglong{threshold});

m_last_size_warning_pct= display_pct;

return true;
}

/** Extend the tablespace, update size_in_header, and emit size warnings.
@param size desired size in pages
@param header tablespace header block
@param mtr mini-transaction
@return whether the extension succeeded */
bool fil_space_t::extend(uint32_t size, buf_block_t *header, mtr_t *mtr)
noexcept
{
if (!fil_space_extend(this, size))
return false;
Comment thread
FarihaIS marked this conversation as resolved.

/* For the system tablespace, we ignore any fragments of a
full megabyte when storing the size to the space header */
const unsigned ps= physical_size();
size_in_header= id
? this->size
: ut_2pow_round(this->size, (1024 * 1024) / ps);

/* recv_sys_t::parse() expects to find a WRITE record that
covers all 4 bytes. Therefore, we must specify mtr_t::FORCED
in order to avoid optimizing away any unchanged most
significant bytes of FSP_SIZE. */
mtr->write<4,mtr_t::FORCED>(*header, FSP_HEADER_OFFSET + FSP_SIZE +
header->page.frame, size_in_header);

if (uint64_t threshold= fil_system.tablespace_size_warning_threshold)
check_size_warning(size_in_header, threshold);
return true;
}

/** Try to extend the last data file of a tablespace if it is auto-extending.
@param[in,out] space tablespace
@param[in,out] header tablespace header
Expand Down Expand Up @@ -741,25 +807,10 @@ fsp_try_extend_data_file(fil_space_t *space, buf_block_t *header, mtr_t *mtr)
return(0);
}

if (!fil_space_extend(space, size + size_increase)) {
if (!space->extend(size + size_increase, header, mtr)) {
return(0);
}

/* For the system tablespace, we ignore any fragments of a
full megabyte when storing the size to the space header */

space->size_in_header = space->id
? space->size
: ut_2pow_round(space->size, (1024 * 1024) / ps);

/* recv_sys_t::parse() expects to find a WRITE record that
covers all 4 bytes. Therefore, we must specify mtr_t::FORCED
in order to avoid optimizing away any unchanged most
significant bytes of FSP_SIZE. */
mtr->write<4,mtr_t::FORCED>(*header, FSP_HEADER_OFFSET + FSP_SIZE
+ header->page.frame,
space->size_in_header);

return(size_increase);
}

Expand Down
Loading
Loading