Skip to content

Commit

Permalink
Merge pull request #5452 from kamil-holubicki/PS-9382-8.0
Browse files Browse the repository at this point in the history
PS-9453: percona_telemetry causes a long wait on COND_thd_list due to the absence of the root user
  • Loading branch information
kamil-holubicki authored Oct 16, 2024
2 parents 73ddb41 + 22edfb4 commit 60a65b3
Show file tree
Hide file tree
Showing 20 changed files with 226 additions and 40 deletions.
44 changes: 31 additions & 13 deletions components/percona_telemetry/data_provider.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@
namespace {
inline const char *b2s(bool val) { return val ? "1" : "0"; }

/*
mysql.session user is mostly enough, but it lacks the following privileges:
1. REPLICATION SLAVE
2. REPLICATION CLIENT
3. SELECT on mysql.component
4. SELECT on performance_schema.replication_group_members
These privileges are added at server startup in setup_percona_telemetry()
if Percona telemetry is enabled.
*/
constexpr const char default_command_user_name[] = "mysql.session";
constexpr const char default_command_host_name[] = "localhost";

namespace JSONKey {
const char *pillar_version = "pillar_version";
const char *db_instance_id = "db_instance_id";
Expand Down Expand Up @@ -120,25 +134,29 @@ bool DataProvider::do_query(const std::string &query, QueryResult *result,
}
result->clear();

/* command_factory_service_.init() allocates memory for mysql_h
We need to call close() always.
Even if init() fails, becaues it doesn't allocate anything, calling close()
is safe, because internally it checks if provided pointer is valid
*/
std::shared_ptr<MYSQL_H> mysql_h_close_guard(
&mysql_h, [&srv = command_factory_service_](MYSQL_H *ptr) {
srv.close(*ptr);
});

mysql_service_status_t sstatus = command_factory_service_.init(&mysql_h);

if (!sstatus)
sstatus |=
command_options_service_.set(mysql_h, MYSQL_COMMAND_PROTOCOL, nullptr);
if (!sstatus)
sstatus |=
command_options_service_.set(mysql_h, MYSQL_COMMAND_USER_NAME, "root");
sstatus |= command_options_service_.set(mysql_h, MYSQL_COMMAND_USER_NAME,
default_command_user_name);
if (!sstatus)
sstatus |=
command_options_service_.set(mysql_h, MYSQL_COMMAND_HOST_NAME, nullptr);
sstatus |= command_options_service_.set(mysql_h, MYSQL_COMMAND_HOST_NAME,
default_command_host_name);
if (!sstatus) sstatus |= command_factory_service_.connect(mysql_h);

// starting from this point, if the above succeeded we need to close mysql_h.
std::shared_ptr<void> mysql_h_close_guard(
mysql_h,
[&srv = command_factory_service_, do_close = !sstatus](void *ptr) {
if (do_close && ptr) srv.close(static_cast<MYSQL_H>(ptr));
});

// if any of the above failed, just exit
if (sstatus) {
goto err;
Expand Down Expand Up @@ -212,7 +230,7 @@ bool DataProvider::collect_db_instance_id_info(rapidjson::Document *document) {
so the SQL query failed. It will recover next time.
2. Some other reason that caused selecting server_id to fail. */
if (id.length() == 0) {
logger_.warning(
logger_.info(
"Collecting db_instance_id failed. It may be caused by server still "
"initializing.");
return true;
Expand Down Expand Up @@ -346,7 +364,7 @@ bool DataProvider::collect_se_usage_info(rapidjson::Document *document) {
QueryResult result;
if (do_query("SELECT DISTINCT ENGINE FROM information_schema.tables WHERE "
"table_schema NOT IN('mysql', 'information_schema', "
"'performance_schema', 'sys');",
"'performance_schema', 'sys')",
&result)) {
return true;
}
Expand Down
8 changes: 6 additions & 2 deletions mysql-test/r/grant.result
Original file line number Diff line number Diff line change
Expand Up @@ -831,7 +831,9 @@ show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO `mysqltest_8`@`%`
GRANT UPDATE ON `test`.`t1` TO `mysqltest_8`@`%`
select * from information_schema.table_privileges where table_schema NOT IN ('sys','mysql');
select * from information_schema.table_privileges where table_schema NOT IN ('sys','mysql')
and not (grantee = "'mysql.session'@'localhost'"
and table_name in ('component', 'replication_group_members'));
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE
'mysqltest_8'@'%' def test t1 UPDATE NO
'mysqltest_8'@'' def test t1 UPDATE NO
Expand All @@ -845,7 +847,9 @@ GRANT USAGE ON *.* TO `mysqltest_8`@``
show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO `mysqltest_8`@`%`
select * from information_schema.table_privileges where table_schema NOT IN ('sys','mysql');
select * from information_schema.table_privileges where table_schema NOT IN ('sys','mysql')
and not (grantee = "'mysql.session'@'localhost'"
and table_name in ('component', 'replication_group_members'));
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE
flush privileges;
show grants for mysqltest_8@'';
Expand Down
4 changes: 3 additions & 1 deletion mysql-test/r/information_schema_ci.result
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,9 @@ grant select (a) on test.t1 to joe@localhost with grant option;
select * from INFORMATION_SCHEMA.COLUMN_PRIVILEGES WHERE table_schema != 'sys';
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME PRIVILEGE_TYPE IS_GRANTABLE
'joe'@'localhost' def test t1 a SELECT YES
select * from INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE table_schema NOT IN ('sys','mysql');
select * from INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE table_schema NOT IN ('sys','mysql')
and not (grantee = "'mysql.session'@'localhost'"
and table_name in ('component', 'replication_group_members'));
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE
drop view v1, v2, v3;
drop table t1;
Expand Down
4 changes: 3 additions & 1 deletion mysql-test/r/information_schema_cs.result
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,9 @@ grant select (a) on test.t1 to joe@localhost with grant option;
select * from INFORMATION_SCHEMA.COLUMN_PRIVILEGES WHERE table_schema != 'sys';
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME PRIVILEGE_TYPE IS_GRANTABLE
'joe'@'localhost' def test t1 a SELECT YES
select * from INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE table_schema NOT IN ('sys','mysql');
select * from INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE table_schema NOT IN ('sys','mysql')
and not (grantee = "'mysql.session'@'localhost'"
and table_name in ('component', 'replication_group_members'));
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE
drop view v1, v2, v3;
drop table t1;
Expand Down
8 changes: 6 additions & 2 deletions mysql-test/r/transactional_acl_tables.result
Original file line number Diff line number Diff line change
Expand Up @@ -1342,7 +1342,9 @@ SELECT host, db, user, table_name, column_name, column_priv FROM mysql.columns_p
host db user table_name column_name column_priv
h test u1 t1 a Select,Insert,Update,References
h test u1 t2 a Insert
SELECT host, db, user, table_name, grantor, table_priv, column_priv FROM mysql.tables_priv;
SELECT host, db, user, table_name, grantor, table_priv, column_priv FROM mysql.tables_priv
WHERE NOT (user = 'mysql.session'
AND table_name IN ('component', 'replication_group_members'));
host db user table_name grantor table_priv column_priv
h test u1 t1 root@localhost Select,Insert,Update,References
h test u1 t2 root@localhost Insert
Expand All @@ -1354,7 +1356,9 @@ COMMIT;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM u1@h;
SELECT host, db, user, table_name, column_name, column_priv FROM mysql.columns_priv;
host db user table_name column_name column_priv
SELECT host, db, user, table_name, grantor, table_priv, column_priv FROM mysql.tables_priv;
SELECT host, db, user, table_name, grantor, table_priv, column_priv FROM mysql.tables_priv
WHERE NOT (user = 'mysql.session'
AND table_name IN ('component', 'replication_group_members'));
host db user table_name grantor table_priv column_priv
localhost mysql mysql.session user root@localhost Select
localhost sys mysql.sys sys_config root@localhost Select
Expand Down
31 changes: 31 additions & 0 deletions mysql-test/suite/component_percona_telemetry/r/no_user.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
SELECT * FROM information_schema.table_privileges WHERE grantee = "'mysql.session'@'localhost'" ORDER BY table_schema, table_name;
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE
'mysql.session'@'localhost' def mysql component SELECT NO
'mysql.session'@'localhost' def mysql user SELECT NO
'mysql.session'@'localhost' def performance_schema replication_group_members SELECT NO
SHOW GRANTS FOR 'mysql.session'@'localhost';
Grants for mysql.session@localhost
GRANT SHUTDOWN, SUPER, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO `mysql.session`@`localhost`
GRANT AUDIT_ABORT_EXEMPT,AUTHENTICATION_POLICY_ADMIN,BACKUP_ADMIN,CLONE_ADMIN,CONNECTION_ADMIN,FIREWALL_EXEMPT,PERSIST_RO_VARIABLES_ADMIN,SESSION_VARIABLES_ADMIN,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN ON *.* TO `mysql.session`@`localhost`
GRANT SELECT ON `performance_schema`.* TO `mysql.session`@`localhost`
GRANT SELECT ON `mysql`.`component` TO `mysql.session`@`localhost`
GRANT SELECT ON `mysql`.`user` TO `mysql.session`@`localhost`
GRANT SELECT ON `performance_schema`.`replication_group_members` TO `mysql.session`@`localhost`
# restart:--percona_telemetry.grace_interval=30 --percona_telemetry.scrape_interval=30 --percona_telemetry.telemetry_root_dir=<telemetry_root_dir>
RENAME USER 'root'@'localhost' to 'root.tmp'@'localhost';
Warnings:
Warning 4005 User 'root'@'localhost' is referenced as a definer account in a stored routine.
Warning 4005 User 'root'@'localhost' is referenced as a definer account in a trigger.
'root' user used by component's 1st verison does not exist. Telemetry dir should contain 1 file.
1
RENAME USER 'root.tmp'@'localhost' to 'root'@'localhost';
Warnings:
Warning 4005 User 'root'@'localhost' is referenced as a definer account in a stored routine.
Warning 4005 User 'root'@'localhost' is referenced as a definer account in a trigger.
# restart:--percona_telemetry.grace_interval=30 --percona_telemetry.scrape_interval=30 --percona_telemetry.telemetry_root_dir=<telemetry_root_dir>
RENAME USER 'mysql.session'@'localhost' to 'mysql.session.tmp'@'localhost';
include/assert.inc [No orphaned sessions expected in processlist]
'mysql.session' user used by component does not exist. Telemetry dir should still contain 1 file.
1
RENAME USER 'mysql.session.tmp'@'localhost' to 'mysql.session'@'localhost';
# restart:--percona_telemetry.grace_interval=30 --percona_telemetry.scrape_interval=30 --percona_telemetry.telemetry_root_dir=<telemetry_root_dir>
70 changes: 70 additions & 0 deletions mysql-test/suite/component_percona_telemetry/t/no_user.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Test that lack of the user used by Percona Telemetry Component
# doesn't cause hangs during server restart and no orphaned sessions are created.

--source include/have_percona_telemetry.inc

--let $telemetry_root_dir = $MYSQL_TMP_DIR/telemetry_dir
--let $grace_interval = 30
--let $scrape_interval = 30

--mkdir $telemetry_root_dir

# Record mysql.session user privileges
SELECT * FROM information_schema.table_privileges WHERE grantee = "'mysql.session'@'localhost'" ORDER BY table_schema, table_name;
SHOW GRANTS FOR 'mysql.session'@'localhost';

# restart the server with custom telemetry file path and timeouts
--let $restart_parameters = "restart:--percona_telemetry.grace_interval=$grace_interval --percona_telemetry.scrape_interval=$scrape_interval --percona_telemetry.telemetry_root_dir=$telemetry_root_dir"
--replace_regex /telemetry_root_dir=.*telemetry_dir/telemetry_root_dir=<telemetry_root_dir>/
--source include/restart_mysqld.inc

# Rename 'root' user (1st version of Percona Telemetry Component used 'root' user)
# 1st version will not collect any data and will not create telemetry file and the restart will hang.
# Fixed version will work properly as it doesn't use 'root' user.
RENAME USER 'root'@'localhost' to 'root.tmp'@'localhost';

# sleep more than grace_interval and check that telemetry file was created
--let $timeout = `select $grace_interval + 10`
--sleep $timeout

--echo 'root' user used by component's 1st verison does not exist. Telemetry dir should contain 1 file.
--exec ls -1 $telemetry_root_dir | wc -l

#
# It should be possible to restart the server.
#
RENAME USER 'root.tmp'@'localhost' to 'root'@'localhost';
--let $restart_parameters = "restart:--percona_telemetry.grace_interval=$grace_interval --percona_telemetry.scrape_interval=$scrape_interval --percona_telemetry.telemetry_root_dir=$telemetry_root_dir"
--replace_regex /telemetry_root_dir=.*telemetry_dir/telemetry_root_dir=<telemetry_root_dir>/
--source include/restart_mysqld.inc


#
# Now rename the user used by component
#
RENAME USER 'mysql.session'@'localhost' to 'mysql.session.tmp'@'localhost';

# Wait a few cycles and ensure that SHOW PROCESSLIST does not contain rows related to orphaned sessions.
--let $timeout = `select $grace_interval + 3 * $scrape_interval`
--sleep $timeout

--let $assert_text = No orphaned sessions expected in processlist
--let $assert_cond = [SELECT COUNT(*) as Result FROM performance_schema.processlist WHERE user = "mysql.session";, Result, 1] = 0
--source include/assert.inc

# Check that no new telemetry file was created
--echo 'mysql.session' user used by component does not exist. Telemetry dir should still contain 1 file.
--exec ls -1 $telemetry_root_dir | wc -l


#
# It should be still possible to restart the server.
#
RENAME USER 'mysql.session.tmp'@'localhost' to 'mysql.session'@'localhost';
--let $restart_parameters = "restart:--percona_telemetry.grace_interval=$grace_interval --percona_telemetry.scrape_interval=$scrape_interval --percona_telemetry.telemetry_root_dir=$telemetry_root_dir"
--replace_regex /telemetry_root_dir=.*telemetry_dir/telemetry_root_dir=<telemetry_root_dir>/
--source include/restart_mysqld.inc

# cleanup
--force-rmdir $telemetry_root_dir

4 changes: 3 additions & 1 deletion mysql-test/suite/funcs_1/r/is_table_privileges.result
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ TABLE_NAME varchar(64) NO
PRIVILEGE_TYPE varchar(64) NO
IS_GRANTABLE varchar(3) NO
SELECT table_catalog, table_schema, table_name, privilege_type
FROM information_schema.table_privileges WHERE table_catalog IS NOT NULL;
FROM information_schema.table_privileges WHERE table_catalog IS NOT NULL
AND NOT (grantee = "'mysql.session'@'localhost'"
AND table_name IN ('component', 'replication_group_members'));
table_catalog table_schema table_name privilege_type
def mysql user SELECT
def sys sys_config SELECT
Expand Down
4 changes: 3 additions & 1 deletion mysql-test/suite/funcs_1/t/is_table_privileges.test
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ eval SHOW COLUMNS FROM information_schema.$is_table;
# Show that TABLE_CATALOG is always NULL.
--sorted_result
SELECT table_catalog, table_schema, table_name, privilege_type
FROM information_schema.table_privileges WHERE table_catalog IS NOT NULL;
FROM information_schema.table_privileges WHERE table_catalog IS NOT NULL
AND NOT (grantee = "'mysql.session'@'localhost'"
AND table_name IN ('component', 'replication_group_members'));

--echo ######################################################################
--echo # Testcase 3.2.11.2+3.2.11.3+3.2.11.4:
Expand Down
28 changes: 15 additions & 13 deletions mysql-test/suite/innodb/r/log_first_rec_group.result
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ include/assert.inc [All must happen within the single log block (this was requir
# value of 5 in first pass, 4 in second).
SELECT * FROM t;
a b
98 1
99 2
100 3
101 4
102 5
103 6
97 1
98 2
99 3
100 4
101 5
102 6
103 7
SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't';
AUTO_INCREMENT
7
8
Pass: 1
# Initialization - create table, resets autoincrement value.
# 0. Move to the next log block.
Expand All @@ -42,14 +43,15 @@ include/assert.inc [All must happen within the single log block (this was requir
# value of 5 in first pass, 4 in second).
SELECT * FROM t;
a b
98 1
99 2
100 3
101 4
103 5
97 1
98 2
99 3
100 4
101 5
103 6
SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't';
AUTO_INCREMENT
6
7
#
# Scenario 2. Restart after writing full log block with record ending at boundary,
# recovery should start in middle of the last written block (pass 0, 2)
Expand Down
1 change: 1 addition & 0 deletions mysql-test/suite/innodb/t/log_first_rec_group.test
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ while ($pass != 2) {

--echo # Initialization - create table, resets autoincrement value.
CREATE TABLE t (a INT NOT NULL, b INT NOT NULL AUTO_INCREMENT PRIMARY KEY) ENGINE = InnoDB;
INSERT INTO t (a) VALUES (97);
INSERT INTO t (a) VALUES (98);
INSERT INTO t (a) VALUES (99);
INSERT INTO t (a) VALUES (100);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--loose-percona-telemetry-disable=ON
8 changes: 6 additions & 2 deletions mysql-test/t/grant.test
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,9 @@ flush privileges;
show grants for mysqltest_8@'';
show grants for mysqltest_8;
--sorted_result
select * from information_schema.table_privileges where table_schema NOT IN ('sys','mysql');
select * from information_schema.table_privileges where table_schema NOT IN ('sys','mysql')
and not (grantee = "'mysql.session'@'localhost'"
and table_name in ('component', 'replication_group_members'));
connect (conn5,localhost,mysqltest_8,,);
select * from t1;
disconnect conn5;
Expand All @@ -665,7 +667,9 @@ revoke update on t1 from mysqltest_8;
show grants for mysqltest_8@'';
show grants for mysqltest_8;
--sorted_result
select * from information_schema.table_privileges where table_schema NOT IN ('sys','mysql');
select * from information_schema.table_privileges where table_schema NOT IN ('sys','mysql')
and not (grantee = "'mysql.session'@'localhost'"
and table_name in ('component', 'replication_group_members'));
flush privileges;
show grants for mysqltest_8@'';
show grants for mysqltest_8;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--loose-percona-telemetry-disable=ON
5 changes: 4 additions & 1 deletion mysql-test/t/information_schema_cs.test
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,10 @@ select * from information_schema.views where table_schema !=
'sys' order by table_name;
grant select (a) on test.t1 to joe@localhost with grant option;
select * from INFORMATION_SCHEMA.COLUMN_PRIVILEGES WHERE table_schema != 'sys';
select * from INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE table_schema NOT IN ('sys','mysql');
select * from INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE table_schema NOT IN ('sys','mysql')
and not (grantee = "'mysql.session'@'localhost'"
and table_name in ('component', 'replication_group_members'));

drop view v1, v2, v3;
drop table t1;
delete from mysql.user where user='joe';
Expand Down
1 change: 1 addition & 0 deletions mysql-test/t/mysql_upgrade-master.opt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--loose-percona-telemetry-disable=ON
8 changes: 6 additions & 2 deletions mysql-test/t/transactional_acl_tables.test
Original file line number Diff line number Diff line change
Expand Up @@ -1829,14 +1829,18 @@ CREATE TABLE t2 (a INT);
GRANT SELECT(a), UPDATE(a), INSERT(a), REFERENCES(a) ON t1 TO u1@h;
GRANT INSERT(a) ON t2 TO u1@h;
SELECT host, db, user, table_name, column_name, column_priv FROM mysql.columns_priv;
SELECT host, db, user, table_name, grantor, table_priv, column_priv FROM mysql.tables_priv;
SELECT host, db, user, table_name, grantor, table_priv, column_priv FROM mysql.tables_priv
WHERE NOT (user = 'mysql.session'
AND table_name IN ('component', 'replication_group_members'));
DELETE FROM mysql.columns_priv WHERE host = 'h' AND user = 'u1'
AND table_name = 't1';
COMMIT;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM u1@h;

SELECT host, db, user, table_name, column_name, column_priv FROM mysql.columns_priv;
SELECT host, db, user, table_name, grantor, table_priv, column_priv FROM mysql.tables_priv;
SELECT host, db, user, table_name, grantor, table_priv, column_priv FROM mysql.tables_priv
WHERE NOT (user = 'mysql.session'
AND table_name IN ('component', 'replication_group_members'));
SHOW GRANTS FOR u1@h;

# Check whether on FLUSH PRIVILEGES the GRANT_TABLE::init handles OUT OF MEMORY
Expand Down
Loading

0 comments on commit 60a65b3

Please sign in to comment.