Skip to content

Commit

Permalink
Merge pull request #4733 from sysown/v2.7_issue_4707_threshold_result…
Browse files Browse the repository at this point in the history
…set_size

Fix Overflow in mysql_thread___threshold_resultset_size During Multiplication
  • Loading branch information
renecannao authored Nov 11, 2024
2 parents b95f1dd + 936c721 commit 0158e98
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 5 deletions.
12 changes: 12 additions & 0 deletions include/gen_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,18 @@ inline unsigned long long realtime_time() {
return (((unsigned long long) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000);
}

template<int FACTOR, typename T>
inline T overflow_safe_multiply(T val) {
static_assert(std::is_integral<T>::value, "T must be an integer type.");
static_assert(std::is_unsigned_v<T>, "T must be an unsigned integer type.");
static_assert(FACTOR > 0, "Negative factors are not supported.");

if constexpr (FACTOR == 0) return 0;
if (val == 0) return 0;
if (val > std::numeric_limits<T>::max() / FACTOR) return std::numeric_limits<T>::max();
return (val * FACTOR);
}

#endif /* __GEN_FUNCTIONS */

bool Proxy_file_exists(const char *);
Expand Down
2 changes: 1 addition & 1 deletion lib/MySQL_Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5995,7 +5995,7 @@ bool MySQL_Thread::set_backend_to_be_skipped_if_frontend_is_slow(MySQL_Data_Stre
// we pause receiving from backend at mysql_thread___threshold_resultset_size * 8
// but assuming that client isn't completely blocked, we will stop checking for data
// only at mysql_thread___threshold_resultset_size * 4
if (buffered_data > (unsigned int)mysql_thread___threshold_resultset_size*4) {
if (buffered_data > overflow_safe_multiply<4,unsigned int>(mysql_thread___threshold_resultset_size)) {
mypolls.fds[n].events = 0;
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions lib/mysql_connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1509,7 +1509,7 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) {
unsigned int buffered_data=0;
buffered_data = myds->sess->client_myds->PSarrayOUT->len * RESULTSET_BUFLEN;
buffered_data += myds->sess->client_myds->resultset->len * RESULTSET_BUFLEN;
if (buffered_data > (unsigned int)mysql_thread___threshold_resultset_size*8) {
if (buffered_data > overflow_safe_multiply<8,unsigned int>(mysql_thread___threshold_resultset_size)) {
next_event(ASYNC_STMT_EXECUTE_STORE_RESULT_CONT); // we temporarily pause . See #1232
break;
}
Expand All @@ -1535,7 +1535,7 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) {
if (rows_read_inner > 1) {
process_rows_in_ASYNC_STMT_EXECUTE_STORE_RESULT_CONT(processed_bytes);
if (
(processed_bytes > (unsigned int)mysql_thread___threshold_resultset_size*8)
(processed_bytes > overflow_safe_multiply<8,unsigned int>(mysql_thread___threshold_resultset_size))
||
( mysql_thread___throttle_ratio_server_to_client && mysql_thread___throttle_max_bytes_per_second_to_client && (processed_bytes > (unsigned long long)mysql_thread___throttle_max_bytes_per_second_to_client/10*(unsigned long long)mysql_thread___throttle_ratio_server_to_client) )
) {
Expand Down Expand Up @@ -1688,7 +1688,7 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) {
unsigned int buffered_data=0;
buffered_data = myds->sess->client_myds->PSarrayOUT->len * RESULTSET_BUFLEN;
buffered_data += myds->sess->client_myds->resultset->len * RESULTSET_BUFLEN;
if (buffered_data > (unsigned int)mysql_thread___threshold_resultset_size*8) {
if (buffered_data > overflow_safe_multiply<8,unsigned int>(mysql_thread___threshold_resultset_size)) {
next_event(ASYNC_USE_RESULT_CONT); // we temporarily pause . See #1232
break;
}
Expand Down Expand Up @@ -1742,7 +1742,7 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) {
bytes_info.bytes_recv += br;
processed_bytes+=br; // issue #527 : this variable will store the amount of bytes processed during this event
if (
(processed_bytes > (unsigned int)mysql_thread___threshold_resultset_size*8)
(processed_bytes > overflow_safe_multiply<8,unsigned int>(mysql_thread___threshold_resultset_size))
||
( mysql_thread___throttle_ratio_server_to_client && mysql_thread___throttle_max_bytes_per_second_to_client && (processed_bytes > (unsigned long long)mysql_thread___throttle_max_bytes_per_second_to_client/10*(unsigned long long)mysql_thread___throttle_ratio_server_to_client) )
) {
Expand Down
113 changes: 113 additions & 0 deletions test/tap/tests/mysql-reg_test_4707_threshold_resultset_size-t.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* @file mysql-reg_test_4707_threshold_resultset_size-t.cpp
* @brief The test specifically examines the impact of different mysql-threshold_resultset_size threshold values on query response times
* and addresses an identified issue caused by variable overflow, which results in slow performance.
*/

#include <string>
#include <sstream>
#include <chrono>
#include "mysql.h"
#include "command_line.h"
#include "tap.h"
#include "utils.h"

CommandLine cl;

int main(int argc, char** argv) {

plan(6); // Total number of tests planned

if (cl.getEnv())
return exit_status();

// Initialize Admin connection
MYSQL* proxysql_admin = mysql_init(NULL);
if (!proxysql_admin) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin));
return -1;
}

// Connnect to ProxySQL Admin
if (!mysql_real_connect(proxysql_admin, cl.admin_host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin));
return -1;
}

// Initialize Backend connection
MYSQL* proxysql_backend = mysql_init(NULL);
if (!proxysql_backend) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_backend));
return -1;
}

// Connnect to ProxySQL Backend
if (!mysql_real_connect(proxysql_backend, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_backend));
return -1;
}
MYSQL_QUERY(proxysql_admin, "DELETE FROM mysql_query_rules");
MYSQL_QUERY(proxysql_admin, "LOAD MYSQL QUERY RULES TO RUNTIME");
MYSQL_QUERY(proxysql_admin, "SET mysql-poll_timeout=2000");
MYSQL_QUERY(proxysql_admin, "SET mysql-threshold_resultset_size=8000");
MYSQL_QUERY(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME");

int rc;

auto start = std::chrono::high_resolution_clock::now();
rc = mysql_query(proxysql_backend, "SELECT 1");
auto end = std::chrono::high_resolution_clock::now();

if (rc == 0) {
MYSQL_RES* res = mysql_store_result(proxysql_backend);
ok(res != nullptr, "Query executed successfully. %s", mysql_error(proxysql_backend));
mysql_free_result(res);
}
else {
ok(false, "Error executing query. %s", mysql_error(proxysql_admin));
}

std::chrono::duration<double, std::milli> duration = end - start;
ok(duration.count() < 10.00, "Execution time should be less than 10 ms. Actual: %f ms", duration.count());

MYSQL_QUERY(proxysql_admin, "SET mysql-threshold_resultset_size=536870912");
MYSQL_QUERY(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME");

start = std::chrono::high_resolution_clock::now();
rc = mysql_query(proxysql_backend, "SELECT 1");
end = std::chrono::high_resolution_clock::now();

if (rc == 0) {
MYSQL_RES* res = mysql_store_result(proxysql_backend);
ok(res != nullptr, "Query executed successfully. %s", mysql_error(proxysql_backend));
mysql_free_result(res);
}
else {
ok(false, "Error executing query. %s", mysql_error(proxysql_admin));
}
duration = end - start;
ok(duration.count() < 10.00, "Execution time should be less than 10 ms. Actual: %f ms", duration.count());

MYSQL_QUERY(proxysql_admin, "SET mysql-threshold_resultset_size=1073741824");
MYSQL_QUERY(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME");

start = std::chrono::high_resolution_clock::now();
rc = mysql_query(proxysql_backend, "SELECT 1");
end = std::chrono::high_resolution_clock::now();

if (rc == 0) {
MYSQL_RES* res = mysql_store_result(proxysql_backend);
ok(res != nullptr, "Query executed successfully. %s", mysql_error(proxysql_backend));
mysql_free_result(res);
}
else {
ok(false, "Error executing query. %s", mysql_error(proxysql_admin));
}
duration = end - start;
ok(duration.count() < 10.00, "Execution time should be less than 10 ms. Actual: %f ms", duration.count());

mysql_close(proxysql_backend);
mysql_close(proxysql_admin);

return exit_status();
}

0 comments on commit 0158e98

Please sign in to comment.