commit 99ec4a6dd1b1a358c18c59be85032b844dcbd2c8 Author: zhongbei.yk Date: Tue Apr 2 11:18:47 2024 +0800 bug#114180 innodb_activity_count counter in innodb_metrics goes negative Description =========== innodb_activity_count counter in informatioin_schema.innodb_metrics goes negative. See MySQL bug#114180. Analysis ======== In srv_get_activity_count() function, the return value is caculated by (srv_sys == nullptr ? 0 : srv_sys->activity_count). '0' is type of signed int (4 bytes), srv_sys->activity_count is an object that overrides the () operator and has the data type ulint (8 bytes). Implicit conversions between the two lose precision. Fix === Implicit conversions should be avoided. diff --git a/mysql-test/suite/innodb/r/innodb_bug114180.result b/mysql-test/suite/innodb/r/innodb_bug114180.result new file mode 100644 index 00000000000..a1438ac5894 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug114180.result @@ -0,0 +1,15 @@ +CREATE PROCEDURE quick_increase_innodb_activity_count() +BEGIN +DECLARE i int DEFAULT 1; +WHILE (i <= 4) DO +SELECT NAME, COUNT FROM information_schema.INNODB_METRICS WHERE name='innodb_activity_count'; +SET i = i + 1; +END WHILE; +END | +# Makes innodb_activity_count greater than 2147483648 (2^31) +SET GLOBAL DEBUG= '+d, srv_increase_activity_count'; +call quick_increase_innodb_activity_count(); +SET GLOBAL DEBUG= '-d, srv_increase_activity_count'; +SET @innodb_activity_count= (SELECT COUNT FROM information_schema.INNODB_METRICS WHERE name='innodb_activity_count'); +# innodb_activity_count is negative before this patch +DROP PROCEDURE quick_increase_innodb_activity_count; diff --git a/mysql-test/suite/innodb/t/innodb_bug114180.test b/mysql-test/suite/innodb/t/innodb_bug114180.test new file mode 100644 index 00000000000..d77ce48d93b --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug114180.test @@ -0,0 +1,41 @@ +# ==== Purpose ==== +# +# bug#114180 innodb_activity_count counter in innodb_metrics goes negative +# +# When innodb_activity_count is greater than 2147483648 (2^31), the query result for this value is negative. +# This test uses DBUG_EXECUTE_IF "srv_increase_activity_count" to rapidly increase the value of innodb_activity_count +# until it greater than 2147483648 (2^31). Thus we can repeat the bug. +# + +--source include/have_debug.inc + +DELIMITER |; + +CREATE PROCEDURE quick_increase_innodb_activity_count() +BEGIN + DECLARE i int DEFAULT 1; + WHILE (i <= 4) DO + SELECT NAME, COUNT FROM information_schema.INNODB_METRICS WHERE name='innodb_activity_count'; + SET i = i + 1; + END WHILE; +END | + +DELIMITER ;| + +--echo # Makes innodb_activity_count greater than 2147483648 (2^31) +SET GLOBAL DEBUG= '+d, srv_increase_activity_count'; +--disable_result_log +call quick_increase_innodb_activity_count(); +--enable_result_log +SET GLOBAL DEBUG= '-d, srv_increase_activity_count'; + +SET @innodb_activity_count= (SELECT COUNT FROM information_schema.INNODB_METRICS WHERE name='innodb_activity_count'); + +--echo # innodb_activity_count is negative before this patch +if (`SELECT CONVERT(@innodb_activity_count, SIGNED) < 0`) +{ + --echo "Unexpected: innodb_activity_count should be positive." + --die +} + +DROP PROCEDURE quick_increase_innodb_activity_count; \ No newline at end of file diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index 3184176f7ae..64e9d02df3b 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -1975,7 +1975,18 @@ void srv_wake_master_thread(void) { reading this value as it is only used in heuristics. @return activity count. */ ulint srv_get_activity_count(void) { - return (srv_sys == nullptr ? 0 : srv_sys->activity_count); + DBUG_EXECUTE_IF("srv_increase_activity_count", + srv_sys->activity_count.add(644245092);); + /* bug#114180: + '0' is type of signed int (4 bytes), srv_sys->activity_count is an object + that overrides the () operator and has the data type ulint (8 bytes). + Implicit conversions between the two may lose precision and should be + avoided. */ + if (srv_sys == nullptr) { + return 0; + } + + return srv_sys->activity_count; } /** Check if there has been any activity.