Skip to content

Commit 346030e

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 ccf72b6 commit 346030e

File tree

7 files changed

+194
-1
lines changed

7 files changed

+194
-1
lines changed

include/MySQL_Session.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,15 @@ 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 *);
235237
bool handler_again___status_CHANGING_CHARSET(int *_rc);
236238
bool handler_again___status_CHANGING_SCHEMA(int *);
237239
bool handler_again___status_CONNECTING_SERVER(int *);
@@ -280,6 +282,7 @@ class MySQL_Session: public Base_Session<MySQL_Session, MySQL_Data_Stream, MySQL
280282
int RunQuery(MySQL_Data_Stream *myds, MySQL_Connection *myconn);
281283
void handler___status_WAITING_CLIENT_DATA();
282284
void handler_rc0_Process_GTID(MySQL_Connection *myconn);
285+
void handler_rc0_Process_Variables(MySQL_Connection *myconn);
283286
void handler_rc0_RefreshActiveTransactions(MySQL_Connection* myconn);
284287
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_INIT_DB_replace_CLICKHOUSE(PtrSize_t& pkt);
285288
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY___not_mysql(PtrSize_t& pkt);

include/mysql_connection.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ 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;
9293
bool ldap_user_variable_sent;
9394
uint8_t protocol_version;
9495
int8_t last_set_autocommit;
@@ -262,6 +263,7 @@ class MySQL_Connection {
262263
void reset();
263264

264265
bool get_gtid(char *buff, uint64_t *trx_id);
266+
bool get_variables(std::unordered_map<std::string, std::string>&);
265267
void reduce_auto_increment_delay_token() { if (auto_increment_delay_token) auto_increment_delay_token--; };
266268

267269
bool match_ff_req_options(const MySQL_Connection *c);

include/proxysql_structs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ enum session_status {
311311
SETTING_NEXT_TRANSACTION_READ,
312312
PROCESSING_EXTENDED_QUERY_SYNC,
313313
RESYNCHRONIZING_CONNECTION,
314+
SETTING_SESSION_TRACK_VARIABLES,
314315
session_status___NONE // special marker
315316
};
316317

lib/MySQL_Session.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,6 +1953,16 @@ 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+
return false;
1964+
}
1965+
19561966

