Merge branch 'v2.x' into v2.6.0-tap_test_fixes

pull/4304/head
Miro Stauder 3 years ago
commit 58eaf8bbe1

@ -127,6 +127,16 @@ class MySQL_Session
void return_proxysql_internal(PtrSize_t *);
bool handler_special_queries(PtrSize_t *);
/**
* @brief Handles 'COMMIT|ROLLBACK' commands.
* @details Forwarding the packet is required when there are active transactions. Since we are limited to
* forwarding just one 'COMMIT|ROLLBACK', we work under the assumption that we only have one active
* transaction. If more transactions are simultaneously open for the session, more 'COMMIT|ROLLBACK'.
* commands are required to be issued by the client, so they could be forwarded to the corresponding
* backend connections.
* @param The received packet to be handled.
* @return 'true' if the packet is intercepted and never forwarded to the client, 'false' otherwise.
*/
bool handler_CommitRollback(PtrSize_t *);
bool handler_SetAutocommit(PtrSize_t *);
/**
@ -329,6 +339,18 @@ class MySQL_Session
unsigned int NumActiveTransactions(bool check_savpoint=false);
bool HasOfflineBackends();
bool SetEventInOfflineBackends();
/**
* @brief Finds one active transaction in the current backend connections.
* @details Since only one connection is returned, if the session holds multiple backend connections with
* potential transactions, the priority is:
* 1. Connections flagged with 'SERVER_STATUS_IN_TRANS', or 'autocommit=0' in combination with
* 'autocommit_false_is_transaction'.
* 2. Connections with 'autocommit=0' holding a 'SAVEPOINT'.
* 3. Connections with 'unknown transaction status', e.g: connections with errors.
* @param check_savepoint Used to also check for connections holding savepoints. See MySQL bug
* https://bugs.mysql.com/bug.php?id=107875.
* @returns The hostgroup in which the connection was found, -1 in case no connection is found.
*/
int FindOneActiveTransaction(bool check_savepoint=false);
unsigned long long IdleTime();

