Description:
The follow sql return a negative value.
mysql> SELECT NAME, COUNT FROM information_schema.INNODB_METRICS WHERE name='innodb_activity_count';
+-----------------------+------------+
| NAME | COUNT |
+-----------------------+------------+
| innodb_activity_count | -429496559 |
+-----------------------+------------+
How to repeat:
From the 8.0.36 code, we can found that innodb_activity_count is obtained in srv0mon.cc:1940:
value = srv_get_activity_count();
The return type of srv_get_activity_count() is ulint, but the type of value is int64_t.
innodb_activity_count will only increase, not decrease. When the innodb_activity_count
is more than 2147483648, it will be interpreted as a signed number(int64_t) in line srv0mon.cc:1940
of the code.
In a normal scenario, it takes a long time to increase innodb_activity_count above 2147483648,
resulting in a negative query result.
So I constructed a test case to repeat the problem, and you need to modify the code in debug mode to quickly
increase the innodb_activity_count value to repeat this problem.
Here is the code diff based on 8.0.36. You can import my diff file into 8.0.36, compile it into debug mode:
diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc
index 3184176f7ae..44fd8dfe8ad 100644
--- a/storage/innobase/srv/srv0srv.cc
+++ b/storage/innobase/srv/srv0srv.cc
@@ -1975,6 +1975,10 @@ 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) {
+ DBUG_EXECUTE_IF(
+ "srv_increase_activity_count",
+ srv_sys->activity_count.add(644245092););
+
return (srv_sys == nullptr ? 0 : srv_sys->activity_count);
}
Then run the follow test case:
DELIMITER |;
CREATE PROCEDURE select_innodb_activity_count()
BEGIN
DECLARE i int DEFAULT 1;
WHILE (i <= 6) DO
SELECT NAME, COUNT FROM information_schema.INNODB_METRICS WHERE name='innodb_activity_count';
SET i = i + 1;
END WHILE;
END |
DELIMITER ;|
SET GLOBAL DEBUG= '+d, srv_increase_activity_count';
call select_innodb_activity_count();
SET GLOBAL DEBUG= '-d, srv_increase_activity_count';
DROP PROCEDURE select_innodb_activity_count;
The result is:
call select_innodb_activity_count();
NAME COUNT
innodb_activity_count 644245277
NAME COUNT
innodb_activity_count 1288490369
NAME COUNT
innodb_activity_count 1932735461
NAME COUNT
innodb_activity_count -1717986743
NAME COUNT
innodb_activity_count -1073741651
NAME COUNT
innodb_activity_count -429496559
Suggested fix:
Avoid the problem of innodb_activity_count being negative when unsigned numbers are converted to signed numbers.
Description: The follow sql return a negative value. mysql> SELECT NAME, COUNT FROM information_schema.INNODB_METRICS WHERE name='innodb_activity_count'; +-----------------------+------------+ | NAME | COUNT | +-----------------------+------------+ | innodb_activity_count | -429496559 | +-----------------------+------------+ How to repeat: From the 8.0.36 code, we can found that innodb_activity_count is obtained in srv0mon.cc:1940: value = srv_get_activity_count(); The return type of srv_get_activity_count() is ulint, but the type of value is int64_t. innodb_activity_count will only increase, not decrease. When the innodb_activity_count is more than 2147483648, it will be interpreted as a signed number(int64_t) in line srv0mon.cc:1940 of the code. In a normal scenario, it takes a long time to increase innodb_activity_count above 2147483648, resulting in a negative query result. So I constructed a test case to repeat the problem, and you need to modify the code in debug mode to quickly increase the innodb_activity_count value to repeat this problem. Here is the code diff based on 8.0.36. You can import my diff file into 8.0.36, compile it into debug mode: diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index 3184176f7ae..44fd8dfe8ad 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -1975,6 +1975,10 @@ 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) { + DBUG_EXECUTE_IF( + "srv_increase_activity_count", + srv_sys->activity_count.add(644245092);); + return (srv_sys == nullptr ? 0 : srv_sys->activity_count); } Then run the follow test case: DELIMITER |; CREATE PROCEDURE select_innodb_activity_count() BEGIN DECLARE i int DEFAULT 1; WHILE (i <= 6) DO SELECT NAME, COUNT FROM information_schema.INNODB_METRICS WHERE name='innodb_activity_count'; SET i = i + 1; END WHILE; END | DELIMITER ;| SET GLOBAL DEBUG= '+d, srv_increase_activity_count'; call select_innodb_activity_count(); SET GLOBAL DEBUG= '-d, srv_increase_activity_count'; DROP PROCEDURE select_innodb_activity_count; The result is: call select_innodb_activity_count(); NAME COUNT innodb_activity_count 644245277 NAME COUNT innodb_activity_count 1288490369 NAME COUNT innodb_activity_count 1932735461 NAME COUNT innodb_activity_count -1717986743 NAME COUNT innodb_activity_count -1073741651 NAME COUNT innodb_activity_count -429496559 Suggested fix: Avoid the problem of innodb_activity_count being negative when unsigned numbers are converted to signed numbers.