Skip to content

Commit cee13cb

Browse files
MDEV-23283 User Statistics does not correctly reflect concurrent_connections
Keep concurrent_connections updated when user stats are enabled. When a user connects, the count in concurrent_connections increases by one; when they disconnect, it decreases by one. Setting the global userstat begins counting new concurrent sessions from that point forward; pre-existing concurrent sessions are not counted until they are restarted. Ideally this configuration is set in the my.cnf file so as to apply from server startup, ensuring that the counter is consistent. If it is set during the server's lifetime (via SET GLOBAL userstat=1) it is recommended to do so before concurrent user sessions exist for accurate counts.
1 parent 77a3d7a commit cee13cb

File tree

5 files changed

+134
-37
lines changed

5 files changed

+134
-37
lines changed

client/mysqltest.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5767,8 +5767,12 @@ void do_close_connection(struct st_command *command)
57675767
DBUG_PRINT("info", ("Closing connection %s", con->name));
57685768
#ifndef EMBEDDED_LIBRARY
57695769
if (command->type == Q_DIRTY_CLOSE)
5770-
{
57715770
mariadb_cancel(con->mysql);
5771+
else
5772+
{
5773+
simple_command(con->mysql,COM_QUIT,0,0,0);
5774+
if (con->util_mysql)
5775+
simple_command(con->util_mysql,COM_QUIT,0,0,0);
57725776
}
57735777
#endif /*!EMBEDDED_LIBRARY*/
57745778
if (con->stmt)

mysql-test/main/userstat-badlogin-4824.test

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
#
22
# MDEV-4824 userstats - wrong user statistics
33
#
4-
# Tests will be skipped for the view protocol because the view protocol creates
4+
# Tests will be skipped for the view protocol because the view protocol creates
55
# an additional util connection and other statistics data
6-
-- source include/no_view_protocol.inc
6+
# Skip this test for ps protocol because commit bead24b changes the expected
7+
# the value of bytes_expected in the userstats output.
8+
--source include/no_view_protocol.inc
9+
--disable_ps_protocol
710

811
--source include/not_embedded.inc
912
set @save_userstat=@@global.userstat;
@@ -40,3 +43,4 @@ select user, bytes_received from information_schema.user_statistics where user =
4043

4144
drop user foo@localhost;
4245
set global userstat=@save_userstat;
46+
--enable_ps_protocol

mysql-test/main/userstat.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ Table_schema Table_name Index_name Rows_read
150150
select TOTAL_CONNECTIONS, TOTAL_SSL_CONNECTIONS, CONCURRENT_CONNECTIONS, ROWS_READ, ROWS_SENT, ROWS_DELETED, ROWS_INSERTED, ROWS_UPDATED, SELECT_COMMANDS, UPDATE_COMMANDS, COMMIT_TRANSACTIONS, ROLLBACK_TRANSACTIONS, DENIED_CONNECTIONS, LOST_CONNECTIONS, ACCESS_DENIED, EMPTY_QUERIES from information_schema.client_statistics;;
151151
TOTAL_CONNECTIONS 2
152152
TOTAL_SSL_CONNECTIONS 1
153-
CONCURRENT_CONNECTIONS 0
153+
CONCURRENT_CONNECTIONS 1
154154
ROWS_READ 6
155155
ROWS_SENT 3
156156
ROWS_DELETED 1
@@ -167,7 +167,7 @@ EMPTY_QUERIES 1
167167
select TOTAL_CONNECTIONS, TOTAL_SSL_CONNECTIONS, CONCURRENT_CONNECTIONS, ROWS_READ, ROWS_SENT, ROWS_DELETED, ROWS_INSERTED, ROWS_UPDATED, SELECT_COMMANDS, UPDATE_COMMANDS, COMMIT_TRANSACTIONS, ROLLBACK_TRANSACTIONS, DENIED_CONNECTIONS, LOST_CONNECTIONS, ACCESS_DENIED, EMPTY_QUERIES from information_schema.user_statistics;;
168168
TOTAL_CONNECTIONS 2
169169
TOTAL_SSL_CONNECTIONS 1
170-
CONCURRENT_CONNECTIONS 0
170+
CONCURRENT_CONNECTIONS 1
171171
ROWS_READ 6
172172
ROWS_SENT 3
173173
ROWS_DELETED 1

mysql-test/suite/rpl/t/rpl_semi_sync_shutdown_await_ack.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ SET GLOBAL debug_dbug="+d,simulate_delay_semisync_slave_reply";
219219
--connection server_2
220220
set debug_sync= "now wait_for io_thd_at_slave_reply";
221221

222-
--disconnect con1
222+
--dirty_close con1
223223

224224
--connection default
225225
--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect

sql/sql_connect.cc

Lines changed: 120 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -535,26 +535,27 @@ void free_global_client_stats(void)
535535
my_hash_free(&global_client_stats);
536536
}
537537

