From 5c227a3bc93de6bc583493b2a1f91bbc230781bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 22 Mar 2026 11:51:11 +0100 Subject: [PATCH] Extract MySQL error classification logic (Phase 3.7, #5495) New: include/MySQLErrorClassifier.h, lib/MySQLErrorClassifier.cpp Functions: - classify_mysql_error(): classifies error codes as retryable (1047 WSREP, 1053 shutdown) or fatal, checking retry conditions - can_retry_on_new_connection(): checks if offline server retry is possible given connection state (reusable, no txn, no transfer) --- include/MySQLErrorClassifier.h | 76 ++++++++++++++++++++++++++++++++++ lib/Makefile | 1 + lib/MySQLErrorClassifier.cpp | 66 +++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 include/MySQLErrorClassifier.h create mode 100644 lib/MySQLErrorClassifier.cpp diff --git a/include/MySQLErrorClassifier.h b/include/MySQLErrorClassifier.h new file mode 100644 index 000000000..9b46549db --- /dev/null +++ b/include/MySQLErrorClassifier.h @@ -0,0 +1,76 @@ +/** + * @file MySQLErrorClassifier.h + * @brief Pure MySQL error classification for retry decisions. + * + * Extracted from MySQL_Session handler_ProcessingQueryError_CheckBackendConnectionStatus() + * and handler_minus1_HandleErrorCodes(). + * + * @see Phase 3.7 (GitHub issue #5495) + */ + +#ifndef MYSQL_ERROR_CLASSIFIER_H +#define MYSQL_ERROR_CLASSIFIER_H + +/** + * @brief Action to take after a MySQL backend query error. + */ +enum MySQLErrorAction { + MYSQL_ERROR_CONTINUE, ///< Error handled, continue processing. + MYSQL_ERROR_RETRY_ON_NEW_CONN, ///< Reconnect and retry on a new server. + MYSQL_ERROR_REPORT_TO_CLIENT ///< Send error to client, no retry. +}; + +/** + * @brief Classify a MySQL error code to determine retry eligibility. + * + * Mirrors the logic in handler_minus1_HandleErrorCodes(): + * - Error 1047 (WSREP not ready): retryable if conditions permit + * - Error 1053 (server shutdown): retryable if conditions permit + * - Other errors: report to client + * + * Retry is only possible when: + * - query_retries_on_failure > 0 + * - connection is reusable + * - no active transaction + * - multiplex not disabled + * + * @param error_code MySQL error number. + * @param retries_remaining Number of retries left. + * @param connection_reusable Whether the connection can be reused. + * @param in_active_transaction Whether a transaction is in progress. + * @param multiplex_disabled Whether multiplexing is disabled. + * @return MySQLErrorAction indicating what to do. + */ +MySQLErrorAction classify_mysql_error( + unsigned int error_code, + int retries_remaining, + bool connection_reusable, + bool in_active_transaction, + bool multiplex_disabled +); + +/** + * @brief Check if a backend query can be retried on a new connection. + * + * Mirrors handler_ProcessingQueryError_CheckBackendConnectionStatus(). + * A retry is possible when the server is offline AND all retry + * conditions are met. + * + * @param server_offline Whether the backend server is offline. + * @param retries_remaining Number of retries left. + * @param connection_reusable Whether the connection can be reused. + * @param in_active_transaction Whether a transaction is in progress. + * @param multiplex_disabled Whether multiplexing is disabled. + * @param transfer_started Whether result transfer has already begun. + * @return true if the query should be retried on a new connection. + */ +bool can_retry_on_new_connection( + bool server_offline, + int retries_remaining, + bool connection_reusable, + bool in_active_transaction, + bool multiplex_disabled, + bool transfer_started +); + +#endif // MYSQL_ERROR_CLASSIFIER_H diff --git a/lib/Makefile b/lib/Makefile index 7af5cd6ef..4f7963fbf 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -108,6 +108,7 @@ _OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo MonitorHealthDecision.oo \ TransactionState.oo \ HostgroupRouting.oo \ + MySQLErrorClassifier.oo \ proxy_sqlite3_symbols.oo # TSDB object files diff --git a/lib/MySQLErrorClassifier.cpp b/lib/MySQLErrorClassifier.cpp new file mode 100644 index 000000000..69ee01027 --- /dev/null +++ b/lib/MySQLErrorClassifier.cpp @@ -0,0 +1,66 @@ +/** + * @file MySQLErrorClassifier.cpp + * @brief Implementation of MySQL error classification. + * + * @see MySQLErrorClassifier.h + * @see Phase 3.7 (GitHub issue #5495) + */ + +#include "MySQLErrorClassifier.h" + +MySQLErrorAction classify_mysql_error( + unsigned int error_code, + int retries_remaining, + bool connection_reusable, + bool in_active_transaction, + bool multiplex_disabled) +{ + // Check if this error code is retryable + bool retryable_error = false; + switch (error_code) { + case 1047: // ER_UNKNOWN_COM_ERROR (WSREP not ready) + case 1053: // ER_SERVER_SHUTDOWN + retryable_error = true; + break; + default: + break; + } + + if (!retryable_error) { + return MYSQL_ERROR_REPORT_TO_CLIENT; + } + + // Check retry conditions (mirrors handler_minus1_HandleErrorCodes) + if (retries_remaining > 0 + && connection_reusable + && !in_active_transaction + && !multiplex_disabled) { + return MYSQL_ERROR_RETRY_ON_NEW_CONN; + } + + return MYSQL_ERROR_REPORT_TO_CLIENT; +} + +bool can_retry_on_new_connection( + bool server_offline, + int retries_remaining, + bool connection_reusable, + bool in_active_transaction, + bool multiplex_disabled, + bool transfer_started) +{ + if (!server_offline) { + return false; // server is fine, no retry needed + } + + // Mirror handler_ProcessingQueryError_CheckBackendConnectionStatus + if (retries_remaining > 0 + && connection_reusable + && !in_active_transaction + && !multiplex_disabled + && !transfer_started) { + return true; + } + + return false; +}