Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Overflow in mysql_thread___threshold_resultset_size During Multiplication #4733

Open
wants to merge 4 commits into
base: v2.7
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
}