You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
proxysql/test/tap/tests/test_ffto_bypass-t.cpp

139 lines
5.7 KiB

#include <string>
#include <stdio.h>
#include <cstring>
#include <unistd.h>
#include <vector>
#include "mysql.h"
#include "tap.h"
#include "command_line.h"
#include "utils.h"
int main(int argc, char** argv) {
CommandLine cl;
if (cl.getEnv()) {
diag("Failed to get the required environmental variables.");
return -1;
}
plan(15);
diag("=== FFTO Bypass Test ===");
diag("This test validates that queries larger than the FFTO max buffer size");
diag("bypass the FFTO (Fast Forward To Optimization) mechanism entirely.");
diag("========================");
MYSQL* admin = mysql_init(NULL);
if (!mysql_real_connect(admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
diag("Admin connection failed");
return -1;
}
ok(admin != NULL, "Connected to ProxySQL Admin");
// Set a very small threshold: 100 bytes
ok(mysql_query(admin, "UPDATE global_variables SET variable_value='true' WHERE variable_name='mysql-ffto_enabled'") == 0, "Enable FFTO");
ok(mysql_query(admin, "UPDATE global_variables SET variable_value='100' WHERE variable_name='mysql-ffto_max_buffer_size'") == 0, "Set FFTO max buffer size to 100");
ok(mysql_query(admin, "LOAD MYSQL VARIABLES TO RUNTIME") == 0, "Load variables to runtime");
// Ensure user exists
char user_query[1024];
snprintf(user_query, sizeof(user_query), "INSERT OR REPLACE INTO mysql_users (username, password, default_hostgroup, fast_forward, default_schema) VALUES ('%s', '%s', 0, 1, 'information_schema')", cl.username, cl.password);
ok(mysql_query(admin, user_query) == 0, "Configure user with fast_forward=1 and default_schema");
ok(mysql_query(admin, "LOAD MYSQL USERS TO RUNTIME") == 0, "Load users to runtime");
// Ensure backend server exists
char server_query[1024];
snprintf(server_query, sizeof(server_query), "INSERT OR REPLACE INTO mysql_servers (hostgroup_id, hostname, port) VALUES (0, '%s', %d)", cl.mysql_host, cl.mysql_port);
mysql_query(admin, server_query);
mysql_query(admin, "LOAD MYSQL SERVERS TO RUNTIME");
ok(mysql_query(admin, "TRUNCATE TABLE stats_mysql_query_digest") == 0, "Clear stats_mysql_query_digest using TRUNCATE");
MYSQL* conn = mysql_init(NULL);
if (!mysql_real_connect(conn, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
diag("Client connection failed: %s", mysql_error(conn));
return -1;
}
ok(conn != NULL, "Connected to ProxySQL Data Port");
ok(mysql_query(conn, "USE information_schema") == 0, "Execute USE information_schema");
// 1. Send a SMALL query (should be recorded)
const char* small_query = "SELECT 1111";
ok(mysql_query(conn, small_query) == 0, "Send small query (should be recorded)");
{
MYSQL_RES* query_res = mysql_store_result(conn);
if (query_res) mysql_free_result(query_res);
}
// Verify small query recorded - wait up to 2 seconds
int count = 0;
for (int i=0; i<20; i++) {
mysql_query(admin, "SELECT count(*) FROM stats_mysql_query_digest WHERE digest_text = 'SELECT ?'");
MYSQL_RES* res = mysql_store_result(admin);
MYSQL_ROW row = mysql_fetch_row(res);
count = row ? atoi(row[0]) : -1;
if (res) mysql_free_result(res);
if (count > 0) break;
usleep(100000); // 100ms
}
if (count == 0) {
diag("Small query NOT found. Dumping ALL digests in stats_mysql_query_digest:");
mysql_query(admin, "SELECT digest_text, count_star FROM stats_mysql_query_digest");
MYSQL_RES* dr = mysql_store_result(admin);
MYSQL_ROW drw;
while(dr && (drw = mysql_fetch_row(dr))) {
diag(" Digest: %s, Count: %s", drw[0], drw[1]);
}
if (dr) mysql_free_result(dr);
}
ok(count > 0, "Small query was recorded by FFTO (count: %d)", count);
// 2. Clear and verify it is empty
ok(mysql_query(admin, "TRUNCATE TABLE stats_mysql_query_digest") == 0, "Clear stats_mysql_query_digest again using TRUNCATE");
mysql_query(admin, "SELECT count(*) FROM stats_mysql_query_digest");
MYSQL_RES* res = mysql_store_result(admin);
MYSQL_ROW row = mysql_fetch_row(res);
count = row ? atoi(row[0]) : -1;
ok(count == 0, "Verified stats_mysql_query_digest is empty after TRUNCATE (count: %d)", count);
if (res) mysql_free_result(res);
// 3. Send LARGE query (should NOT be recorded)
std::string large_query = "SELECT '";
for(int i=0; i<200; i++) large_query += "x";
large_query += "', 2222";
ok(mysql_query(conn, large_query.c_str()) == 0, "Send large query (exceeding threshold, should NOT be recorded)");
{
MYSQL_RES* query_res = mysql_store_result(conn);
if (query_res) mysql_free_result(query_res);
}
// Wait a bit to ensure it would have appeared if it was recorded
usleep(500000);
// Verify that NO digest was recorded for the large query
mysql_query(admin, "SELECT count(*), digest_text FROM stats_mysql_query_digest");
res = mysql_store_result(admin);
if (!res) {
ok(0, "Failed to store result from stats query");
} else {
row = mysql_fetch_row(res);
count = row ? atoi(row[0]) : -1;
if (count != 0) {
diag("Unexpected digests found in stats_mysql_query_digest:");
mysql_data_seek(res, 0);
while((row = mysql_fetch_row(res))) {
diag(" Count: %s, Digest: %s", row[0], row[1]);
}
}
ok(count == 0, "No digests recorded for queries exceeding threshold (count: %d)", count);
}
if (res) mysql_free_result(res);
mysql_close(conn);
mysql_close(admin);
return exit_status();
}