MDEV-36149 UBSAN in X is outside the range of representable values of type 'unsigned long' | page_cleaner_flush_pages_recommendation

Currently it is allowed to set innodb_io_capacity to very large value
up to unsigned 8 byte maximum value 18446744073709551615. While
calculating the number of pages to flush, we could sometime go beyond
innodb_io_capacity. Specifically, MDEV-24369 has introduced a logic
for aggressive flushing when dirty page percentage in buffer pool
exceeds innodb_max_dirty_pages_pct. So, when innodb_io_capacity is
set to very large value and dirty page percentage exceeds the
threshold, there is a multiplication overflow in Innodb page cleaner.

Fix: We should prevent setting io_capacity to unrealistic values and
define a practical limit to it. The patch introduces limits for
innodb_io_capacity_max and innodb_io_capacity to the maximum of 4 byte
unsigned integer i.e. 4294967295 (2^32-1). For 16k page size this limit
translates to 64 TiB/sec write IO speed which looks sufficient.

Reviewed by: Marko Mäkelä
This commit is contained in:
mariadb-DebarunBanerjee 2025-03-11 12:13:05 +05:30
parent 839828e57f
commit a8e35a1cc6
5 changed files with 59 additions and 58 deletions
mysql-test/suite/sys_vars/r
storage/innobase

View file