@ -209,16 +209,21 @@ class MySQL_Connection {
void process_rows_in_ASYNC_STMT_EXECUTE_STORE_RESULT_CONT(unsigned long long& processed_bytes);
void async_free_result();
bool IsActiveTransaction(); /* {
bool ret=false;
if (mysql) {
ret = (mysql->server_status & SERVER_STATUS_IN_TRANS);
if (ret == false && (mysql)->net.last_errno) {
ret = true;
}
}
return ret;
} */
/**
* @brief Returns if the connection is **for sure**, known to be in an active transaction.
* @details The function considers two things:
* 1. If 'server_status' is flagged with 'SERVER_STATUS_IN_TRANS'.
* 2. If the connection has 'autcommit=0' and 'autocommit_false_is_transaction' is set.
* @return True if the connection is known to be in a transaction, or equivalent state.
*/
bool IsKnownActiveTransaction();
/**
* @brief Returns if the connection is in a **potential transaction**.
* @details This function is a more strict version of 'IsKnownActiveTransaction', which also considers
* connections which holds 'unknown_transaction_status' as potentially active transactions.
* @return True if the connection is in potentially in an active transaction.
*/
bool IsActiveTransaction();
bool IsServerOffline();
bool IsAutoCommit();
bool AutocommitFalse_AndSavepoint();

@ -387,8 +387,21 @@ class Query_Processor {
* @return Old 'fast_routing_resultset' that has been replaced. Required to be freed by caller.
*/
SQLite3_result* load_fast_routing(const fast_routing_hashmap_t& fast_routing_hashmap);
/**
* @brief Searches for a matching rule in the supplied map, returning the destination hostgroup.
* @details This functions takes a pointer to the hashmap pointer. This is because it performs a
* conditional internal locking of member 'rwlock'. Since the original pointer value could be modified
* after the function call, we must perform the resource acquisition (dereference) after we have
* acquired the internal locking.
* @param khStrInt The map to be used for performing the search. See @details.
* @param u Username, used for the search as part of the map key.
* @param s Schemaname, used for the search as part of the map key.
* @param flagIN FlagIn, used for the search as part of the map key.
* @param lock Whether or not the member lock 'rwlock' should be taken for the search.
* @return If a matching rule is found, the target destination hostgroup, -1 otherwise.
*/
int search_rules_fast_routing_dest_hg(
khash_t(khStrInt)* _rules_fast_routing, const char* u, const char* s, int flagIN, bool lock
khash_t(khStrInt)** __rules_fast_routing, const char* u, const char* s, int flagIN, bool lock
);
SQLite3_result * get_current_query_rules_fast_routing();
SQLite3_result * get_current_query_rules_fast_routing_inner();

@ -917,9 +917,16 @@ bool MySQL_Session::handler_CommitRollback(PtrSize_t *pkt) {
// in this part of the code (as at release 2.4.3) where we call
// NumActiveTransactions() with the check_savepoint flag .
// This to try to handle MySQL bug https://bugs.mysql.com/bug.php?id=107875
unsigned int nTrx=NumActiveTransactions(true);
if (nTrx) {
//
// Since we are limited to forwarding just one 'COMMIT|ROLLBACK', we work under the assumption that we
// only have one active transaction. Under this premise, we should execute this command under that
// specific connection, for that, we update 'current_hostgroup' with the first active transaction we are
// able to find. If more transactions are simultaneously open for the session, more 'COMMIT|ROLLBACK'
// commands are required to be issued by the client to continue ending transactions.
unsigned int hg = FindOneActiveTransaction(true);
if (hg != -1) {
// there is an active transaction, we must forward the request
current_hostgroup = hg;
return false;
} else {
// there is no active transaction, we will just reply OK
@ -7497,8 +7504,10 @@ int MySQL_Session::FindOneActiveTransaction(bool check_savepoint) {
_mybe=(MySQL_Backend *)mybes->index(i);
if (_mybe->server_myds) {
if (_mybe->server_myds->myconn) {
if (_mybe->server_myds->myconn->IsActiveTransaction()) {
if (_mybe->server_myds->myconn->IsKnownActiveTransaction()) {
return (int)_mybe->server_myds->myconn->parent->myhgc->hid;
} else if (_mybe->server_myds->myconn->IsActiveTransaction()) {
ret = (int)_mybe->server_myds->myconn->parent->myhgc->hid;
} else {
// we use check_savepoint to check if we shouldn't ignore COMMIT or ROLLBACK due
// to MySQL bug https://bugs.mysql.com/bug.php?id=107875 related to

@ -981,7 +981,7 @@ SQLite3_result * Query_Processor::get_current_query_rules_fast_routing() {
}
int Query_Processor::search_rules_fast_routing_dest_hg(
khash_t(khStrInt)* _rules_fast_routing, const char* u, const char* s, int flagIN, bool lock
khash_t(khStrInt)** __rules_fast_routing, const char* u, const char* s, int flagIN, bool lock
) {
int dest_hg = -1;
const size_t u_len = strlen(u);
@ -998,6 +998,7 @@ int Query_Processor::search_rules_fast_routing_dest_hg(
if (lock) {
pthread_rwlock_rdlock(&this->rwlock);
}
khash_t(khStrInt)* _rules_fast_routing = *__rules_fast_routing;
khiter_t k = kh_get(khStrInt, _rules_fast_routing, keybuf_ptr);
if (k == kh_end(_rules_fast_routing)) {
khiter_t k2 = kh_get(khStrInt, _rules_fast_routing, keybuf_ptr + u_len);
@ -2133,10 +2134,12 @@ __exit_process_mysql_query:
if (_thr_SQP_rules_fast_routing != nullptr) {
proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 7, "Searching thread-local 'rules_fast_routing' hashmap with: user='%s', schema='%s', and flagIN='%d'\n", u, s, flagIN);
dst_hg = search_rules_fast_routing_dest_hg(_thr_SQP_rules_fast_routing, u, s, flagIN, false);
dst_hg = search_rules_fast_routing_dest_hg(&_thr_SQP_rules_fast_routing, u, s, flagIN, false);
} else if (rules_fast_routing != nullptr) {
proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 7, "Searching global 'rules_fast_routing' hashmap with: user='%s', schema='%s', and flagIN='%d'\n", u, s, flagIN);
dst_hg = search_rules_fast_routing_dest_hg(rules_fast_routing, u, s, flagIN, true);
// NOTE: A pointer to the member 'this->rules_fast_routing' is required, since the value of the
// member could have changed before the function acquires the internal lock. See function doc.
dst_hg = search_rules_fast_routing_dest_hg(&this->rules_fast_routing, u, s, flagIN, true);
}
if (dst_hg != -1) {
@ -3263,7 +3266,7 @@ int Query_Processor::testing___find_HG_in_mysql_query_rules_fast_routing_dual(
khash_t(khStrInt)* rules_fast_routing = _rules_fast_routing ? _rules_fast_routing : this->rules_fast_routing;
if (rules_fast_routing) {
ret = search_rules_fast_routing_dest_hg(rules_fast_routing, username, schemaname, flagIN, lock);
ret = search_rules_fast_routing_dest_hg(&rules_fast_routing, username, schemaname, flagIN, lock);
}
return ret;

@ -2385,6 +2385,16 @@ bool MySQL_Connection::AutocommitFalse_AndSavepoint() {
return ret;
}
bool MySQL_Connection::IsKnownActiveTransaction() {
bool in_trx = mysql ? mysql->server_status & SERVER_STATUS_IN_TRANS : false;
if (in_trx == false) {
in_trx = mysql_thread___autocommit_false_is_transaction && (IsAutoCommit() == false);
}
return in_trx;
}
bool MySQL_Connection::IsActiveTransaction() {
bool ret=false;
if (mysql) {

@ -16,15 +16,21 @@
#include "utils.h"
#include <unistd.h>
#include <utility>
#include <sys/wait.h>
#include <fcntl.h>
#include <iostream>
#include "proxysql_utils.h"
using std::pair;
using std::map;
using std::string;
using std::vector;
using std::to_string;
using nlohmann::json;
std::size_t count_matches(const string& str, const string& substr) {
std::size_t result = 0;
std::size_t pos = 0;
@ -1256,3 +1262,314 @@ cleanup:
return res;
}
json fetch_internal_session(MYSQL* proxy) {
int rc = mysql_query_t(proxy, "PROXYSQL INTERNAL SESSION");
if (rc ) {
return json {};
} else {
MYSQL_RES* myres = mysql_store_result(proxy);
MYSQL_ROW row = mysql_fetch_row(myres);
json j_session = json::parse(row[0]);
mysql_free_result(myres);
return j_session;
}
}
struct cols_table_info_t {
vector<string> names;
vector<size_t> widths;
};
std::string dump_as_table(MYSQL_RES* result, const cols_table_info_t& cols_info) {
if (!result) { return {}; }
const vector<string>& cols_names { cols_info.names };
const vector<size_t>& cols_widths { cols_info.widths };
uint32_t num_fields = mysql_num_fields(result);
std::string table_str { "+" };
for (size_t width : cols_widths) {
table_str += std::string(width + 2, '-') + "+";
}
table_str += "\n";
table_str += "|";
for (size_t col = 0; col < num_fields; col++) {
table_str += " " + cols_names[col] + std::string(cols_widths[col] - cols_names[col].size(), ' ') + " |";
}
table_str += "\n";
table_str += "+";
for (size_t width : cols_widths) {
table_str += std::string(width + 2, '-') + "+";
}
table_str += "\n";
while (MYSQL_ROW row = mysql_fetch_row(result)) {
table_str += "|";
for (size_t col = 0; col < num_fields; col++) {
std::string value = row[col] ? row[col] : "";
table_str += " " + value + std::string(cols_widths[col] - value.size(), ' ') + " |";
}
table_str += "\n";
}
table_str += "+";
for (size_t width : cols_widths) {
table_str += std::string(width + 2, '-') + "+";
}
table_str += "\n";
mysql_data_seek(result, 0);
return table_str;
}
std::string dump_as_table(MYSQL_RES* result) {
if (!result) { return {}; }
uint32_t num_fields = mysql_num_fields(result);
MYSQL_FIELD* fields = mysql_fetch_fields(result);
vector<string> columns {};
for (uint32_t i = 0; i < num_fields; ++i) {
columns.push_back(fields[i].name);
}
vector<size_t> cols_widths(num_fields, 0);
for (int col = 0; col < num_fields; ++col) {
cols_widths[col] = std::max(cols_widths[col], columns[col].size());
}
while (MYSQL_ROW row = mysql_fetch_row(result)) {
for (uint32_t col = 0; col < num_fields; col++) {
if (row[col]) {
cols_widths[col] = std::max(cols_widths[col], strlen(row[col]));
}
}
}
mysql_data_seek(result, 0);
std::string res { dump_as_table(result, {columns, cols_widths}) };
return res;
}
pair<int,vector<mysql_row_t>> exec_dql_query(MYSQL* conn, const string& query, bool dump_res) {
if (mysql_query(conn, query.c_str())) {
diag("Failed to executed query `%s`", query.c_str());
return { EXIT_FAILURE, {} };
}
MYSQL_RES* my_stats_res = mysql_store_result(conn);
if (my_stats_res == nullptr) {
diag("Failed to retrieve a resultset, expected DQL query");
return { EXIT_FAILURE, {} };
} else {
if (dump_res) {
fprintf(stderr, "%s", dump_as_table(my_stats_res).c_str());
}
vector<mysql_row_t> my_rows { extract_mysql_rows(my_stats_res) };
mysql_free_result(my_stats_res);
return { EXIT_SUCCESS, my_rows };
}
}
string join(string delim, const vector<string>& words) {
return std::accumulate(
words.begin(), words.end(), string {},
[&delim] (const string& s1, const string& s2) {
if (s1.empty()) {
return s2;
} else {
return s1 + delim + s2;
}
}
);
}
string gen_conn_stats_query(const vector<uint32_t>& hgs) {
const auto _to_string = [] (uint32_t n) -> string { return to_string(n); };
vector<string> hgs_str {};
std::transform(hgs.begin(), hgs.end(), std::back_inserter(hgs_str), _to_string);
const string CONN_STATS_HGS { join(",", hgs_str) };
const string CONN_STATS_QUERY_T {
"SELECT hostgroup,ConnUsed,ConnFree,ConnOk,ConnERR,MaxConnUsed,Queries"
" FROM stats.stats_mysql_connection_pool"
};
if (hgs.empty()) {
return CONN_STATS_QUERY_T;
} else {
return CONN_STATS_QUERY_T + " WHERE hostgroup IN (" + CONN_STATS_HGS + ")";
}
}
int dump_conn_stats(MYSQL* admin, const vector<uint32_t> hgs) {
const string query { gen_conn_stats_query(hgs) };
MYSQL_QUERY(admin, query.c_str());
MYSQL_RES* myres = mysql_store_result(admin);
const string table { dump_as_table(myres) };
mysql_free_result(myres);
fprintf(stderr, "%s", table.c_str());
return EXIT_SUCCESS;
}
pair<int,pool_state_t> fetch_conn_stats(MYSQL* admin, const vector<uint32_t> hgs) {
const string stats_query { gen_conn_stats_query(hgs) };
const pair<int,vector<mysql_row_t>> conn_pool_stats { exec_dql_query(admin, stats_query, true) };
if (conn_pool_stats.first || conn_pool_stats.second.size() != hgs.size()) {
if (conn_pool_stats.first) {
diag("Failed to extract stats from 'CONNPOOL'");
}
if (conn_pool_stats.second.size() != hgs.size()) {
diag("Expected '%ld' row in 'CONNPOOL' stats resultset", hgs.size());
}
return { EXIT_FAILURE, {} };
}
if (conn_pool_stats.first) {
return { conn_pool_stats.first, {} };
} else {
map<uint32_t,mysql_row_t> res_map {};
for (const mysql_row_t& row : conn_pool_stats.second) {
const string& column = row[POOL_STATS_IDX::HOSTGROUP];
const uint32_t hg = std::stol(row[POOL_STATS_IDX::HOSTGROUP]);
res_map.insert({ hg, row });
}
return { EXIT_SUCCESS, res_map };
}
}
int wait_for_cond(MYSQL* mysql, const std::string& query, uint32_t timeout) {
int result = EXIT_FAILURE;
auto start = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed {};
while (elapsed.count() < timeout && result == EXIT_FAILURE) {
int rc = mysql_query(mysql, query.c_str());
fprintf(
stderr, "# %s: Waiting for condition '%s' in ('%s':%d)\n",
get_formatted_time().c_str(), query.c_str(), mysql->host, mysql->port
);
if (rc == EXIT_SUCCESS) {
MYSQL_RES* myres = mysql_store_result(mysql);
if (myres) {
uint32_t field_num = mysql_num_fields(myres);
uint32_t row_num = mysql_num_rows(myres);
if (field_num == 1 && row_num == 1) {
MYSQL_ROW row = mysql_fetch_row(myres);
if (row && strcasecmp("TRUE", row[0]) == 0) {
result = EXIT_SUCCESS;
}
}
mysql_free_result(myres);
if (result == EXIT_SUCCESS) {
break;
}
}
} else {
diag("Condition query failed with error: ('%d','%s')", mysql_errno(mysql), mysql_error(mysql));
result = EXIT_FAILURE;
break;
}
usleep(500 * 1000);
auto it_end = std::chrono::system_clock::now();
elapsed = it_end - start;
}
return result;
}
void check_conn_count(MYSQL* admin, const string& conn_type, uint32_t conn_num, int32_t hg) {
const string hg_s { to_string(hg) };
const string conn_num_s { to_string(conn_num) };
string select_conns_in_hg {};
if (hg == -1) {
select_conns_in_hg = "SELECT SUM(" + conn_type + ") FROM stats_mysql_connection_pool";
} else {
select_conns_in_hg = "SELECT " + conn_type + " FROM stats_mysql_connection_pool WHERE hostgroup=" + hg_s;
}
const string check_used_conns {
"SELECT IIF((" + select_conns_in_hg + ")=" + conn_num_s + ",'TRUE','FALSE')"
};
int to = wait_for_cond(admin, check_used_conns, 3);
ok(to == EXIT_SUCCESS, "Conns should met the required condition");
if (to != EXIT_SUCCESS) {
dump_conn_stats(admin, {});
}
};
void check_query_count(MYSQL* admin, uint32_t queries, uint32_t hg) {
const string queries_s { to_string(queries) };
const string hg_s { to_string(hg) };
const string select_hg_queries {
"SELECT Queries FROM stats_mysql_connection_pool WHERE hostgroup=" + to_string(hg)
};
const string check_queries {
"SELECT IIF((" + select_hg_queries + ")=" + queries_s + ",'TRUE','FALSE')"
};
int to = wait_for_cond(admin, check_queries, 3);
ok(to == EXIT_SUCCESS, "Queries counted on hg '%d' should be '%d'", hg, queries);
if (to != EXIT_SUCCESS) {
dump_conn_stats(admin, {});
}
};
void check_query_count(MYSQL* admin, vector<uint32_t> queries, uint32_t hg) {
const string queries_s {
std::accumulate(queries.begin(), queries.end(), std::string(),
[](const std::string& str, const uint32_t& n) -> std::string {
return str + (str.length() > 0 ? "," : "") + std::to_string(n);
}
)
};
const string hg_s { to_string(hg) };
const string select_hg_queries {
"SELECT Queries FROM stats_mysql_connection_pool WHERE hostgroup=" + to_string(hg)
};
const string check_queries {
"SELECT IIF((" + select_hg_queries + ") IN (" + queries_s + "),'TRUE','FALSE')"
};
int to = wait_for_cond(admin, check_queries, 3);
ok(to == EXIT_SUCCESS, "Queries counted on hg '%d' should be in '%s'", hg, queries_s.c_str());
if (to != EXIT_SUCCESS) {
dump_conn_stats(admin, {});
} else {
dump_conn_stats(admin, { hg });
}
};

@ -14,6 +14,7 @@
#include "sqlite3db.h"
#include "command_line.h"
#include "json.hpp"
inline std::string get_formatted_time() {
time_t __timer;
@ -459,6 +460,11 @@ int extract_sqlite3_host_port(MYSQL* admin, std::pair<std::string, int>& host_po
*/
std::vector<std::string> split(const std::string& s, char delim);
/**
* @brief Joins the supplied list of words using the supplied delim.
*/
std::string join(std::string delim, const std::vector<std::string>& words);
/**
* @brief Gets the supplied environmental variable as a std::string.
* @param var The variable to value to extract.
@ -518,4 +524,72 @@ enum SQ3_RES_T {
*/
sq3_res_t sqlite3_execute_stmt(sqlite3* db, const std::string& query);
/**
* @brief Returns a 'JSON' object holding 'PROXYSQL INTERNAL SESSION' contents.
* @param proxy And already openned connection to ProxySQL.
*/
nlohmann::json fetch_internal_session(MYSQL* proxy);
/**
* @brief Returns a string table representation of the supplied resultset.
*/
std::string dump_as_table(MYSQL_RES* result);
using mysql_row_t = std::vector<std::string>;
/**
* @brief Executes a DQL query and returns the contents of its resultset.
* @param conn An already opened MYSQL connection.
* @param query The DQL query to be executed.
* @param dump_res Wether or not to dump the resultset contents as a table to 'stderr'.
* @return A pair with the shape {err_code, contents}.
*/
std::pair<int,std::vector<mysql_row_t>> exec_dql_query(MYSQL* conn, const std::string& query, bool dump_res=false);
struct POOL_STATS_IDX {
enum {
HOSTGROUP,
CONN_USED,
CONN_FREE,
CONN_OK,
CONN_ERR,
MAX_CONN_USED,
QUERIES,
};
};
/**
* @brief Dumps a resultset with fields from the supplied hgs from 'stats_mysql_connection_pool'.
* @details The fetched fields are 'hostgroup,ConnUsed,ConnFree,ConnOk,ConnERR,MaxConnUsed,Queries'.
*/
int dump_conn_stats(MYSQL* admin, const std::vector<uint32_t> hgs);
using pool_state_t = std::map<uint32_t,mysql_row_t>;
/**
* @brief Fetches several fields from table 'stats_mysql_connection_pool' for supplied hostgroups.
* @details The fetched fields are 'hostgroup,ConnUsed,ConnFree,ConnOk,ConnERR,MaxConnUsed,Queries'.
* @param admin An already opened connection to Admin.
* @param hgs The hostgroups from which to fetch several fields.
* @return A pair of the shape {err_code, pool_state_t}.
*/
std::pair<int,pool_state_t> fetch_conn_stats(MYSQL* admin, const std::vector<uint32_t> hgs);
/**
* @brief Waits until the condition specified by the 'query' holds, or 'timeout' is reached.
* @details Several details about the function impl:
* - Sleeps of 500ms are performed between each check.
* - The time and check being performed is always logged ahead.
* - If query execution fails, reason is logged, wait aborted and EXIT_FAILURE returned.
* @param mysql And already opened conn to ProxySQL in which the query is to be executed.
* @param query Query with the condition check, it's expected to return 'TRUE' when the check succeeds.
* @param timeout A timeout specified in seconds.
* @return EXIT_SUCCESS if the checks holds before the timeout, EXIT_FAILURE otherwise.
*/
int wait_for_cond(MYSQL* mysql, const std::string& query, uint32_t timeout);
// Helpers using 'wait_for_cond' on 'stats_mysql_connection'
void check_conn_count(MYSQL* admin, const std::string& conn_type, uint32_t conn_num, int32_t hg=-1);
void check_query_count(MYSQL* admin, uint32_t queries, uint32_t hg);
void check_query_count(MYSQL* admin, std::vector<uint32_t> queries, uint32_t hg);
#endif // #define UTILS_H

@ -23,24 +23,6 @@
using std::string;
using namespace nlohmann;
/**
* @brief Helper function to convert a 'MYSQL_RES' into a
* nlohmann::json.
*
* @param result The 'MYSQL_RES*' to be converted into JSON.
* @param j 'nlohmann::json' output parameter holding the
* converted 'MYSQL_RES' supplied.
*/
void parse_result_json_column(MYSQL_RES *result, json& j) {
if(!result) return;
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
j = json::parse(row[0]);
}
}
/**
* @brief Valid variations of 'SET wait_timeout' supported
* by ProxySQL to be ignored.
@ -96,12 +78,7 @@ int main(int argc, char** argv) {
int query_err = mysql_query(proxysql_mysql, set_wait_timeout.c_str());
ok (query_err == 0, "Query '%s' should be properly executed.", set_wait_timeout.c_str());
MYSQL_QUERY(proxysql_mysql, "PROXYSQL INTERNAL SESSION");
json j_status {};
MYSQL_RES* int_session_res = mysql_store_result(proxysql_mysql);
parse_result_json_column(int_session_res, j_status);
mysql_free_result(int_session_res);
json j_status = fetch_internal_session(proxysql_mysql);
bool found_backends = j_status.contains("backends");
ok(found_backends == false, "No backends should be found for the current connection.");
}

@ -45,15 +45,7 @@ void parse_result_json_column(MYSQL_RES *result, json& j) {
int get_session_schemaname(MYSQL* proxysql, std::string& schemaname) {
int res = EXIT_FAILURE;
json j_status;
int query_res = mysql_query(proxysql, "PROXYSQL INTERNAL SESSION");
if (query_res) {
return query_res;
}
MYSQL_RES* tr_res = mysql_store_result(proxysql);
parse_result_json_column(tr_res, j_status);
mysql_free_result(tr_res);
json j_status = fetch_internal_session(proxysql);
try {
schemaname = j_status["client"]["userinfo"]["schemaname"];

File diff suppressed because it is too large Load Diff

@ -82,20 +82,10 @@ int get_query_result(MYSQL* mysql, const string& query, uint64_t& out_val) {
#define log_err(err_msg) fprintf(stderr, "File %s, line %d, Error: \"%s\"\n", __FILE__, __LINE__, err_msg);
int get_conn_auto_inc_delay_token(MYSQL* proxy_mysql, int& out_auto_inc_delay) {
MYSQL_QUERY(proxy_mysql, "PROXYSQL INTERNAL SESSION");
MYSQL_RES* my_res = mysql_store_result(proxy_mysql);
vector<mysql_res_row> int_sess_res = extract_mysql_rows(my_res);
mysql_free_result(my_res);
int cur_auto_inc_delay_mult = 0;
if (int_sess_res.empty()) {
log_err("Empty result received from 'PROXYSQL INTERNAL SESSION'");
return EXIT_FAILURE;
}
try {
nlohmann::json j_int_sess = nlohmann::json::parse(int_sess_res[0][0]);
nlohmann::json j_int_sess = fetch_internal_session(proxy_mysql);
nlohmann::json backend_conns = j_int_sess.at("backends");
nlohmann::json m_off_conn {};
@ -124,20 +114,8 @@ int get_conn_auto_inc_delay_token(MYSQL* proxy_mysql, int& out_auto_inc_delay) {
}
int get_session_backends(MYSQL* proxy_mysql,vector<json>& out_backend_conns) {
MYSQL_QUERY(proxy_mysql, "PROXYSQL INTERNAL SESSION");
MYSQL_RES* my_res = mysql_store_result(proxy_mysql);
vector<mysql_res_row> int_sess_res = extract_mysql_rows(my_res);
mysql_free_result(my_res);
int cur_auto_inc_delay_mult = 0;
if (int_sess_res.empty()) {
log_err("Empty result received from 'PROXYSQL INTERNAL SESSION'");
return EXIT_FAILURE;
}
try {
nlohmann::json j_int_sess = nlohmann::json::parse(int_sess_res[0][0]);
nlohmann::json j_int_sess = fetch_internal_session(proxy_mysql);
nlohmann::json backend_conns = j_int_sess.at("backends");
vector<json> _out_conns {};

@ -33,23 +33,6 @@
using std::string;
using namespace nlohmann;
/**
* @brief Helper function to convert a 'MYSQL_RES' into a
* nlohmann::json.
*
* @param result The 'MYSQL_RES*' to be converted into JSON.
* @param j 'nlohmann::json' output parameter holding the
* converted 'MYSQL_RES' supplied.
*/
void parse_result_json_column(MYSQL_RES *result, json& j) {
if(!result) return;
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
j = json::parse(row[0]);
}
}
using user_attributes = std::tuple<std::string, std::string, std::string, std::string>;
/**
@ -87,11 +70,7 @@ int check_front_conn_isolation_level(
const std::string& exp_iso_level,
const bool set_via_attr
) {
MYSQL_QUERY(proxysql_mysql, "PROXYSQL INTERNAL SESSION");
json j_status {};
MYSQL_RES* int_session_res = mysql_store_result(proxysql_mysql);
parse_result_json_column(int_session_res, j_status);
mysql_free_result(int_session_res);
json j_status = fetch_internal_session(proxysql_mysql);
try {
std::string front_conn_isolation_level =
@ -145,6 +124,7 @@ int check_backend_conn_isolation_level(
// Verify that the query produced a correct result
if (trx_iso_row && trx_iso_row[0]) {
trx_iso_val = std::string { trx_iso_row[0] };
mysql_free_result(trx_iso_res);
} else {
const std::string err_msg {
"Empty result received from query '" + select_trx_iso_query + "'"
@ -320,5 +300,8 @@ int main(int argc, char** argv) {
mysql_close(proxysql_mysql);
}
mysql_close(proxysql_admin);
mysql_close(mysql_server);
return exit_status();
}

@ -6,6 +6,9 @@
* execution time of the dummy queries has no been afected by the execution
* time of the queries that read from table stats_mysql_query_digest. Finally,
* check that the data stored in stats_mysql_query_digest is correct.
*
* NOTE: This test assumes that the queries being executed in sequence ('DUMMY_QUERIES') are completed within
* the same second. Failures are expected if this is not the case.
*/
#include <unistd.h>
@ -83,7 +86,7 @@ vector<digest_stats> get_digest_stats(MYSQL* proxy_admin) {
MYSQL_RES *res = NULL;
res = mysql_store_result(proxy_admin);
MYSQL_ROW row;
while (row = mysql_fetch_row(res)) {
while ((row = mysql_fetch_row(res))) {
digest_stats ds = {};
ds.hostgroup = atoi(row[0]);
ds.schemaname = row[1];
@ -269,7 +272,7 @@ int main(int argc, char** argv) {
" Client_address -> before:`%s` - after:`%s`.\n"
" Digests -> before:`%s` - after:`%s`.\n"
" Digests_text -> before:`%s` - after:`%s`.\n"
" First_seen -> before:`%lld` - after:`%lldd`.",
" First_seen -> before:`%lld` - after:`%lld`.",
ds_vector_before[i].hostgroup, ds_vector_after[i].hostgroup,
ds_vector_before[i].schemaname.c_str(), ds_vector_after[i].schemaname.c_str(),
ds_vector_before[i].username.c_str(), ds_vector_after[i].username.c_str(),
@ -284,8 +287,10 @@ int main(int argc, char** argv) {
ds_vector_after[i].digest_text.c_str(), num_dummy_queries_executed,
ds_vector_after[i].count_star - ds_vector_before[i].count_star
);
// NOTE: Equality is included for 'before' and 'after' just in case query execution was very fast.
ok(
ds_vector_before[i].last_seen < ds_vector_after[i].last_seen &&
ds_vector_before[i].last_seen <= ds_vector_after[i].last_seen &&
ds_vector_before[i].sum_time < ds_vector_after[i].sum_time,
"Last_seen and sum_time must have increased.\n"
" Last_seen -> before:`%lld` - after:`%lld`.\n"
@ -303,9 +308,9 @@ int main(int argc, char** argv) {
uint64_t bf_last_seen = ds_vector_before[i].last_seen;
ok(
init_time <= bf_last_seen && final_time >= bf_last_seen,
init_time - 1 <= bf_last_seen && final_time + 1 >= bf_last_seen,
"'last_seen' within required time range - min: %ld, max: %ld, last_seen: %ld",
init_time, final_time, bf_last_seen
init_time - 1, final_time + 1, bf_last_seen
);
}

@ -237,7 +237,7 @@ int main(int argc, char** argv) {
// EXECUTE_QUERY("DELETE FROM mysql_servers WHERE hostgroup_id=999", proxysql_admin, false),
EXECUTE_QUERY("DELETE FROM mysql_servers", proxysql_admin, false),
EXECUTE_QUERY("LOAD MYSQL SERVERS TO RUNTIME", proxysql_admin, false),
DELAY_SEC(4),
DELAY_SEC(20),
UPDATE_AFTER_METRICS(proxysql_admin),
CHECK_RESULT(std::greater<double>, "proxysql_mysql_monitor_dns_cache_record_updated"),
CHECK_RESULT(std::equal_to<double>, "proxysql_mysql_monitor_dns_cache_lookup_success"),

@ -18,15 +18,6 @@
using std::string;
using namespace nlohmann;
void parse_result_json_column(MYSQL_RES *result, json& j) {
if(!result) return;
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
j = json::parse(row[0]);
}
}
std::vector<std::string> select_queries {
"select @@session.autocommit, @@session.big_tables, @@autocommit,@@bulk_insert_buffer_size, @@character_set_database,@@transaction_isolation, @@version,@@session.transaction_isolation",
"select @@autocommit, @@sql_mode, @@big_tables, @@autocommit,@@bulk_insert_buffer_size, @@character_set_database,@@session.transaction_isolation, @@version,@@transaction_isolation",
@ -53,11 +44,7 @@ int check_multiplexing_disabled(const CommandLine& cl, const std::string query,
MYSQL_RES* dummy_res = mysql_store_result(proxysql_mysql);
mysql_free_result(dummy_res);
MYSQL_QUERY(proxysql_mysql, "PROXYSQL INTERNAL SESSION");
json j_status {};
MYSQL_RES* int_session_res = mysql_store_result(proxysql_mysql);
parse_result_json_column(int_session_res, j_status);
mysql_free_result(int_session_res);
json j_status = fetch_internal_session(proxysql_mysql);
if (j_status.contains("backends")) {
for (auto& backend : j_status["backends"]) {

Loading…
Cancel
Save