Skip to content

Commit f88e3af

Browse files
committed
Extract hostgroup routing and locking logic from MySQL and PgSQL sessions
This commit completes Milestone 3 Phase 3.5 by moving hostgroup routing and locking decisions out of the MySQL_Session and PgSQL_Session classes. The logic is now encapsulated in standalone, pure functions designed for easier testing and better maintainability. Key changes: - Created include/MySQL_HostGroup_Routing.h and lib/MySQL_HostGroup_Routing.cpp: - Implements resolve_hostgroup_routing(), a pure function that handles: - Mirroring logic (highest priority). - Intercepting SHOW WARNINGS and routing to the previous HG. - Routing SELECT LAST_INSERT_ID() / @@IDENTITY queries to the HG where the last affected rows occurred. - Applying destination_hostgroup from Query Processor Output (QPO). - Enforcing hostgroup locking (mysql-set_query_lock_on_hostgroup). - Created include/PgSQL_HostGroup_Routing.h and lib/PgSQL_HostGroup_Routing.cpp: - Implements resolve_pgsql_hostgroup_routing() with similar logic adapted for PostgreSQL sessions. - Refactored lib/MySQL_Session.cpp and lib/PgSQL_Session.cpp: - Replaced scattered inline logic with calls to the new routing functions. - Integration points include STATE_SLEEP, STMT_PREPARE, STMT_EXECUTE, and the main query execution path. - Enhanced Unit Testing: - Added test/tap/tests/unit/MySQL_HostGroup_Routing-t.cpp (15 test cases). - Added test/tap/tests/unit/PgSQL_HostGroup_Routing-t.cpp (10 test cases). - Verified all 25 test cases pass successfully. - Updated Build System: - Modified lib/Makefile to include the new object files. - Updated test/tap/tests/unit/Makefile to include unit test targets. This refactoring significantly reduces coupling between the session state and routing logic, providing a foundation for more robust session orchestration and simplified unit testing of critical ProxySQL features. Issue: #5493
1 parent babac8e commit f88e3af

10 files changed

Lines changed: 836 additions & 214 deletions

include/MySQL_HostGroup_Routing.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#ifndef __MYSQL_HOSTGROUP_ROUTING_H
2+
#define __MYSQL_HOSTGROUP_ROUTING_H
3+
4+
#include <string>
5+
6+
/**
7+
* @struct MySQL_Routing_Session_State
8+
* @brief Represents the session state relevant for hostgroup routing decisions.
9+
*/
10+
struct MySQL_Routing_Session_State {
11+
int current_hostgroup;
12+
int default_hostgroup;
13+
int locked_on_hostgroup;
14+
int transaction_persistent_hostgroup;
15+
int last_HG_affected_rows;
16+
int warning_in_hg;
17+
bool autocommit;
18+
int autocommit_on_hostgroup;
19+
bool mirror;
20+
};
21+
22+
/**
23+
* @struct MySQL_Routing_QPO_State
24+
* @brief Represents the Query Processor Output relevant for hostgroup routing decisions.
25+
*/
26+
struct MySQL_Routing_QPO_State {
27+
int destination_hostgroup;
28+
bool is_set_statement; // Derived from query parsing
29+
bool is_show_warnings; // Derived from query parsing
30+
bool is_last_insert_id; // Derived from query parsing
31+
bool is_version_query; // Derived from query parsing
32+
};
33+
34+
/**
35+
* @struct MySQL_Routing_Result
36+
* @brief Represents the output of the hostgroup routing decision.
37+
*/
38+
struct MySQL_Routing_Result {
39+
int new_current_hostgroup;
40+
int new_locked_on_hostgroup;
41+
bool lock_hostgroup;
42+
bool error;
43+
std::string error_msg;
44+
};
45+
46+
/**
47+
* @brief Resolves the target hostgroup and locking decisions based on session and QPO state.
48+
*
49+
* This is a pure function designed to be easily testable.
50+
*
51+
* @param sess_state Current session state.
52+
* @param qpo_state Query Processor Output state.
53+
* @param set_query_lock_on_hostgroup Global configuration (mysql-set_query_lock_on_hostgroup).
54+
* @return MySQL_Routing_Result The routing decision.
55+
*/
56+
MySQL_Routing_Result resolve_hostgroup_routing(
57+
const MySQL_Routing_Session_State& sess_state,
58+
const MySQL_Routing_QPO_State& qpo_state,
59+
int set_query_lock_on_hostgroup
60+
);
61+
62+
#endif // __MYSQL_HOSTGROUP_ROUTING_H