@ -9,7 +9,7 @@
VARIABLE_COMMENT Number of InnoDB Adaptive Hash Index Partitions (default 8)
NUMERIC_MIN_VALUE 1
NUMERIC_MAX_VALUE 512
@@ -71,7 +71,7 @@
@@ -83,7 +83,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 1
VARIABLE_SCOPE GLOBAL
@ -18,7 +18,7 @@
VARIABLE_COMMENT The AUTOINC lock modes supported by InnoDB: 0 => Old style AUTOINC locking (for backward compatibility); 1 => New style AUTOINC locking; 2 => No AUTOINC locking (unsafe for SBR)
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 2
@@ -83,10 +83,10 @@
@@ -95,10 +95,10 @@
SESSION_VALUE NULL
DEFAULT_VALUE 0
VARIABLE_SCOPE GLOBAL
@ -31,7 +31,7 @@
NUMERIC_BLOCK_SIZE 1048576
ENUM_VALUE_LIST NULL
READ_ONLY YES
@@ -119,7 +119,7 @@
@@ -131,7 +131,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 25
VARIABLE_SCOPE GLOBAL
@ -40,7 +40,7 @@
VARIABLE_COMMENT Dump only the hottest N% of each buffer pool, defaults to 25
NUMERIC_MIN_VALUE 1
NUMERIC_MAX_VALUE 100
@@ -203,7 +203,7 @@
@@ -215,7 +215,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 0
VARIABLE_SCOPE GLOBAL
@ -49,7 +49,7 @@
VARIABLE_COMMENT A number between [0, 100] that tells how oftern buffer pool dump status in percentages should be printed. E.g. 10 means that buffer pool dump status is printed when every 10% of number of buffer pool pages are dumped. Default is 0 (only start and end status is printed).
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 100
@@ -323,7 +323,7 @@
@@ -335,7 +335,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 5
VARIABLE_SCOPE GLOBAL
@ -58,7 +58,7 @@
VARIABLE_COMMENT If the compression failure rate of a table is greater than this number more padding is added to the pages to reduce the failures. A value of zero implies no padding
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 100
@@ -347,7 +347,7 @@
@@ -359,7 +359,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 50
VARIABLE_SCOPE GLOBAL
@ -67,7 +67,7 @@
VARIABLE_COMMENT Percentage of empty space on a data page that can be reserved to make the page compressible.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 75
@@ -623,7 +623,7 @@
@@ -647,7 +647,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 600
VARIABLE_SCOPE GLOBAL
@ -76,7 +76,7 @@
VARIABLE_COMMENT Maximum number of seconds that semaphore times out in InnoDB.
NUMERIC_MIN_VALUE 1
NUMERIC_MAX_VALUE 4294967295
@@ -671,7 +671,7 @@
@@ -695,7 +695,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 30
VARIABLE_SCOPE GLOBAL
@ -85,7 +85,7 @@
VARIABLE_COMMENT Number of iterations over which the background flushing is averaged.
NUMERIC_MIN_VALUE 1
NUMERIC_MAX_VALUE 1000
@@ -695,7 +695,7 @@
@@ -719,7 +719,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 1
VARIABLE_SCOPE GLOBAL
@ -94,7 +94,7 @@
VARIABLE_COMMENT Controls the durability/speed trade-off for commits. Set to 0 (write and flush redo log to disk only once per second), 1 (flush to disk at each commit), 2 (write to log at commit but flush to disk only once per second) or 3 (flush to disk at prepare and at commit, slower and usually redundant). 1 and 3 guarantees that after a crash, committed transactions will not be lost and will be consistent with the binlog and other transactional engines. 2 can get inconsistent and lose transactions if there is a power failure or kernel crash but not if mysqld crashes. 0 has no guarantees in case of crash. 0 and 2 can be faster than 1 or 3.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 3
@@ -719,7 +719,7 @@
@@ -743,7 +743,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 1
VARIABLE_SCOPE GLOBAL
@ -103,7 +103,7 @@
VARIABLE_COMMENT Set to 0 (don't flush neighbors from buffer pool), 1 (flush contiguous neighbors from buffer pool) or 2 (flush neighbors from buffer pool), when flushing a block
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 2
@@ -755,7 +755,7 @@
@@ -779,7 +779,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 0
VARIABLE_SCOPE GLOBAL
@ -112,7 +112,7 @@
VARIABLE_COMMENT Helps to save your data in case the disk image of the database becomes corrupt. Value 5 can return bogus data, and 6 can permanently corrupt data.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 6
@@ -779,10 +779,10 @@
@@ -803,10 +803,10 @@
SESSION_VALUE NULL
DEFAULT_VALUE 8000000
VARIABLE_SCOPE GLOBAL
@ -125,7 +125,7 @@
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
@@ -815,7 +815,7 @@
@@ -839,7 +839,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 84
VARIABLE_SCOPE GLOBAL
@ -134,7 +134,7 @@
VARIABLE_COMMENT InnoDB Fulltext search maximum token size in characters
NUMERIC_MIN_VALUE 10
NUMERIC_MAX_VALUE 84
@@ -827,7 +827,7 @@
@@ -851,7 +851,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 3
VARIABLE_SCOPE GLOBAL
@ -143,7 +143,7 @@
VARIABLE_COMMENT InnoDB Fulltext search minimum token size in characters
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 16
@@ -839,7 +839,7 @@
@@ -863,7 +863,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 2000
VARIABLE_SCOPE GLOBAL
@ -152,7 +152,7 @@
VARIABLE_COMMENT InnoDB Fulltext search number of words to optimize for each optimize table call
NUMERIC_MIN_VALUE 1000
NUMERIC_MAX_VALUE 10000
@@ -851,10 +851,10 @@
@@ -875,10 +875,10 @@
SESSION_VALUE NULL
DEFAULT_VALUE 2000000000
VARIABLE_SCOPE GLOBAL
@ -165,7 +165,7 @@
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
@@ -875,7 +875,7 @@
@@ -899,7 +899,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 2
VARIABLE_SCOPE GLOBAL
@ -174,7 +174,7 @@
VARIABLE_COMMENT InnoDB Fulltext search parallel sort degree, will round up to nearest power of 2 number
NUMERIC_MIN_VALUE 1
NUMERIC_MAX_VALUE 16
@@ -887,10 +887,10 @@
@@ -911,10 +911,10 @@
SESSION_VALUE NULL
DEFAULT_VALUE 640000000
VARIABLE_SCOPE GLOBAL
@ -187,7 +187,7 @@
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
@@ -935,22 +935,22 @@
@@ -959,7 +959,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 200
VARIABLE_SCOPE GLOBAL
@ -195,27 +195,17 @@
+VARIABLE_TYPE INT UNSIGNED
VARIABLE_COMMENT Number of IOPs the server can do. Tunes the background IO rate
NUMERIC_MIN_VALUE 100
-NUMERIC_MAX_VALUE 18446744073709551615
+NUMERIC_MAX_VALUE 4294967295
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME INNODB_IO_CAPACITY_MAX
NUMERIC_MAX_VALUE 4294967295
@@ -971,7 +971,7 @@
SESSION_VALUE NULL
-DEFAULT_VALUE 18446744073709551615
+DEFAULT_VALUE 4294967295
DEFAULT_VALUE 4294967295
VARIABLE_SCOPE GLOBAL
-VARIABLE_TYPE BIGINT UNSIGNED
+VARIABLE_TYPE INT UNSIGNED
VARIABLE_COMMENT Limit to which innodb_io_capacity can be inflated.
NUMERIC_MIN_VALUE 100
-NUMERIC_MAX_VALUE 18446744073709551615
+NUMERIC_MAX_VALUE 4294967295
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
@@ -1043,10 +1043,10 @@
NUMERIC_MAX_VALUE 4294967295
@@ -1091,10 +1091,10 @@
SESSION_VALUE NULL
DEFAULT_VALUE 32
VARIABLE_SCOPE GLOBAL
@ -228,7 +218,7 @@
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
@@ -1055,10 +1055,10 @@
@@ -1103,10 +1103,10 @@
SESSION_VALUE NULL
DEFAULT_VALUE 1536
VARIABLE_SCOPE GLOBAL
@ -241,7 +231,7 @@
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
@@ -1091,10 +1091,10 @@
@@ -1139,10 +1139,10 @@
SESSION_VALUE NULL
DEFAULT_VALUE 0
VARIABLE_SCOPE GLOBAL
@ -254,7 +244,7 @@
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
@@ -1103,7 +1103,7 @@
@@ -1151,7 +1151,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 0
VARIABLE_SCOPE GLOBAL
@ -263,7 +253,7 @@
VARIABLE_COMMENT Maximum delay of user threads in micro-seconds
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 10000000
@@ -1235,10 +1235,10 @@
@@ -1283,10 +1283,10 @@
SESSION_VALUE NULL
DEFAULT_VALUE 0
VARIABLE_SCOPE GLOBAL
@ -276,7 +266,7 @@
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY YES
@@ -1259,7 +1259,7 @@
@@ -1307,7 +1307,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 16384
VARIABLE_SCOPE GLOBAL
@ -285,16 +275,16 @@
VARIABLE_COMMENT Page size to use for all InnoDB tablespaces.
NUMERIC_MIN_VALUE 4096
NUMERIC_MAX_VALUE 65536
@@ -1295,7 +1295,7 @@
@@ -1343,7 +1343,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 1000
DEFAULT_VALUE 127
VARIABLE_SCOPE GLOBAL
-VARIABLE_TYPE BIGINT UNSIGNED
+VARIABLE_TYPE INT UNSIGNED
VARIABLE_COMMENT Number of UNDO log pages to purge in one batch from the history list.
NUMERIC_MIN_VALUE 1
NUMERIC_MAX_VALUE 5000
@@ -1307,7 +1307,7 @@
@@ -1355,7 +1355,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 128
VARIABLE_SCOPE GLOBAL
@ -303,7 +293,7 @@
VARIABLE_COMMENT Deprecated parameter with no effect
NUMERIC_MIN_VALUE 1
NUMERIC_MAX_VALUE 128
@@ -1343,7 +1343,7 @@
@@ -1391,7 +1391,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 56
VARIABLE_SCOPE GLOBAL
@ -312,7 +302,7 @@
VARIABLE_COMMENT Number of pages that must be accessed sequentially for InnoDB to trigger a readahead.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 64
@@ -1427,7 +1427,7 @@
@@ -1475,7 +1475,7 @@
SESSION_VALUE NULL
DEFAULT_VALUE 1048576
VARIABLE_SCOPE GLOBAL
@ -321,7 +311,7 @@
VARIABLE_COMMENT Memory buffer size for index creation
NUMERIC_MIN_VALUE 65536
NUMERIC_MAX_VALUE 67108864
@@ -1595,10 +1595,10 @@
@@ -1643,10 +1643,10 @@
SESSION_VALUE NULL
DEFAULT_VALUE 30
VARIABLE_SCOPE GLOBAL

View file

@ -962,19 +962,19 @@ VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BIGINT UNSIGNED
VARIABLE_COMMENT Number of IOPs the server can do. Tunes the background IO rate
NUMERIC_MIN_VALUE 100
NUMERIC_MAX_VALUE 18446744073709551615
NUMERIC_MAX_VALUE 4294967295
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME INNODB_IO_CAPACITY_MAX
SESSION_VALUE NULL
DEFAULT_VALUE 18446744073709551615
DEFAULT_VALUE 4294967295
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BIGINT UNSIGNED
VARIABLE_COMMENT Limit to which innodb_io_capacity can be inflated.
NUMERIC_MIN_VALUE 100
NUMERIC_MAX_VALUE 18446744073709551615
NUMERIC_MAX_VALUE 4294967295
NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO

View file

@ -2310,13 +2310,23 @@ static ulint page_cleaner_flush_pages_recommendation(ulint last_pages_in,
if (!prev_lsn || !pct_for_lsn) {
prev_time = curr_time;
prev_lsn = cur_lsn;
if (max_pct > 0.0) {
dirty_pct /= max_pct;
}
n_pages = ulint(dirty_pct * double(srv_io_capacity));
if (n_pages < dirty_blocks) {
n_pages= std::min<ulint>(srv_io_capacity, dirty_blocks);
if (srv_io_capacity >= dirty_blocks) {
n_pages = dirty_blocks;
} else {
if (max_pct > 1.0) {
dirty_pct/= max_pct;
}
n_pages= ulint(dirty_pct * double(srv_io_capacity));
if (n_pages < dirty_blocks) {
n_pages= srv_io_capacity;
} else {
/* Set maximum IO capacity upper bound. */
n_pages= std::min<ulint>(srv_max_io_capacity,
dirty_blocks);
}
}
func_exit:

View file

@ -17518,7 +17518,8 @@ innodb_io_capacity_update(
" higher than innodb_io_capacity_max %lu",
in_val, srv_max_io_capacity);
srv_max_io_capacity = (in_val & ~(~0UL >> 1))
/* Avoid overflow. */
srv_max_io_capacity = (in_val >= SRV_MAX_IO_CAPACITY_LIMIT / 2)
? in_val : in_val * 2;
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
@ -19123,7 +19124,7 @@ static MYSQL_SYSVAR_ENUM(instant_alter_column_allowed,
static MYSQL_SYSVAR_ULONG(io_capacity, srv_io_capacity,
PLUGIN_VAR_RQCMDARG,
"Number of IOPs the server can do. Tunes the background IO rate",
NULL, innodb_io_capacity_update, 200, 100, ~0UL, 0);
NULL, innodb_io_capacity_update, 200, 100, SRV_MAX_IO_CAPACITY_LIMIT, 0);
static MYSQL_SYSVAR_ULONG(io_capacity_max, srv_max_io_capacity,
PLUGIN_VAR_RQCMDARG,

View file

@ -267,8 +267,8 @@ extern ulong srv_io_capacity;
/* We use this dummy default value at startup for max_io_capacity.
The real value is set based on the value of io_capacity. */
#define SRV_MAX_IO_CAPACITY_DUMMY_DEFAULT (~0UL)
#define SRV_MAX_IO_CAPACITY_LIMIT (~0UL)
#define SRV_MAX_IO_CAPACITY_DUMMY_DEFAULT (UINT32_MAX)
#define SRV_MAX_IO_CAPACITY_LIMIT (UINT32_MAX)
extern ulong srv_max_io_capacity;
/* The "innodb_stats_method" setting, decides how InnoDB is going