538+
538539
/*
539-
Increments the global stats connection count for an entry from
540-
global_client_stats or global_user_stats. Returns 0 on success
541-
and 1 on error.
542-
*/
540+
Common code for increment_count_by_name, decrement_count_by_name, to fetch
541+
the USER_STATS corresponding to 'name'.
542+
*/
543543

544-
static bool increment_count_by_name(const char *name, size_t name_length,
545-
const char *role_name,
546-
HASH *users_or_clients, THD *thd)
544+
static USER_STATS* count_by_name_common(const char *name, size_t name_length,
545+
const char *role_name,
546+
HASH *users_or_clients, THD *thd)
547547
{
548-
USER_STATS *user_stats;
548+
USER_STATS *user_stats= nullptr;
549549

550-
if (!(user_stats= (USER_STATS*) my_hash_search(users_or_clients, (uchar*) name,
551-
name_length)))
550+
if (!(user_stats= (USER_STATS*) my_hash_search(users_or_clients,
551+
(uchar*) name,
552+
name_length)))
552553
{
553554
/* First connection for this user or client */
554555
if (!(user_stats= ((USER_STATS*)
555556
my_malloc(PSI_INSTRUMENT_ME, sizeof(USER_STATS),
556557
MYF(MY_WME | MY_ZEROFILL)))))
557-
return TRUE; // Out of memory
558+
return nullptr; // Out of memory
558559

559560
init_user_stats(user_stats, name, name_length, role_name,
560561
0, 0, 0, // connections
@@ -573,12 +574,68 @@ static bool increment_count_by_name(const char *name, size_t name_length,
573574
if (my_hash_insert(users_or_clients, (uchar*)user_stats))
574575
{
575576
my_free(user_stats);
576-
return TRUE; // Out of memory
577+
return nullptr; // Out of memory
577578
}
578579
}
579-
user_stats->total_connections++;
580+
581+
DBUG_ASSERT(user_stats);
582+
return user_stats;
583+
}
584+
585+
586+
/*
587+
Increments the global stats connection count for an entry from
588+
global_client_stats or global_user_stats. Returns FALSE on success
589+
and TRUE on error.
590+
*/
591+
592+
static bool increment_count_by_name(const char *name, size_t name_length,
593+
const char *role_name,
594+
HASH *users_or_clients, THD *thd)
595+
{
596+
USER_STATS *user_stats= count_by_name_common(name, name_length, role_name,
597+
users_or_clients, thd);
598+
if (!user_stats)
599+
return TRUE;
600+
601+
++user_stats->total_connections;
602+
#ifndef EMBEDDED_LIBRARY
603+
/*
604+
For the embedded library, we get here only because THD::update_all_stats
605+
is called after command dispatch, not because of any connection events
606+
(those are compiled-out for the embedded library). Maybe this entire
607+
function should do nothing in the case of embedded library? At least
608+
this makes it behave the same way it did before supporting
609+
concurrent_connections.
610+
*/
611+
++user_stats->concurrent_connections;
612+
#endif
580613
if (thd->net.vio && thd->net.vio->type == VIO_TYPE_SSL)
581-
user_stats->total_ssl_connections++;
614+
++user_stats->total_ssl_connections;
615+
616+
return FALSE;
617+
}
618+
619+
620+
/*
621+
Decrements the global stats connection count for an entry from
622+
global_client_stats or global_user_stats. Returns FALSE on success
623+
and TRUE on error.
624+
*/
625+
626+
#ifndef EMBEDDED_LIBRARY
627+
static bool decrement_count_by_name(const char *name, size_t name_length,
628+
const char *role_name,
629+
HASH *users_or_clients, THD *thd)
630+
{
631+
USER_STATS *user_stats= count_by_name_common(name, name_length, role_name,
632+
users_or_clients, thd);
633+
if (!user_stats)
634+
return TRUE;
635+
636+
if (user_stats->concurrent_connections > 0)
637+
--user_stats->concurrent_connections;
638+
582639
return FALSE;
583640
}
584641

@@ -592,36 +649,65 @@ static bool increment_count_by_name(const char *name, size_t name_length,
592649
@retval 1 error.
593650
*/
594651

595-
#ifndef EMBEDDED_LIBRARY
596-
static bool increment_connection_count(THD* thd, bool use_lock)
652+
static bool increment_connection_count(THD* thd)
597653
{
598654
const char *user_string= get_valid_user_string(thd->main_security_ctx.user);
599655
const char *client_string= get_client_host(thd);
600-
bool return_value= FALSE;
601656

602657
if (!thd->userstat_running)
603658
return FALSE;
604659

605-
if (use_lock)
606-
mysql_mutex_lock(&LOCK_global_user_client_stats);
660+
mysql_mutex_lock(&LOCK_global_user_client_stats);
661+
SCOPE_EXIT([] () {
662+
mysql_mutex_unlock(&LOCK_global_user_client_stats);
663+
});
607664

608665
if (increment_count_by_name(user_string, strlen(user_string), user_string,
609666
&global_user_stats, thd))
610-
{
611-
return_value= TRUE;
612-
goto end;
613-
}
667+
return TRUE;
668+
614669
if (increment_count_by_name(client_string, strlen(client_string),
615670
user_string, &global_client_stats, thd))
616-
{
617-
return_value= TRUE;
618-
goto end;
619-
}
671+
return TRUE;
620672

621-
end:
622-
if (use_lock)
673+
return FALSE;
674+
}
675+
676+
static bool decrement_connection_count(THD* thd)
677+
{
678+
const char *user_string= get_valid_user_string(thd->main_security_ctx.user);
679+
const char *client_string= get_client_host(thd);
680+
681+
/*
682+
THD::update_all_stats, called only from dispatch_command, clears
683+
thd->userstat_running to avoid double counting. thd->userstat_running
684+
is set during THD::init.
685+
686+
When a user connects for the first time, thd->userstat_running is set
687+
from the global variable opt_userstat_running during THD::init (indirectly
688+
called from THD::change_user). After each dispatched command, as noted
689+
above, it is cleared (even if the user maintains the connection). So for
690+
normal cases where the user disconnects after running a query, we need to
691+
check opt_userstat_running. We check thd->userstat_running for abnormal
692+
cases where the user disconnects during a dispatched command, before it
693+
reaches THD::update_all_stats.
694+
*/
695+
if (!thd->userstat_running && !opt_userstat_running)
696+
return FALSE;
697+
698+
mysql_mutex_lock(&LOCK_global_user_client_stats);
699+
SCOPE_EXIT([] () {
623700
mysql_mutex_unlock(&LOCK_global_user_client_stats);
624-
return return_value;
701+
});
702+
703+
if (decrement_count_by_name(user_string, strlen(user_string), user_string,
704+
&global_user_stats, thd))
705+
return TRUE;
706+
if (decrement_count_by_name(client_string, strlen(client_string),
707+
user_string, &global_client_stats, thd))
708+
return TRUE;
709+
710+
return FALSE;
625711
}
626712
#endif
627713

@@ -1143,7 +1229,7 @@ bool login_connection(THD *thd)
11431229
my_net_set_write_timeout(net, thd->variables.net_write_timeout);
11441230

11451231
/* Updates global user connection stats. */
1146-
if (increment_connection_count(thd, TRUE))
1232+
if (increment_connection_count(thd))
11471233
{
11481234
my_error(ER_OUTOFMEMORY, MYF(0), (int) (2*sizeof(USER_STATS)));
11491235
error= 1;
@@ -1202,6 +1288,9 @@ void end_connection(THD *thd)
12021288
if (likely(!thd->killed) && (net->error && net->vio != 0))
12031289
thd->print_aborted_warning(1, thd->get_stmt_da()->is_error()
12041290
? thd->get_stmt_da()->message() : ER_THD(thd, ER_UNKNOWN_ERROR));
1291+
1292+
if (decrement_connection_count(thd))
1293+
my_error(ER_OUTOFMEMORY, MYF(ME_ERROR_LOG), (int) (2*sizeof(USER_STATS)));
12051294
}
12061295

12071296

0 commit comments

Comments
 (0)