Skip to content

Commit 88da8b1

Browse files
committed
MySQL: Track session-specific system variables changes
- Configure `session_track_system_variables` and `session_track_state_change` in all backend connections. - Utilize notifications from backend servers to capture system variable changes that cannot be handled by `MySQL_Set_Stmt_Parser` - Update both client and server variable maps based on backend responses. - TAP test to verify this patch. Signed-off-by: Wazir Ahmed <[email protected]>
1 parent cce161b commit 88da8b1

File tree

8 files changed

+226
-5
lines changed

8 files changed

+226
-5
lines changed

include/MySQL_Session.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,16 @@ class MySQL_Session: public Base_Session<MySQL_Session, MySQL_Data_Stream, MySQL
225225
bool handler_again___verify_ldap_user_variable();
226226
bool handler_again___verify_backend_autocommit();
227227
bool handler_again___verify_backend_session_track_gtids();
228+
bool handler_again___verify_backend_session_track_variables();
228229
bool handler_again___verify_backend_multi_statement();
229230
bool handler_again___verify_backend_user_schema();
230231
bool handler_again___verify_multiple_variables(MySQL_Connection *);
231232
bool handler_again___status_SETTING_INIT_CONNECT(int *);
232233
bool handler_again___status_SETTING_LDAP_USER_VARIABLE(int *);
233234
bool handler_again___status_SETTING_SQL_MODE(int *);
234235
bool handler_again___status_SETTING_SESSION_TRACK_GTIDS(int *);
236+
bool handler_again___status_SETTING_SESSION_TRACK_VARIABLES(int *);
237+
bool handler_again___status_SETTING_SESSION_TRACK_STATE(int *);
235238
bool handler_again___status_CHANGING_CHARSET(int *_rc);
236239
bool handler_again___status_CHANGING_SCHEMA(int *);
237240
bool handler_again___status_CONNECTING_SERVER(int *);
@@ -280,6 +283,7 @@ class MySQL_Session: public Base_Session<MySQL_Session, MySQL_Data_Stream, MySQL
280283
int RunQuery(MySQL_Data_Stream *myds, MySQL_Connection *myconn);
281284
void handler___status_WAITING_CLIENT_DATA();
282285
void handler_rc0_Process_GTID(MySQL_Connection *myconn);
286+
void handler_rc0_Process_Variables(MySQL_Connection *myconn);
283287
void handler_rc0_RefreshActiveTransactions(MySQL_Connection* myconn);
284288
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_INIT_DB_replace_CLICKHOUSE(PtrSize_t& pkt);
285289
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY___not_mysql(PtrSize_t& pkt);

