Bug #99039 PSI thread info not getting updated after statement execution
Submitted: 23 Mar 2020 14:29 Modified: 24 Mar 2020 7:58
Reporter: Pranay Motupalli Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Performance Schema Severity:S3 (Non-critical)
Version:>= 5.6.46 , >=5.7.28, >=8.0.18, 5.6.47, 5.7.29, 8.0.19 OS:Any
Assigned to: CPU Architecture:Any
Tags: regression

[23 Mar 2020 14:29] Pranay Motupalli
Description:
After the execution of a statement, PROCESSLIST_INFO is not getting updated in PERFORMANCE_SCHEMA.THREADS table.

How to repeat:
>= 5.6.46
=========

mysql> select @@performance_schema,@@version;
+----------------------+------------+
| @@performance_schema | @@version  |
+----------------------+------------+
|                    1 | 5.6.46-log |
+----------------------+------------+
1 row in set (0.01 sec)

Session 1:
----------

mysql> select now(),connection_id();
+---------------------+-----------------+
| now()               | connection_id() |
+---------------------+-----------------+
| 2020-03-23 14:11:14 |             109 |
+---------------------+-----------------+
1 row in set (0.00 sec)

Opening Session keeping Session 1 open

Session 2:
----------

Reading from performance_schema.threads

mysql> select * from performance_schema.threads where PROCESSLIST_ID=109\G
*************************** 1. row ***************************
          THREAD_ID: 128
               NAME: thread/sql/one_connection
               TYPE: FOREGROUND
     PROCESSLIST_ID: 109
   PROCESSLIST_USER: root
   PROCESSLIST_HOST: 172.31.41.31
     PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Sleep
   PROCESSLIST_TIME: 18
  PROCESSLIST_STATE: NULL
   PROCESSLIST_INFO: select now(),connection_id()
   PARENT_THREAD_ID: NULL
               ROLE: NULL
       INSTRUMENTED: YES
1 row in set (0.00 sec)

Reading from information_schema.processlist

mysql> select * from information_schema.processlist where id=109\G
*************************** 1. row ***************************
     ID: 109
   USER: root
   HOST: 172.31.41.31:57020
     DB: NULL
COMMAND: Sleep
   TIME: 55
  STATE: 
   INFO: NULL
1 row in set (0.01 sec)

<= 5.6.45:
==========

mysql> select @@version,@@performance_schema;
+------------+----------------------+
| @@version  | @@performance_schema |
+------------+----------------------+
| 5.6.44-log |                    1 |
+------------+----------------------+
1 row in set (0.00 sec)

Session 1:
---------

mysql> select now(),connection_id();
+---------------------+-----------------+
| now()               | connection_id() |
+---------------------+-----------------+
| 2020-03-23 14:08:55 |              98 |
+---------------------+-----------------+
1 row in set (0.00 sec)

Opening Session keeping Session 1 open

Session 2:
----------

Reading from performance_schema.threads

mysql> select * from performance_schema.threads where PROCESSLIST_ID=98\G
*************************** 1. row ***************************
          THREAD_ID: 117
               NAME: thread/sql/one_connection
               TYPE: FOREGROUND
     PROCESSLIST_ID: 98
   PROCESSLIST_USER: root
   PROCESSLIST_HOST: 172.31.41.31
     PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Sleep
   PROCESSLIST_TIME: 74
  PROCESSLIST_STATE: NULL
   PROCESSLIST_INFO: NULL
   PARENT_THREAD_ID: 1
               ROLE: NULL
       INSTRUMENTED: YES
1 row in set (0.00 sec)

Reading from information_schema.processlist

mysql> select * from information_schema.processlist where id=98\G
*************************** 1. row ***************************
     ID: 98
   USER: root
   HOST: 172.31.41.31:40716
     DB: NULL
COMMAND: Sleep
   TIME: 110
  STATE: 
   INFO: NULL
1 row in set (0.00 sec)

####################################################

MySQL <=5.6.45:
==============

Thread 26 "mysqld" hit Breakpoint 7, THD::set_query (this=0x5623f453b540, string_arg=...) at /root/mysql-server/sql/sql_class.cc:4674
4674	  mysql_mutex_lock(&LOCK_thd_data);
(gdb) n
4675	  set_query_inner(string_arg);
(gdb) n
4676	  mysql_mutex_unlock(&LOCK_thd_data);
(gdb) n
4679	  PSI_THREAD_CALL(set_thread_info)(query(), query_length());
(gdb) bt
#0  THD::set_query (this=0x5623f453b540, string_arg=...) at /root/mysql-server/sql/sql_class.cc:4679
#1  0x00005623f097f7d6 in THD::reset_query (this=0x5623f453b540) at /root/mysql-server/sql/sql_class.h:4042
#2  0x00005623f096e767 in dispatch_command (command=COM_QUERY, thd=0x5623f453b540, packet=0x5623f453ed81 "", packet_length=12)
    at /root/mysql-server/sql/sql_parse.cc:1839
#3  0x00005623f096c138 in do_command (thd=0x5623f453b540) at /root/mysql-server/sql/sql_parse.cc:1064
#4  0x00005623f09318c6 in do_handle_one_connection (thd_arg=0x5623f453b540) at /root/mysql-server/sql/sql_connect.cc:982
#5  0x00005623f0931614 in handle_one_connection (arg=0x5623f453b540) at /root/mysql-server/sql/sql_connect.cc:899
#6  0x00005623f0fe6149 in pfs_spawn_thread (arg=0x5623f410dfe0) at /root/mysql-server/storage/perfschema/pfs.cc:1861
#7  0x00007f5a22f0c6db in start_thread (arg=0x7f59f9825700) at pthread_create.c:463
#8  0x00007f5a222f688f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