include/PgSQL_HostGroup_Routing.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#ifndef __PGSQL_HOSTGROUP_ROUTING_H
2+
#define __PGSQL_HOSTGROUP_ROUTING_H
3+
4+
#include <string>
5+
6+
/**
7+
* @struct PgSQL_Routing_Session_State
8+
* @brief Represents the session state relevant for hostgroup routing decisions in PostgreSQL.
9+
*/
10+
struct PgSQL_Routing_Session_State {
11+
int current_hostgroup;
12+
int default_hostgroup;
13+
int locked_on_hostgroup;
14+
int transaction_persistent_hostgroup;
15+
};
16+
17+
/**
18+
* @struct PgSQL_Routing_QPO_State
19+
* @brief Represents the Query Processor Output relevant for hostgroup routing decisions in PostgreSQL.
20+
*/
21+
struct PgSQL_Routing_QPO_State {
22+
int destination_hostgroup;
23+
bool lock_hostgroup; // Derived from query parsing
24+
};
25+
26+
/**
27+
* @struct PgSQL_Routing_Result
28+
* @brief Represents the output of the hostgroup routing decision for PostgreSQL.
29+
*/
30+
struct PgSQL_Routing_Result {
31+
int new_current_hostgroup;
32+
int new_locked_on_hostgroup;
33+
bool lock_hostgroup;
34+
bool error;
35+
std::string error_msg;
36+
};
37+
38+
/**
39+
* @brief Resolves the target hostgroup and locking decisions based on session and QPO state.
40+
*
41+
* This is a pure function designed to be easily testable.
42+
*
43+
* @param sess_state Current session state.
44+
* @param qpo_state Query Processor Output state.
45+
* @param set_query_lock_on_hostgroup Global configuration (pgsql-set_query_lock_on_hostgroup).
46+
* @return PgSQL_Routing_Result The routing decision.
47+
*/
48+
PgSQL_Routing_Result resolve_pgsql_hostgroup_routing(
49+
const PgSQL_Routing_Session_State& sess_state,
50+
const PgSQL_Routing_QPO_State& qpo_state,
51+
int set_query_lock_on_hostgroup
52+
);
53+
54+
#endif // __PGSQL_HOSTGROUP_ROUTING_H

lib/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ MYCXXFLAGS := $(STDCPP) $(MYCFLAGS) $(PSQLCH) $(PSQLGA) $(PSQL31) $(PSQLFFTO) $(
8888
default: libproxysql.a
8989
.PHONY: default
9090

91-
_OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo MySQL_Query_Processor.oo PgSQL_Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo log_utils.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo proxysql_coredump.oo proxysql_sslkeylog.oo \
91+
_OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_HostGroup_Routing.oo PgSQL_Session.oo PgSQL_HostGroup_Routing.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo MySQL_Query_Processor.oo PgSQL_Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo log_utils.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo proxysql_coredump.oo proxysql_sslkeylog.oo \
9292
sha256crypt.oo \
9393
BaseSrvList.oo BaseHGC.oo Base_HostGroups_Manager.oo \
9494
QP_rule_text.oo QP_query_digest_stats.oo \

lib/MySQL_HostGroup_Routing.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#include "MySQL_HostGroup_Routing.h"
2+
3+
MySQL_Routing_Result resolve_hostgroup_routing(
4+
const MySQL_Routing_Session_State& sess_state,
5+
const MySQL_Routing_QPO_State& qpo_state,
6+
int set_query_lock_on_hostgroup
7+
) {
8+
MySQL_Routing_Result result;
9+
result.new_current_hostgroup = sess_state.current_hostgroup;
10+
result.new_locked_on_hostgroup = sess_state.locked_on_hostgroup;
11+
result.lock_hostgroup = false;
12+
result.error = false;
13+
result.error_msg = "";
14+
15+
// 1. Mirroring
16+
if (sess_state.mirror) {
17+
result.new_current_hostgroup = qpo_state.destination_hostgroup;
18+
return result;
19+
}
20+
21+
// 2. SHOW WARNINGS / SHOW COUNT(*) WARNINGS
22+
if (qpo_state.is_show_warnings) {
23+
if (sess_state.warning_in_hg > -1) {
24+
result.new_current_hostgroup = sess_state.warning_in_hg;
25+
}
26+
return result;
27+
}
28+
29+
// 3. LAST_INSERT_ID / @@IDENTITY
30+
if (qpo_state.is_last_insert_id) {
31+
if (sess_state.last_HG_affected_rows >= 0) {
32+
result.new_current_hostgroup = sess_state.last_HG_affected_rows;
33+
return result;
34+
}
35+
}
36+
37+
// 4. Default routing from QPO
38+
if (qpo_state.destination_hostgroup >= 0) {
39+
if (sess_state.transaction_persistent_hostgroup == -1) {
40+
result.new_current_hostgroup = qpo_state.destination_hostgroup;
41+
}
42+
}
43+
44+
// 5. Hostgroup Locking Decisions (mysql-set_query_lock_on_hostgroup)
45+
if (set_query_lock_on_hostgroup == 1) {
46+
// Algorithm introduced in ProxySQL 2.0.6
47+
if (result.new_locked_on_hostgroup < 0) {
48+
if (qpo_state.is_set_statement) {
49+
// If it's a SET statement that caused locking (determined by parser)
50+
// In a pure function, we assume qpo_state.is_set_statement implies it should lock
51+
result.lock_hostgroup = true;
52+
result.new_locked_on_hostgroup = result.new_current_hostgroup;
53+
}
54+
}
55+
56+
if (result.new_locked_on_hostgroup >= 0) {
57+
if (result.new_current_hostgroup != result.new_locked_on_hostgroup) {
58+
result.error = true;
59+
result.error_msg = "ProxySQL Error: connection is locked to hostgroup " +
60+
std::to_string(result.new_locked_on_hostgroup) +
61+
" but trying to reach hostgroup " +
62+
std::to_string(result.new_current_hostgroup);
63+
return result;
64+
}
65+
}
66+
} else {
67+
// Legacy behavior before 2.0.6
68+
if (sess_state.transaction_persistent_hostgroup == -1) {
69+
if (qpo_state.destination_hostgroup < 0) {
70+
result.new_current_hostgroup = sess_state.default_hostgroup;
71+
}
72+
}
73+
}
74+
75+
return result;
76+
}

0 commit comments

Comments
 (0)