include/mysql_connection.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ class MySQL_Connection {
8989
char *ldap_user_variable;
9090
char *ldap_user_variable_value;
9191
bool session_track_gtids_sent;
92+
bool session_track_variables_sent;
93+
bool session_track_state_sent;
9294
bool ldap_user_variable_sent;
9395
uint8_t protocol_version;
9496
int8_t last_set_autocommit;
@@ -262,6 +264,7 @@ class MySQL_Connection {
262264
void reset();
263265

264266
bool get_gtid(char *buff, uint64_t *trx_id);
267+
bool get_variables(std::unordered_map<std::string, std::string>&);
265268
void reduce_auto_increment_delay_token() { if (auto_increment_delay_token) auto_increment_delay_token--; };
266269

267270
bool match_ff_req_options(const MySQL_Connection *c);

include/proxysql_structs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ enum session_status {
311311
SETTING_NEXT_TRANSACTION_READ,
312312
PROCESSING_EXTENDED_QUERY_SYNC,
313313
RESYNCHRONIZING_CONNECTION,
314+
SETTING_SESSION_TRACK_VARIABLES,
315+
SETTING_SESSION_TRACK_STATE,
314316
session_status___NONE // special marker
315317
};
316318

lib/MySQL_Session.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,6 +1953,21 @@ bool MySQL_Session::handler_again___verify_backend_session_track_gtids() {
19531953
return ret;
19541954
}
19551955

1956+
bool MySQL_Session::handler_again___verify_backend_session_track_variables() {
1957+
if (mybe->server_myds->myconn->options.session_track_variables_sent == false) {
1958+
mybe->server_myds->myconn->options.session_track_variables_sent = true;
1959+
set_previous_status_mode3();
1960+
NEXT_IMMEDIATE_NEW(SETTING_SESSION_TRACK_VARIABLES);
1961+
}
1962+
1963+
if (mybe->server_myds->myconn->options.session_track_state_sent == false) {
1964+
mybe->server_myds->myconn->options.session_track_state_sent = true;
1965+
set_previous_status_mode3();
1966+
NEXT_IMMEDIATE_NEW(SETTING_SESSION_TRACK_STATE);
1967+
}
1968+
1969+
return false;
1970+
}
19561971

19571972
bool MySQL_Session::handler_again___verify_multiple_variables(MySQL_Connection* myconn) {
19581973
for (auto i = 0; i < SQL_NAME_LAST_LOW_WM; i++) {
@@ -2753,6 +2768,20 @@ bool MySQL_Session::handler_again___status_SETTING_SESSION_TRACK_GTIDS(int *_rc)
27532768
return ret;
27542769
}
27552770

2771+
bool MySQL_Session::handler_again___status_SETTING_SESSION_TRACK_VARIABLES(int *_rc) {
2772+
bool ret=false;
2773+
assert(mybe->server_myds->myconn);
2774+
ret = handler_again___status_SETTING_GENERIC_VARIABLE(_rc, (char *)"session_track_system_variables", "*", false);
2775+
return ret;
2776+
}
2777+
2778+
bool MySQL_Session::handler_again___status_SETTING_SESSION_TRACK_STATE(int *_rc) {
2779+
bool ret=false;
2780+
assert(mybe->server_myds->myconn);
2781+
ret = handler_again___status_SETTING_GENERIC_VARIABLE(_rc, (char *)"session_track_state_change", "ON", false);
2782+
return ret;
2783+
}
2784+
27562785
bool MySQL_Session::handler_again___status_CHANGING_SCHEMA(int *_rc) {
27572786
bool ret=false;
27582787
//fprintf(stderr,"CHANGING_SCHEMA\n");
@@ -4862,6 +4891,41 @@ void MySQL_Session::handler_rc0_Process_GTID(MySQL_Connection *myconn) {
48624891
}
48634892
}
48644893

4894+
void MySQL_Session::handler_rc0_Process_Variables(MySQL_Connection *myconn) {
4895+
std::unordered_map<string, string> var_map;
4896+
4897+
if(myconn->get_variables(var_map)) {
4898+
std::string variable;
4899+
std::string value;
4900+
4901+
for (int idx = 0 ; idx < SQL_NAME_LAST_HIGH_WM ; idx++) {
4902+
variable = mysql_tracked_variables[idx].set_variable_name;
4903+
4904+
auto itr = var_map.find(variable);
4905+
if(itr != var_map.end()) {
4906+
value = itr->second;
4907+
proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 7, "Session=%p, backend=%p. Notification for session_track_system_variables: variable=%s, value=%s\n", this, this->mybe, variable.c_str(), value.c_str());
4908+
4909+
const MARIADB_CHARSET_INFO *ci = NULL;
4910+
if (variable == "character_set_results" || variable == "character_set_connection" ||
4911+
variable == "character_set_client" || variable == "character_set_database") {
4912+
ci = proxysql_find_charset_name(value.c_str());
4913+
}
4914+
else if (variable == "collation_connection") {
4915+
ci = proxysql_find_charset_collate(value.c_str());
4916+
}
4917+
4918+
if (ci) {
4919+
value = std::to_string(ci->nr);
4920+
}
4921+
4922+
mysql_variables.client_set_value(this, idx, value);
4923+
mysql_variables.server_set_value(this, idx, value.c_str());
4924+
}
4925+
}
4926+
}
4927+
}
4928+
48654929
void MySQL_Session::handler_KillConnectionIfNeeded() {
48664930
if ( // two conditions
48674931
// If the server connection is in a non-idle state (ASYNC_IDLE), and the current time is greater than or equal to mybe->server_myds->wait_until
@@ -5059,6 +5123,10 @@ int MySQL_Session::handler() {
50595123
goto handler_again;
50605124
}
50615125

5126+
if (handler_again___verify_backend_session_track_variables()) {
5127+
goto handler_again;
5128+
}
5129+
50625130
// Optimize network traffic when we can use 'SET NAMES'
50635131
if (verify_set_names(this)) {
50645132
goto handler_again;
@@ -5139,6 +5207,8 @@ int MySQL_Session::handler() {
51395207

51405208
handler_rc0_Process_GTID(myconn);
51415209

5210+
handler_rc0_Process_Variables(myconn);
5211+
51425212
// if we are locked on hostgroup, the value of autocommit is copied from the backend connection
51435213
// see bug #3549
51445214
if (locked_on_hostgroup >= 0) {
@@ -5435,6 +5505,12 @@ bool MySQL_Session::handler_again___multiple_statuses(int *rc) {
54355505
case SETTING_SESSION_TRACK_GTIDS:
54365506
ret = handler_again___status_SETTING_SESSION_TRACK_GTIDS(rc);
54375507
break;
5508+
case SETTING_SESSION_TRACK_VARIABLES:
5509+
ret = handler_again___status_SETTING_SESSION_TRACK_VARIABLES(rc);
5510+
break;
5511+
case SETTING_SESSION_TRACK_STATE:
5512+
ret = handler_again___status_SETTING_SESSION_TRACK_STATE(rc);
5513+
break;
54385514
case SETTING_SET_NAMES:
54395515
ret = handler_again___status_CHANGING_CHARSET(rc);
54405516
break;

lib/mysql_connection.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,8 @@ MySQL_Connection::MySQL_Connection() {
452452
options.init_connect_sent=false;
453453
options.session_track_gtids = NULL;
454454
options.session_track_gtids_sent = false;
455+
options.session_track_variables_sent = false;
456+
options.session_track_state_sent = false;
455457
options.ldap_user_variable=NULL;
456458
options.ldap_user_variable_value=NULL;
457459
options.ldap_user_variable_sent=false;
@@ -3082,6 +3084,8 @@ void MySQL_Connection::reset() {
30823084
options.session_track_gtids = NULL;
30833085
options.session_track_gtids_sent = false;
30843086
}
3087+
options.session_track_variables_sent = false;
3088+
options.session_track_state_sent = false;
30853089
}
30863090

30873091
bool MySQL_Connection::get_gtid(char *buff, uint64_t *trx_id) {
@@ -3116,6 +3120,48 @@ bool MySQL_Connection::get_gtid(char *buff, uint64_t *trx_id) {
31163120
return ret;
31173121
}
31183122

3123+
bool MySQL_Connection::get_variables(std::unordered_map<string, string>& variables) {
3124+
bool ret = false;
3125+
3126+
if ((mysql != nullptr)
3127+
&& (mysql->net.last_errno == 0)
3128+
&& (mysql->server_status & SERVER_SESSION_STATE_CHANGED)) {
3129+
// when there is no error and status changed
3130+
const char *data;
3131+
size_t length;
3132+
3133+
if (mysql_session_track_get_first(mysql, SESSION_TRACK_SYSTEM_VARIABLES, &data, &length) == 0) {
3134+
string var_name(data, length);
3135+
string val;
3136+
3137+
// get_first() returns a variable_name
3138+
// get_next() will return the value
3139+
bool expect_value = true;
3140+
3141+
while (mysql_session_track_get_next(mysql, SESSION_TRACK_SYSTEM_VARIABLES, &data, &length) == 0) {
3142+
if (expect_value) {
3143+
val = string(data, length);
3144+
variables[var_name] = val;
3145+
// got a value in this iteration
3146+
// in the next iteration, we have to expect a variable_name
3147+
expect_value = false;
3148+
} else {
3149+
var_name = string(data, length);
3150+
// got a variable_name in this iteration
3151+
// in the next iteration, we have to expect the value of this variable
3152+
expect_value = true;
3153+
}
3154+
}
3155+
3156+
// update counters
3157+
// __sync_fetch_and_add(&myds->sess->thread->status_variables.stvar[st_var_gtid_session_collected],1);
3158+
ret = true;
3159+
}
3160+
}
3161+
3162+
return ret;
3163+
}
3164+
31193165
void MySQL_Connection::set_ssl_params(MYSQL *mysql, MySQLServers_SslParams *ssl_params) {
31203166
if (ssl_params == NULL) {
31213167
mysql_ssl_set(mysql,

test/tap/groups/groups.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,5 +229,6 @@
229229
"test_ssl_fast_forward-2_libmysql-t": [ "default-g4", "mysql-auto_increment_delay_multiplex=0-g4", "mysql-multiplexing=false-g4", "mysql-query_digests=0-g4", "mysql-query_digests_keep_comment=1-g4" ],
230230
"test_ssl_fast_forward-3_libmariadb-t": [ "default-g4", "mysql-auto_increment_delay_multiplex=0-g4", "mysql-multiplexing=false-g4", "mysql-query_digests=0-g4", "mysql-query_digests_keep_comment=1-g4" ],
231231
"test_ssl_fast_forward-3_libmysql-t": [ "default-g4", "mysql-auto_increment_delay_multiplex=0-g4", "mysql-multiplexing=false-g4", "mysql-query_digests=0-g4", "mysql-query_digests_keep_comment=1-g4" ],
232-
"test_ignore_min_gtid-t": [ "default-g4", "mysql-auto_increment_delay_multiplex=0-g4", "mysql-multiplexing=false-g4", "mysql-query_digests=0-g4", "mysql-query_digests_keep_comment=1-g4" ]
232+
"test_ignore_min_gtid-t": [ "default-g4", "mysql-auto_increment_delay_multiplex=0-g4", "mysql-multiplexing=false-g4", "mysql-query_digests=0-g4", "mysql-query_digests_keep_comment=1-g4" ],
233+
"mysql-track_system_variables-t" : [ "default-g4","mysql-auto_increment_delay_multiplex=0-g4","mysql-multiplexing=false-g4","mysql-query_digests=0-g4","mysql-query_digests_keep_comment=1-g4" ]
233234
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* @file test_track_system_variables-t.cpp
3+
* @brief This test verifies that ProxySQL properly tracks session-specific
4+
* system variables across the backend connections.
5+
*/
6+
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include "json.hpp"
10+
#include "mysql.h"
11+
#include "tap.h"
12+
#include "command_line.h"
13+
#include "utils.h"
14+
15+
using nlohmann::json;
16+
17+
int main(int argc, char** argv) {
18+
CommandLine cl;
19+
if (cl.getEnv()) {
20+
diag("Failed to get the required environmental variables.");
21+
return exit_status();
22+
}
23+
24+
plan(1);
25+
26+
MYSQL* proxy = init_mysql_conn(cl.host, cl.port, cl.username, cl.password, true);
27+
if (!proxy) {
28+
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy));
29+
return exit_status();
30+
}
31+
32+
MYSQL_QUERY_T(proxy, "CREATE DATABASE IF NOT EXISTS test");
33+
MYSQL_QUERY_T(proxy, "SELECT 1");
34+
MYSQL_RES* reset_result = mysql_store_result(proxy);
35+
mysql_free_result(reset_result);
36+
37+
MYSQL_QUERY_T(proxy, "DROP PROCEDURE IF EXISTS test.set_innodb_lock_wait_timeout");
38+
const char* create_proc =
39+
"CREATE PROCEDURE test.set_innodb_lock_wait_timeout() "
40+
"BEGIN "
41+
" SET innodb_lock_wait_timeout = CAST(FLOOR(50 + (RAND() * 100)) AS UNSIGNED); "
42+
"END";
43+
44+
MYSQL_QUERY_T(proxy, create_proc);
45+
MYSQL_QUERY_T(proxy, "CALL test.set_innodb_lock_wait_timeout()");
46+
47+
int set_value = -1;
48+
MYSQL_QUERY_T(proxy, "SELECT @@innodb_lock_wait_timeout");
49+
MYSQL_RES* result = mysql_store_result(proxy);
50+
if (result) {
51+
MYSQL_ROW row = mysql_fetch_row(result);
52+
if (row) {
53+
set_value = atoi(row[0]);
54+
}
55+
mysql_free_result(result);
56+
}
57+
58+
MYSQL_QUERY(proxy, "PROXYSQL INTERNAL SESSION");
59+
result = mysql_store_result(proxy);
60+
MYSQL_ROW row = mysql_fetch_row(result);
61+
auto j_session = nlohmann::json::parse(row[0]);
62+
mysql_free_result(result);
63+
64+
int backend_value = -1;
65+
int client_value = -1;
66+
if (j_session.contains("backends")) {
67+
for (auto& backend : j_session["backends"]) {
68+
if (backend != nullptr && backend.contains("conn")) {
69+
if (backend["conn"].contains("innodb_lock_wait_timeout")) {
70+
backend_value = std::stoi(backend["conn"]["innodb_lock_wait_timeout"].get<std::string>());
71+
break;
72+
}
73+
}
74+
}
75+
}
76+
if (j_session.contains("conn")) {
77+
if (j_session["conn"].contains("innodb_lock_wait_timeout")) {
78+
client_value = std::stoi(j_session["conn"]["innodb_lock_wait_timeout"].get<std::string>());
79+
}
80+
}
81+
82+
ok(set_value == backend_value && set_value == client_value,
83+
"Match session innodb_lock_wait_timeout value with backend & client variable list. Expected: %d, Backend: %d, Client: %d", set_value, backend_value, client_value);
84+
85+
mysql_close(proxy);
86+
return exit_status();
87+
}

test/tap/tests/test_binlog_reader-t.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,12 @@ int main(int argc, char** argv) {
263263
vector<mysql_res_row> reader_1_read {};
264264
vector<mysql_res_row> reader_2_read {};
265265

266+
// Create backend connections before clearing the stats.
267+
// This is to exclude queries sent during connection setup phrase.
268+
MYSQL_QUERY_T(proxysql_mysql, "DO 1");
269+
MYSQL_QUERY_T(proxysql_mysql, "SELECT 1");
270+
mysql_free_result(mysql_store_result(proxysql_mysql));
271+
266272
// Reset connection pool stats
267273
int rc = mysql_query(proxysql_admin, "SELECT * FROM stats.stats_mysql_connection_pool_reset");
268274
if (rc != EXIT_SUCCESS) { goto cleanup; }
@@ -279,10 +285,6 @@ int main(int argc, char** argv) {
279285
rc = perform_update(proxysql_mysql, NUM_ROWS);
280286
if (rc != EXIT_SUCCESS) { goto cleanup; }
281287

282-
MYSQL_RES* my_res = mysql_store_result(proxysql_admin);
283-
vector<mysql_res_row> pre_select_rows = extract_mysql_rows(my_res);
284-
mysql_free_result(my_res);
285-
286288
int r_row = rand() % NUM_ROWS;
287289
if (r_row == 0) { r_row = 1; }
288290

0 commit comments

Comments
 (0)