https://github.com/mysql/mysql-server/blob/mysql-5.6.44/sql/sql_class.cc

Function:
========

void reset_query()               /* Mutex protected */
  { set_query(CSET_STRING()); }

void THD::set_query(const CSET_STRING &string_arg)
{
  mysql_mutex_lock(&LOCK_thd_data);
  set_query_inner(string_arg);
  mysql_mutex_unlock(&LOCK_thd_data);

#ifdef HAVE_PSI_THREAD_INTERFACE
  PSI_THREAD_CALL(set_thread_info)(query(), query_length());
#endif
}

MySQL >= 5.6.46
################

Thread 22 "mysqld" hit Breakpoint 11, mysql_execute_command (thd=0x559bd73d30e0) at /root/mysql-server/sql/sql_parse.cc:2355
2355	{
..
..
..
5039	  goto finish;
(gdb) n
5046	  DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() ||
(gdb) n
5049	  if (! thd->in_sub_stmt)
(gdb) p thd->in_sub_stmt
$12 = 0
(gdb) n
5052	    if (thd->killed_errno())
(gdb) n
5054	    if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
(gdb) p thd->killed
$13 = THD::NOT_KILLED

(gdb) bt
#0  mysql_execute_command (thd=0x559bd73d30e0) at /root/mysql-server/sql/sql_parse.cc:5054
#1  0x0000559bd4ab59e5 in mysql_parse (thd=0x559bd73d30e0, rawbuf=0x7f6154005300 "select now()", length=12, parser_state=0x7f618311c6a0)
    at /root/mysql-server/sql/sql_parse.cc:6435
#2  0x0000559bd4aa8574 in dispatch_command (command=COM_QUERY, thd=0x559bd73d30e0, packet=0x559bd750fcc1 "", packet_length=12)
    at /root/mysql-server/sql/sql_parse.cc:1406
#3  0x0000559bd4aa75a8 in do_command (thd=0x559bd73d30e0) at /root/mysql-server/sql/sql_parse.cc:1071
#4  0x0000559bd4a6cdb5 in do_handle_one_connection (thd_arg=0x559bd73d30e0) at /root/mysql-server/sql/sql_connect.cc:989
#5  0x0000559bd4a6cb30 in handle_one_connection (arg=0x559bd73d30e0) at /root/mysql-server/sql/sql_connect.cc:906
#6  0x0000559bd5120983 in pfs_spawn_thread (arg=0x559bd746cce0) at /root/mysql-server/storage/perfschema/pfs.cc:1868
#7  0x00007f61ac70a6db in start_thread (arg=0x7f618311d700) at pthread_create.c:463
#8  0x00007f61abaf488f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Code: https://github.com/mysql/mysql-server/blob/mysql-5.6.46/sql/sql_parse.cc

Function: mysql_execute_command

#############################################################

DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() ||
               thd->in_multi_stmt_transaction_mode());

  if (! thd->in_sub_stmt)
  {
    /* report error issued during command execution */
    if (thd->killed_errno())
      thd->send_kill_message();
    if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
    {
      thd->killed= THD::NOT_KILLED;
      thd->mysys_var->abort= 0;
      thd->reset_query_for_display();
    }
    if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
      trans_rollback_stmt(thd);
    else
    {
      /* If commit fails, we should be able to reset the OK status. */
      thd->get_stmt_da()->set_overwrite_status(true);
      trans_commit_stmt(thd);
      thd->get_stmt_da()->set_overwrite_status(false);
    }
  }

Code: https://github.com/mysql/mysql-server/blob/mysql-5.6.46/sql/sql_class.h

void set_query_for_display(const char *query_arg, size_t query_length_arg) {
    MYSQL_SET_STATEMENT_TEXT(m_statement_psi, query_arg, query_length_arg);
#ifdef HAVE_PSI_THREAD_INTERFACE
    PSI_THREAD_CALL(set_thread_info)(query_arg, query_length_arg);
#endif
  }

void reset_query_for_display(void) { set_query_for_display(NULL, 0); }

##############################################################

Looks like in >=5.6.46 versions, MySQL started using set_query_for_display and reset_query_for_display functions. However, in this case, it looks like reset_query_for_display is not being called after execution of the query. 

As per MySQL doc https://dev.mysql.com/doc/mysql-perfschema-excerpt/5.6/en/threads-table.html,

PROCESSLIST_INFO

The statement the thread is executing, or NULL if it is not executing any statement. The statement might be the one sent to the server, or an innermost statement if the statement executes other statements. For example, if a CALL statement executes a stored procedure that is executing a SELECT statement, the PROCESSLIST_INFO value shows the SELECT statement. 

In this case, MySQL is showing the last executed statement in PROCESSLIST_INFO even after the completion of statement execution which is violating the above statement.

Suggested fix:
This regression has been caused due to changes made in commit https://github.com/mysql/mysql-server/commit/4012ca94f66aa4a4c963b1f58b349f28f7ba4c23

May be reset_query_for_display function can be called after completion of statement execution to fix this issue.
[23 Mar 2020 15:05] Pranay Motupalli
Affected Versions:
#################
>=5.6.46
>=5.7.28
>=8.0.18
[24 Mar 2020 7:58] MySQL Verification Team
Hello Pranay,

Thank you for the report and test case.
Verified as described with 5.6.47 build.

regards,
Umesh