-
-
Notifications
You must be signed in to change notification settings - Fork 2k
MDEV-38936 Proactive handling of InnoDB tablespace full condition #4721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| # | ||
| # MDEV-38936: Proactive handling of InnoDB tablespace full condition | ||
| # | ||
| SET @old_threshold = @@global.innodb_tablespace_size_warning_threshold; | ||
| SET @old_enabled = @@global.innodb_tablespace_size_warning_enabled; | ||
| SET @old_pct = @@global.innodb_tablespace_size_warning_pct; | ||
| # Test system variables | ||
| SHOW VARIABLES LIKE 'innodb_tablespace_size_warning_threshold'; | ||
| Variable_name Value | ||
| innodb_tablespace_size_warning_threshold 17592186044416 | ||
| SHOW VARIABLES LIKE 'innodb_tablespace_size_warning_enabled'; | ||
| Variable_name Value | ||
| innodb_tablespace_size_warning_enabled ON | ||
| SHOW VARIABLES LIKE 'innodb_tablespace_size_warning_pct'; | ||
| Variable_name Value | ||
| innodb_tablespace_size_warning_pct 70 | ||
| # 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; | ||
| FOUND 1 /Tablespace 'test/t1' size .* bytes .* exceeds warning threshold/ 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; | ||
| FOUND 1 /Tablespace 'test/t1b' size .* bytes .* exceeds warning threshold/ 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; | ||
| DROP TABLE t2; | ||
| # Test andon cord disable/enable | ||
| SET GLOBAL innodb_tablespace_size_warning_threshold = 10485760; | ||
| SET GLOBAL innodb_tablespace_size_warning_pct = 70; | ||
| SET GLOBAL innodb_tablespace_size_warning_enabled = FALSE; | ||
| CREATE TABLE t3 ( | ||
| id INT AUTO_INCREMENT PRIMARY KEY, | ||
| data LONGBLOB | ||
| ) ENGINE=InnoDB; | ||
| SET GLOBAL innodb_tablespace_size_warning_enabled = TRUE; | ||
| FOUND 1 /Tablespace 'test/t3' size .* bytes .* exceeds warning threshold/ 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; | ||
| FOUND 1 /Tablespace 'test/t4' size .* bytes .* exceeds warning threshold/ in mysqld.1.err | ||
| TRUNCATE TABLE t4; | ||
| FOUND 1 /Tablespace 'test/t4' size .* bytes .* exceeds warning threshold/ in mysqld.1.err | ||
| DROP TABLE t4; | ||
| SET GLOBAL innodb_tablespace_size_warning_threshold = @old_threshold; | ||
| SET GLOBAL innodb_tablespace_size_warning_enabled = @old_enabled; | ||
| SET GLOBAL innodb_tablespace_size_warning_pct = @old_pct; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| --source include/have_innodb.inc | ||
| --source include/not_embedded.inc | ||
|
|
||
| --disable_query_log | ||
| call mtr.add_suppression("Tablespace .* size .* bytes .* exceeds warning threshold"); | ||
| --enable_query_log | ||
|
|
||
| --echo # | ||
| --echo # MDEV-38936: Proactive handling of InnoDB tablespace full condition | ||
| --echo # | ||
|
|
||
| # Save original values | ||
| SET @old_threshold = @@global.innodb_tablespace_size_warning_threshold; | ||
| SET @old_enabled = @@global.innodb_tablespace_size_warning_enabled; | ||
| SET @old_pct = @@global.innodb_tablespace_size_warning_pct; | ||
|
|
||
| --echo # Test system variables | ||
| SHOW VARIABLES LIKE 'innodb_tablespace_size_warning_threshold'; | ||
| SHOW VARIABLES LIKE 'innodb_tablespace_size_warning_enabled'; | ||
| SHOW VARIABLES LIKE 'innodb_tablespace_size_warning_pct'; | ||
|
|
||
| --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; | ||
|
|
||
| --disable_query_log | ||
| let $i = 10; | ||
| while ($i) { | ||
| eval INSERT INTO t1 (data) VALUES (REPEAT('a', 1024*1024)); | ||
| dec $i; | ||
| } | ||
| --enable_query_log | ||
|
|
||
| let SEARCH_FILE=$MYSQLTEST_VARDIR/log/mysqld.1.err; | ||
| let SEARCH_PATTERN=Tablespace 'test/t1' size .* bytes .* exceeds warning threshold; | ||
| --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; | ||
|
|
||
| --disable_query_log | ||
| let $i = 8; | ||
| while ($i) { | ||
| eval INSERT INTO t1b (data) VALUES (REPEAT('x', 1024*1024)); | ||
| dec $i; | ||
| } | ||
| --enable_query_log | ||
|
|
||
| let SEARCH_PATTERN=Tablespace 'test/t1b' size .* bytes .* exceeds warning threshold; | ||
| --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; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here maybe you should check that no warning is present in the log after the |
||
|
|
||
| CREATE TABLE t2 ( | ||
| id INT AUTO_INCREMENT PRIMARY KEY, | ||
| data LONGBLOB | ||
| ) ENGINE=InnoDB; | ||
|
|
||
| --disable_query_log | ||
| let $i = 5; | ||
| while ($i) { | ||
| eval INSERT INTO t2 (data) VALUES (REPEAT('b', 1024*1024)); | ||
| dec $i; | ||
| } | ||
| --enable_query_log | ||
|
|
||
| DROP TABLE t2; | ||
|
|
||
| --echo # Test andon cord disable/enable | ||
| SET GLOBAL innodb_tablespace_size_warning_threshold = 10485760; | ||
| SET GLOBAL innodb_tablespace_size_warning_pct = 70; | ||
| SET GLOBAL innodb_tablespace_size_warning_enabled = FALSE; | ||
|
|
||
| CREATE TABLE t3 ( | ||
| id INT AUTO_INCREMENT PRIMARY KEY, | ||
| data LONGBLOB | ||
| ) ENGINE=InnoDB; | ||
|
|
||
| --disable_query_log | ||
| let $i = 10; | ||
| while ($i) { | ||
| eval INSERT INTO t3 (data) VALUES (REPEAT('c', 1024*1024)); | ||
| dec $i; | ||
| } | ||
| --enable_query_log | ||
|
|
||
| SET GLOBAL innodb_tablespace_size_warning_enabled = TRUE; | ||
|
|
||
| --disable_query_log | ||
| INSERT INTO t3 (data) VALUES (REPEAT('d', 1024*1024)); | ||
| --enable_query_log | ||
|
|
||
| let SEARCH_PATTERN=Tablespace 'test/t3' size .* bytes .* exceeds warning threshold; | ||
| --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; | ||
|
|
||
| --disable_query_log | ||
| let $i = 10; | ||
| while ($i) { | ||
| eval INSERT INTO t4 (data) VALUES (REPEAT('e', 1024*1024)); | ||
| dec $i; | ||
| } | ||
| --enable_query_log | ||
|
|
||
| let SEARCH_PATTERN=Tablespace 'test/t4' size .* bytes .* exceeds warning threshold; | ||
| --source include/search_pattern_in_file.inc | ||
|
|
||
| TRUNCATE TABLE t4; | ||
|
|
||
| --disable_query_log | ||
| let $i = 10; | ||
| while ($i) { | ||
| eval INSERT INTO t4 (data) VALUES (REPEAT('f', 1024*1024)); | ||
| dec $i; | ||
| } | ||
| --enable_query_log | ||
|
|
||
| let SEARCH_PATTERN=Tablespace 'test/t4' size .* bytes .* exceeds warning threshold; | ||
| --source include/search_pattern_in_file.inc | ||
|
|
||
| DROP TABLE t4; | ||
|
|
||
| # Restore original values | ||
| SET GLOBAL innodb_tablespace_size_warning_threshold = @old_threshold; | ||
| SET GLOBAL innodb_tablespace_size_warning_enabled = @old_enabled; | ||
| SET GLOBAL innodb_tablespace_size_warning_pct = @old_pct; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -658,6 +658,84 @@ static uint32_t fsp_get_pages_to_extend_ibd(unsigned physical_size, | |
| return extent_size; | ||
| } | ||
|
|
||
| /** Check if tablespace size exceeds warning threshold. | ||
| @param[in] space Tablespace | ||
| @param[in] new_size New size in pages | ||
| @return true if warning was emitted */ | ||
| static bool fsp_check_size_warning(fil_space_t *space, uint32_t new_size) noexcept | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Related to this PR is MDEV-38898. When |
||
| { | ||
| /* Named constant for high-resolution warning threshold */ | ||
| constexpr uint8_t high_resolution_pct = 90; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a good compromise would be to remove the dual-tier warning thresholds and the hardcoded which I suppose is a negligible overhead, and allows for a simplified logic and no hard-coded constants. |
||
|
|
||
| if (!srv_tablespace_size_warning_enabled) | ||
| return false; | ||
|
|
||
| if (srv_tablespace_size_warning_threshold == 0) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please load this into a local variable once and then read from the local variable, to avoid inconsistencies if the variable is changed in the middle of execution. |
||
| return false; | ||
|
|
||
| /* Reset state if threshold changed */ | ||
| if (space->m_last_warning_threshold != srv_tablespace_size_warning_threshold) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe reset the state also if |
||
| space->m_last_size_warning_pct= 0; | ||
| space->m_last_warning_threshold= srv_tablespace_size_warning_threshold; | ||
| space->m_warning_count_in_decade= 0; | ||
| } | ||
|
|
||
| uint64_t current_bytes= | ||
| static_cast<uint64_t>(new_size) * space->physical_size(); | ||
| uint64_t current_pct= | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens when |
||
| (current_bytes * 100) / srv_tablespace_size_warning_threshold; | ||
| uint64_t display_pct= std::min(current_pct, static_cast<uint64_t>(100)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| bool should_warn= false; | ||
|
|
||
| if (display_pct < srv_tablespace_size_warning_pct) | ||
| return false; | ||
|
|
||
| if (display_pct >= high_resolution_pct) { | ||
| /* Above high_resolution_pct: print on every 1% increase */ | ||
| should_warn= (display_pct > space->m_last_size_warning_pct); | ||
| } else { | ||
| /* Between tablespace_size_warning_pct and high_resolution_pct: | ||
| print at most twice per 10% (e.g., 70%, 77%, 81%, 89%) */ | ||
| uint8_t current_decade= static_cast<uint8_t>(display_pct / 10); | ||
| uint8_t last_decade= space->m_last_size_warning_pct / 10; | ||
|
|
||
| /* If we've moved to a new decade, reset the counter */ | ||
| if (current_decade > last_decade) { | ||
| space->m_warning_count_in_decade= 0; | ||
| } | ||
|
|
||
| /* Warn if we haven't warned twice yet in this decade, percentage increased, | ||
| and there's at least a 5% gap since last warning (or it's the first warning) */ | ||
| if (space->m_warning_count_in_decade < 2 && | ||
| display_pct > space->m_last_size_warning_pct && | ||
| (space->m_warning_count_in_decade == 0 || | ||
| display_pct >= static_cast<uint64_t>(space->m_last_size_warning_pct + 5))) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If |
||
| should_warn= true; | ||
| } | ||
| } | ||
|
|
||
| if (should_warn) { | ||
| const auto name= space->name(); | ||
| ib::warn() << "Tablespace '" << std::string_view(name.data(), name.size()) | ||
| << "' size " << current_bytes | ||
| << " bytes (" << display_pct << "%)" | ||
| << " exceeds warning threshold of " | ||
| << srv_tablespace_size_warning_threshold << " bytes"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I got it correctly, this warning will fire before the threshold is exceeded (when pct < 100%). So the warning may be rephrased to better highlight this, something like: |
||
|
|
||
| space->m_last_size_warning_pct= static_cast<uint8_t>(display_pct); | ||
|
|
||
| /* Increment counter only for the tiered warning range (below high_resolution_pct) */ | ||
| if (display_pct < high_resolution_pct) { | ||
| space->m_warning_count_in_decade++; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /** 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 | ||
|
|
@@ -758,6 +836,9 @@ fsp_try_extend_data_file(fil_space_t *space, buf_block_t *header, mtr_t *mtr) | |
| + header->page.frame, | ||
| space->size_in_header); | ||
|
|
||
| /* Check if tablespace size exceeds warning threshold */ | ||
| fsp_check_size_warning(space, space->size_in_header); | ||
|
|
||
| return(size_increase); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19138,6 +19138,30 @@ static MYSQL_SYSVAR_ULONG(purge_batch_size, srv_purge_batch_size, | |
| 1, /* Minimum value */ | ||
| innodb_purge_batch_size_MAX, 0); | ||
|
|
||
| static MYSQL_SYSVAR_ULONGLONG(tablespace_size_warning_threshold, | ||
| srv_tablespace_size_warning_threshold, | ||
| PLUGIN_VAR_RQCMDARG, | ||
| "Threshold in bytes for tablespace size warnings (0 = disabled)", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If |
||
| NULL, NULL, | ||
| 17592186044416ULL, /* Default setting */ | ||
| 0, /* Minimum value */ | ||
| ULLONG_MAX, 0); /* Maximum value */ | ||
|
|
||
| static MYSQL_SYSVAR_UINT(tablespace_size_warning_pct, | ||
| srv_tablespace_size_warning_pct, | ||
| PLUGIN_VAR_RQCMDARG, | ||
| "Percentage at which to start emitting tablespace size warnings", | ||
| NULL, NULL, | ||
| 70, /* Default setting */ | ||
| 0, /* Minimum value */ | ||
| 100, 0); /* Maximum value */ | ||
|
|
||
| static MYSQL_SYSVAR_BOOL(tablespace_size_warning_enabled, | ||
| srv_tablespace_size_warning_enabled, | ||
| PLUGIN_VAR_OPCMDARG, | ||
| "Enable/disable tablespace size warning feature", | ||
| NULL, NULL, TRUE); | ||
|
|
||
| extern void srv_update_purge_thread_count(uint n); | ||
|
|
||
| static | ||
|
|
@@ -20102,6 +20126,9 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { | |
| MYSQL_SYSVAR(monitor_reset_all), | ||
| MYSQL_SYSVAR(purge_threads), | ||
| MYSQL_SYSVAR(purge_batch_size), | ||
| MYSQL_SYSVAR(tablespace_size_warning_threshold), | ||
| MYSQL_SYSVAR(tablespace_size_warning_pct), | ||
| MYSQL_SYSVAR(tablespace_size_warning_enabled), | ||
| MYSQL_SYSVAR(log_checkpoint_now), | ||
| #ifdef UNIV_DEBUG | ||
| MYSQL_SYSVAR(buf_flush_list_now), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested and there are actually 4 logs with this pattern in the
mysqld.1.errlog.The
.*pattern is matching multiple lines and showingFOUND 1here.Replacing
.*with[^\n]*should fix the behavior and show the actual count of logs, which is interesting to test in this file.