19571967
bool MySQL_Session::handler_again___verify_multiple_variables(MySQL_Connection* myconn) {
19581968
for (auto i = 0; i < SQL_NAME_LAST_LOW_WM; i++) {
@@ -2565,6 +2575,12 @@ bool MySQL_Session::handler_again___status_SETTING_GENERIC_VARIABLE(int *_rc, co
25652575
free(query);
25662576
query=NULL;
25672577
}
2578+
2579+
if (rc == 0 && strncasecmp(var_name, "session_track_system_variables", strlen("session_track_system_variables")) == 0) {
2580+
char *q = (char *)"SET session_track_state_change=ON";
2581+
rc = myconn->async_send_simple_command(myds->revents, q, strlen(q));
2582+
}
2583+
25682584
if (rc==0) {
25692585
myds->revents|=POLLOUT; // we also set again POLLOUT to send a query immediately!
25702586
myds->DSS = STATE_MARIADB_GENERIC;
@@ -2753,6 +2769,13 @@ bool MySQL_Session::handler_again___status_SETTING_SESSION_TRACK_GTIDS(int *_rc)
27532769
return ret;
27542770
}
27552771

2772+
bool MySQL_Session::handler_again___status_SETTING_SESSION_TRACK_VARIABLES(int *_rc) {
2773+
bool ret=false;
2774+
assert(mybe->server_myds->myconn);
2775+
ret = handler_again___status_SETTING_GENERIC_VARIABLE(_rc, (char *)"session_track_system_variables", "*", false);
2776+
return ret;
2777+
}
2778+
27562779
bool MySQL_Session::handler_again___status_CHANGING_SCHEMA(int *_rc) {
27572780
bool ret=false;
27582781
//fprintf(stderr,"CHANGING_SCHEMA\n");
@@ -4862,6 +4885,28 @@ void MySQL_Session::handler_rc0_Process_GTID(MySQL_Connection *myconn) {
48624885
}
48634886
}
48644887

4888+
void MySQL_Session::handler_rc0_Process_Variables(MySQL_Connection *myconn) {
4889+
std::unordered_map<string, string> var_map;
4890+
4891+
if(myconn->get_variables(var_map)) {
4892+
const char *variable = nullptr;
4893+
const char *value = nullptr;
4894+
4895+
for (int idx = 0 ; idx < SQL_NAME_LAST_HIGH_WM ; idx++) {
4896+
variable = mysql_tracked_variables[idx].set_variable_name;
4897+
4898+
auto itr = var_map.find(variable);
4899+
if(itr != var_map.end()) {
4900+
value = itr->second.c_str();
4901+
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, value);
4902+
4903+
mysql_variables.client_set_value(this, idx, value);
4904+
mysql_variables.server_set_value(this, idx, value);
4905+
}
4906+
}
4907+
}
4908+
}
4909+
48654910
void MySQL_Session::handler_KillConnectionIfNeeded() {
48664911
if ( // two conditions
48674912
// 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 +5104,10 @@ int MySQL_Session::handler() {
50595104
goto handler_again;
50605105
}
50615106

5107+
if (handler_again___verify_backend_session_track_variables()) {
5108+
goto handler_again;
5109+
}
5110+
50625111
// Optimize network traffic when we can use 'SET NAMES'
50635112
if (verify_set_names(this)) {
50645113
goto handler_again;
@@ -5139,6 +5188,8 @@ int MySQL_Session::handler() {
51395188

51405189
handler_rc0_Process_GTID(myconn);
51415190

5191+
handler_rc0_Process_Variables(myconn);
5192+
51425193
// if we are locked on hostgroup, the value of autocommit is copied from the backend connection
51435194
// see bug #3549
51445195
if (locked_on_hostgroup >= 0) {
@@ -5435,6 +5486,9 @@ bool MySQL_Session::handler_again___multiple_statuses(int *rc) {
54355486
case SETTING_SESSION_TRACK_GTIDS:
54365487
ret = handler_again___status_SETTING_SESSION_TRACK_GTIDS(rc);
54375488
break;
5489+
case SETTING_SESSION_TRACK_VARIABLES:
5490+
ret = handler_again___status_SETTING_SESSION_TRACK_VARIABLES(rc);
5491+
break;
54385492
case SETTING_SET_NAMES:
54395493
ret = handler_again___status_CHANGING_CHARSET(rc);
54405494
break;

lib/mysql_connection.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ 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;
455456
options.ldap_user_variable=NULL;
456457
options.ldap_user_variable_value=NULL;
457458
options.ldap_user_variable_sent=false;
@@ -3082,6 +3083,7 @@ void MySQL_Connection::reset() {
30823083
options.session_track_gtids = NULL;
30833084
options.session_track_gtids_sent = false;
30843085
}
3086+
options.session_track_variables_sent = false;
30853087
}
30863088

30873089
bool MySQL_Connection::get_gtid(char *buff, uint64_t *trx_id) {
@@ -3116,6 +3118,48 @@ bool MySQL_Connection::get_gtid(char *buff, uint64_t *trx_id) {
31163118
return ret;
31173119
}
31183120

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

test/tap/groups/groups.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,5 +205,7 @@
205205
"eof_mixed_flags_queries-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" ],
206206
"eof_packet_mixed_queries-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" ],
207207
"ok_packet_mixed_queries-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" ],
208-
"test_load_from_config_validation-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" ]
208+
"test_load_from_config_validation-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" ],
209+
"mysql-show_ssl_version-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" ],
210+
"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" ]
209211
}
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, "USE 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 set_innodb_lock_wait_timeout");
38+
const char* create_proc =
39+
"CREATE PROCEDURE 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 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+
}

0 commit comments

Comments
 (0)