Permalink
Browse files

Bug #18591145: SOME MONOTONICALLY INCREASING STATUS

VARIABLES DICREASES UNEXPECTEDLY

Analysis
--------
"SHOW STATUS VARIABLES" shows incorrect status values when
concurrent connection is executing statement to change user
or is being disconnected.

During switch user/session disconnect session status
variables are merged to global status variables. After
this, session variables are not reset for a while. During
this time select of global status variable from another
session will result in wrong data as all the session values
get added to global to calculate final value. Effectively
current session status variable value gets added twice.

Fix:
---
Code added to reset session variable after adding global
status variable for change user and disconnect operations.

Test:
----
mtr test added for switch user flow. Session disconnect
is tested with debugger as DEBUG_SYNC suite will not
work during thread disconnect.
  • Loading branch information...
Ajo Robert
Ajo Robert committed Jun 1, 2015
1 parent 7a36c15 commit c8243dd36047debb76134344d761e48f0cedf78e
Showing with 75 additions and 0 deletions.
  1. +23 −0 mysql-test/r/status_debug.result
  2. +45 −0 mysql-test/t/status_debug.test
  3. +3 −0 sql/sql_class.cc
  4. +4 −0 sql/sql_show.cc
@@ -0,0 +1,23 @@
#
# Bug#18591145 - SOME MONOTONICALLY INCREASING STATUS VARIABLES DECREASES UNEXPECTEDLY
#
CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64), val VARCHAR(1024));
# Insert 1 tuple to increment com_insert status.
INSERT INTO t1(name, val) VALUES ('dummy', 0);
connect con1, localhost, root,,;
SET DEBUG_SYNC='before_preparing_global_status_array SIGNAL change_user WAIT_FOR continue';
SET DEBUG_SYNC='after_preparing_global_status_array SIGNAL continue_change_user';
INSERT INTO t1(name, val) SELECT * FROM INFORMATION_SCHEMA.global_status WHERE variable_name='com_insert';;
connection default;
SET DEBUG_SYNC='now WAIT_FOR change_user';
SET DEBUG_SYNC='thd_cleanup_start SIGNAL continue WAIT_FOR continue_change_user';
connection con1;
SET DEBUG_SYNC='RESET';
connection default;
INSERT INTO t1(name, val)
SELECT * FROM INFORMATION_SCHEMA.global_status WHERE variable_name='com_insert';
SELECT (SELECT val FROM t1 WHERE id = 2) - (SELECT val FROM t1 WHERE id = 3);
(SELECT val FROM t1 WHERE id = 2) - (SELECT val FROM t1 WHERE id = 3)
0
disconnect con1;
DROP TABLE t1;
@@ -0,0 +1,45 @@
#
--source include/have_debug_sync.inc
# Save the initial number of concurrent sessions.
--source include/count_sessions.inc
--echo #
--echo # Bug#18591145 - SOME MONOTONICALLY INCREASING STATUS VARIABLES DECREASES UNEXPECTEDLY
--echo #
--enable_connect_log
CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64), val VARCHAR(1024));
--echo # Insert 1 tuple to increment com_insert status.
INSERT INTO t1(name, val) VALUES ('dummy', 0);
connect (con1, localhost, root,,);
SET DEBUG_SYNC='before_preparing_global_status_array SIGNAL change_user WAIT_FOR continue';
SET DEBUG_SYNC='after_preparing_global_status_array SIGNAL continue_change_user';
--SEND INSERT INTO t1(name, val) SELECT * FROM INFORMATION_SCHEMA.global_status WHERE variable_name='com_insert';
CONNECTION default;
SET DEBUG_SYNC='now WAIT_FOR change_user';
SET DEBUG_SYNC='thd_cleanup_start SIGNAL continue WAIT_FOR continue_change_user';
--change_user root,,test
CONNECTION con1;
--REAP
SET DEBUG_SYNC='RESET';
CONNECTION default;
INSERT INTO t1(name, val)
SELECT * FROM INFORMATION_SCHEMA.global_status WHERE variable_name='com_insert';
# With fix, difference of com_insert status should be "0" here.
SELECT (SELECT val FROM t1 WHERE id = 2) - (SELECT val FROM t1 WHERE id = 3);
# Cleanup
DISCONNECT con1;
DROP TABLE t1;
--disable_connect_log
# Wait till we reached the initial number of concurrent sessions
--source include/wait_until_count_sessions.inc
View
@@ -1454,6 +1454,7 @@ void THD::change_user(void)
{
mysql_mutex_lock(&LOCK_status);
add_to_status(&global_status_var, &status_var);
memset(&status_var, 0, sizeof(status_var));
mysql_mutex_unlock(&LOCK_status);
cleanup();
@@ -1477,6 +1478,7 @@ void THD::cleanup(void)
{
DBUG_ENTER("THD::cleanup");
DBUG_ASSERT(cleanup_done == 0);
DEBUG_SYNC(this, "thd_cleanup_start");
killed= KILL_CONNECTION;
#ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE
@@ -1556,6 +1558,7 @@ void THD::release_resources()
mysql_mutex_lock(&LOCK_status);
add_to_status(&global_status_var, &status_var);
memset(&status_var, 0, sizeof(status_var));
mysql_mutex_unlock(&LOCK_status);
/* Ensure that no one is using THD */
View
@@ -6737,6 +6737,8 @@ int fill_status(THD *thd, TABLE_LIST *tables, Item *cond)
Avoid recursive acquisition of LOCK_status in cases when WHERE clause
represented by "cond" contains subquery on I_S.SESSION/GLOBAL_STATUS.
*/
DEBUG_SYNC(thd, "before_preparing_global_status_array");
if (thd->fill_status_recursion_level++ == 0)
mysql_mutex_lock(&LOCK_status);
if (option_type == OPT_GLOBAL)
@@ -6747,6 +6749,8 @@ int fill_status(THD *thd, TABLE_LIST *tables, Item *cond)
upper_case_names, cond);
if (thd->fill_status_recursion_level-- == 1)
mysql_mutex_unlock(&LOCK_status);
DEBUG_SYNC(thd, "after_preparing_global_status_array");
DBUG_RETURN(res);
}

0 comments on commit c8243dd

Please sign in to comment.