diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index 1ff246c1d..84bf6d023 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -1418,7 +1418,7 @@ const char t_restapi_insert[] { "VALUES (1,%ld,'%s','%s','%s','comm')", }; -const string base_address { "http://localhost:6070/sync/" }; +const string base_address { "http://proxysql:6070/sync/" }; int configure_endpoints( MYSQL* admin, @@ -1464,7 +1464,8 @@ int configure_endpoints( int endpoint_timeout = wait_post_enpoint_ready(full_endpoint, "{}", 1000, 100); if (endpoint_timeout) { - diag("Timeout while trying to reach first valid enpoint"); + diag("Timeout while trying to reach first valid endpoint: %s", full_endpoint.c_str()); + diag("This usually means RESTAPI enabled but script failed to execute. Check TAP_WORKDIR."); return EXIT_FAILURE; } diff --git a/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp b/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp index 86c3336d7..be29f7286 100644 --- a/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp +++ b/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp @@ -31,7 +31,7 @@ using std::vector; using hrc = std::chrono::high_resolution_clock; using nlohmann::json; -const string base_address { "http://localhost:6070/sync/" }; +const string base_address { "http://proxysql:6070/sync/" }; const vector honest_requests { { { "valid_output_script", "%s.py", "POST", 1000 }, { "{}" } }, @@ -126,11 +126,23 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } + diag("=== Regression Test #3223: RESTAPI Script Execution & Return Codes ==="); + diag("This test ensures that ProxySQL RESTAPI correctly handles script execution"); + diag("and returns the expected HTTP status codes and script exit codes."); + diag("The test strategy is:"); + diag("1. Register multiple valid and faulty scripts in RESTAPI routes."); + diag("2. Issue POST/GET requests to these endpoints."); + diag("3. Verify HTTP return codes (200 for success, 400/424 for failures)."); + diag("4. Verify internal script error codes (exit codes, timeouts, signals)."); + diag("5. Check behavior with large outputs and partial flushes."); + diag("========================================================================="); + plan(count_exp_tests(honest_requests, invalid_requests)); MYSQL* admin = mysql_init(NULL); // Initialize connections + diag("Connecting to ProxySQL Admin at %s:%d as %s", cl.host, cl.admin_port, cl.admin_username); if (!admin) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); return EXIT_FAILURE; @@ -142,12 +154,14 @@ int main(int argc, char** argv) { } // Enable 'RESTAPI' + diag("Enabling RESTAPI on port 6070"); MYSQL_QUERY(admin, "SET admin-restapi_enabled='true'"); MYSQL_QUERY(admin, "SET admin-restapi_port=6070"); MYSQL_QUERY(admin, "LOAD ADMIN VARIABLES TO RUNTIME"); // Clean current 'restapi_routes' if any + diag("Configuring RESTAPI routes..."); MYSQL_QUERY(admin, "DELETE FROM restapi_routes"); // Configure restapi_routes to be used @@ -161,18 +175,20 @@ int main(int argc, char** argv) { honest_requests.begin(), honest_requests.end(), std::back_inserter(v_epts_info), ext_v_epts_info ); + diag("Initializing endpoints for valid requests..."); int ept_conf_res = configure_endpoints(admin, script_base_path, v_epts_info, dummy_ept, true); if (ept_conf_res) { diag("Endpoint configuration failed. Skipping endpoint testing..."); return EXIT_FAILURE; } + diag("Testing valid RESTAPI requests..."); { for (const auto& req : honest_requests) { for (const string& params : req.params) { const string ept { join_path(base_address, req.ept_info.name) }; diag( - "Checking valid '%s' request - ept: '%s', params: '%s'", + " Checking valid '%s' request - ept: '%s', params: '%s'", req.ept_info.method.c_str(), ept.c_str(), params.c_str() ); std::chrono::nanoseconds duration; @@ -294,12 +310,14 @@ int main(int argc, char** argv) { invalid_requests.begin(), invalid_requests.end(), std::back_inserter(i_epts_info), ext_i_epts_info ); + diag("Initializing endpoints for invalid requests..."); ept_conf_res = configure_endpoints(admin, script_base_path, i_epts_info, dummy_ept, true); if (ept_conf_res) { diag("Endpoint configuration failed. Skipping endpoint testing..."); return EXIT_FAILURE; } + diag("Testing invalid RESTAPI requests..."); for (const auto& req : invalid_requests) { for (const ept_pl_t& ept_pl : req.ept_pls) { std::chrono::nanoseconds duration; @@ -308,7 +326,7 @@ int main(int argc, char** argv) { const string ept { join_path(base_address, req.ept_info.name) }; diag( - "Checking valid '%s' request - ept: '%s', params: '%s'", + " Checking invalid '%s' request - ept: '%s', params: '%s'", req.ept_info.method.c_str(), ept.c_str(), ept_pl.params.c_str() ); diff --git a/test/tap/tests/reg_test_3591-restapi_num_fds-t.cpp b/test/tap/tests/reg_test_3591-restapi_num_fds-t.cpp index 13bb29142..d43704bd3 100644 --- a/test/tap/tests/reg_test_3591-restapi_num_fds-t.cpp +++ b/test/tap/tests/reg_test_3591-restapi_num_fds-t.cpp @@ -38,6 +38,17 @@ int main(int argc, char** argv) { diag("Failed to get the required environmental variables."); return -1; } + + diag("=== Regression Test #3591: RESTAPI with High Number of FDs ==="); + diag("This test verifies that the ProxySQL metrics endpoint remains functional"); + diag("even when a large number of client connections (2047) are opened."); + diag("The test strategy is:"); + diag("1. Elevate process FD limits."); + diag("2. Enable RESTAPI on port 6070."); + diag("3. Open 2047 concurrent MySQL connections to ProxySQL."); + diag("4. Verify that the /metrics endpoint is still reachable."); + diag("=============================================================="); + struct rlimit limits { 0, 0 }; getrlimit(RLIMIT_NOFILE, &limits); diag("Old process limits: { %ld, %ld }", limits.rlim_cur, limits.rlim_max); @@ -48,6 +59,7 @@ int main(int argc, char** argv) { MYSQL* admin = mysql_init(NULL); // Initialize connections + diag("Connecting to ProxySQL Admin at %s:%d as %s", cl.host, cl.admin_port, cl.admin_username); if (!admin) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); return -1; @@ -59,12 +71,14 @@ int main(int argc, char** argv) { } // Enable 'RESTAPI' + diag("Enabling RESTAPI on port 6070"); MYSQL_QUERY(admin, "SET admin-restapi_enabled='true'"); MYSQL_QUERY(admin, "SET admin-restapi_port=6070"); MYSQL_QUERY(admin, "LOAD ADMIN VARIABLES TO RUNTIME"); std::vector mysql_connections {}; + diag("Establishing %d connections to ProxySQL...", NUM_CONNECTIONS); for (int i = 0; i < NUM_CONNECTIONS; i++) { MYSQL* proxy = mysql_init(NULL); if ( @@ -79,11 +93,15 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } mysql_connections.push_back(proxy); + if ((i + 1) % 500 == 0) diag(" Established %d/%d connections...", i + 1, NUM_CONNECTIONS); } + diag("Connections established."); - int endpoint_timeout = wait_get_enpoint_ready("http://localhost:6070/metrics/", 1000, 100); + diag("Verifying RESTAPI /metrics endpoint via proxysql:6070..."); + int endpoint_timeout = wait_get_enpoint_ready("http://proxysql:6070/metrics/", 1000, 100); ok(endpoint_timeout == 0, "The endpoint should be available instead of timing out."); + diag("Closing connections and cleaning up..."); for (int i = 0; i < NUM_CONNECTIONS; i++) { mysql_close(mysql_connections[i]); } diff --git a/test/tap/tests/reg_test_4001-restapi_scripts_num_fds-t.cpp b/test/tap/tests/reg_test_4001-restapi_scripts_num_fds-t.cpp index b035d8c40..4a4a79791 100644 --- a/test/tap/tests/reg_test_4001-restapi_scripts_num_fds-t.cpp +++ b/test/tap/tests/reg_test_4001-restapi_scripts_num_fds-t.cpp @@ -35,7 +35,7 @@ using std::string; using std::vector; const int NUM_CONNECTIONS = 1300; -const string base_address { "http://localhost:6070/sync/" }; +const string base_address { "http://proxysql:6070/sync/" }; const vector honest_requests { { { "valid_output_script", "%s.py", "POST", 1000 }, { "{}" } }, @@ -56,6 +56,17 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } + diag("=== Regression Test #4001: RESTAPI with High FD Usage ==="); + diag("This test verifies that the RESTAPI remains operational when ProxySQL"); + diag("is handling a large number of concurrent file descriptors (> FD_SETSIZE)."); + diag("The test strategy is:"); + diag("1. Elevate process FD limits."); + diag("2. Establish 1300 concurrent MySQL connections to ProxySQL."); + diag("3. While these connections are held, perform continuous RESTAPI requests."); + diag("4. Simultaneously create and destroy additional MySQL connections."); + diag("5. Verify that RESTAPI requests and connection operations remain stable."); + diag("=========================================================="); + diag("Setting new process limits beyond 'FD_SETSIZE'"); struct rlimit limits { 0, 0 }; getrlimit(RLIMIT_NOFILE, &limits); @@ -67,6 +78,7 @@ int main(int argc, char** argv) { MYSQL* admin = mysql_init(NULL); // Initialize connections + diag("Connecting to ProxySQL Admin at %s:%d as %s", cl.host, cl.admin_port, cl.admin_username); if (!admin) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); return EXIT_FAILURE; @@ -77,7 +89,7 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - string script_base_path { string { cl.workdir } + "reg_test_3223_scripts" }; + const char* d_env = getenv("REGULAR_INFRA_DATADIR"); string script_base_path = (d_env ? string(d_env) + "/reg_test_3223_scripts" : string(cl.workdir) + "reg_test_3223_scripts"); const ept_info_t dummy_ept { "dummy_ept_script", "%s.py", "POST", 1000 }; vector v_epts_info {}; @@ -86,6 +98,7 @@ int main(int argc, char** argv) { honest_requests.begin(), honest_requests.end(), std::back_inserter(v_epts_info), ext_v_epts_info ); + diag("Configuring RESTAPI endpoints..."); int ept_conf_res = configure_endpoints(admin, script_base_path, v_epts_info, dummy_ept, true); if (ept_conf_res) { diag("Endpoint configuration failed. Skipping endpoint testing..."); @@ -94,6 +107,7 @@ int main(int argc, char** argv) { std::vector mysql_connections {}; + diag("Establishing %d baseline connections to ProxySQL...", NUM_CONNECTIONS); for (int i = 0; i < NUM_CONNECTIONS; i++) { MYSQL* proxy = mysql_init(NULL); if (!mysql_real_connect(proxy, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { @@ -101,7 +115,9 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } mysql_connections.push_back(proxy); + if ((i + 1) % 200 == 0) diag(" Established %d/%d connections...", i + 1, NUM_CONNECTIONS); } + diag("Baseline connections established."); typedef std::chrono::high_resolution_clock hrc; const uint64_t test_duration = 10000; @@ -111,6 +127,7 @@ int main(int argc, char** argv) { uint64_t conn_count = 0; uint64_t mysql_fails = 0; + diag("Starting simultaneous RESTAPI requests and connection churn (Duration: %ldms)...", test_duration); { std::thread create_conns([&]() -> int { std::chrono::nanoseconds duration; @@ -120,7 +137,7 @@ int main(int argc, char** argv) { start = hrc::now(); while (true) { - if (mysql_fails > (conn_count * FAILURE_RATE) / 100) { + if (conn_count > 0 && mysql_fails > (conn_count * FAILURE_RATE) / 100) { diag("Too many mysql failures in connection creation, considering test as failed..."); conn_creation_res = EXIT_FAILURE; return EXIT_FAILURE; @@ -129,17 +146,17 @@ int main(int argc, char** argv) { MYSQL* proxy = mysql_init(NULL); if (!mysql_real_connect(proxy, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { - fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy)); - mysql_fails += 1; - } - - int rc = mysql_query(proxy, "DO 1"); - if (rc) { - diag("mysql_errno: '%d', mysql_error: '%s'", mysql_errno(proxy), mysql_error(proxy)); + diag("Churn connection failed: %s", mysql_error(proxy)); mysql_fails += 1; + } else { + int rc = mysql_query(proxy, "DO 1"); + if (rc) { + diag("Churn query failed: mysql_errno: '%d', mysql_error: '%s'", mysql_errno(proxy), mysql_error(proxy)); + mysql_fails += 1; + } + mysql_close(proxy); } - mysql_close(proxy); end = hrc::now(); duration = end - start; @@ -160,6 +177,7 @@ int main(int argc, char** argv) { start = hrc::now(); + int api_request_count = 0; while (true) { int rc = 0; @@ -191,10 +209,13 @@ int main(int argc, char** argv) { } if (curl_err == 7) { - diag("Operation over endpoint failed with 'CURLE_COULDNT_CONNECT'. Aborting..."); + diag("Operation over endpoint %s failed with 'CURLE_COULDNT_CONNECT'. Aborting...", ept.c_str()); + rc = 1; break; } + api_request_count++; } + if (rc) break; } end = hrc::now(); @@ -205,7 +226,9 @@ int main(int argc, char** argv) { } } + diag("Joining churn thread and cleaning up..."); create_conns.join(); + diag("Churn thread finished. Total API requests: %d, Total churn conns: %ld, Failures: %ld", api_request_count, conn_count, mysql_fails); ok( conn_creation_res == EXIT_SUCCESS,