From 622b748fdf9a893d439d6b90657a8de54ff8256d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 7 Aug 2022 15:13:02 +0000 Subject: [PATCH] Switching autocommit from 0 to 1 drops SAVEPOINTs Another edge case due to MySQL bug https://bugs.mysql.com/bug.php?id=107875 related to autocommit=0 and SAVEPOINT --- include/MySQL_Session.h | 2 +- lib/MySQL_Session.cpp | 36 ++++++++++++++---- test/tap/tests/savepoint-948-t.cpp | 59 ++++++++++++++++++------------ 3 files changed, 65 insertions(+), 32 deletions(-) diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index 9139c5576..b5fcf62f9 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -311,7 +311,7 @@ class MySQL_Session unsigned int NumActiveTransactions(bool check_savpoint=false); bool HasOfflineBackends(); bool SetEventInOfflineBackends(); - int FindOneActiveTransaction(); + int FindOneActiveTransaction(bool check_savepoint=false); unsigned long long IdleTime(); void reset_all_backends(); diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index ca553f2a2..eaadfa91e 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -830,7 +830,7 @@ bool MySQL_Session::handler_CommitRollback(PtrSize_t *pkt) { if (ret==false) { return false; // quick exit } - // this is the only part of the code (as at release 2.4.3) where we call + // in this part of the code (as at release 2.4.3) where we call // NumActiveTransactions() with the check_savepoint flag . // This to try to handle MySQL bug https://bugs.mysql.com/bug.php?id=107875 unsigned int nTrx=NumActiveTransactions(true); @@ -953,12 +953,16 @@ bool MySQL_Session::handler_SetAutocommit(PtrSize_t *pkt) { goto __ret_autocommit_OK; } if (fd==1 && autocommit==false) { - if (nTrx) { + // in this part of the code (as at release 2.4.3) where we call + // NumActiveTransactions() with the check_savepoint flag . + // This to try to handle MySQL bug https://bugs.mysql.com/bug.php?id=107875 + unsigned int nTrx_SP=NumActiveTransactions(true); // check savepoint + if (nTrx_SP) { // previously we were checking nTrx . Now nTrx_SP // there is an active transaction, we need to forward it // because this can potentially close the transaction autocommit=true; client_myds->myconn->set_autocommit(autocommit); - autocommit_on_hostgroup=FindOneActiveTransaction(); + autocommit_on_hostgroup=FindOneActiveTransaction(true); free(_new_pkt.ptr); sending_set_autocommit=true; return false; @@ -1897,6 +1901,12 @@ bool MySQL_Session::handler_again___verify_backend_autocommit() { MySQL_Connection *mc = mybe->server_myds->myconn; mc->set_autocommit(autocommit); mc->options.last_set_autocommit = ( mc->options.autocommit ? 1 : 0 ); + if (autocommit==1) { + // due to MySQL bug https://bugs.mysql.com/bug.php?id=107875 related to + // SAVEPOINT and autocommit=0 , if we send autocommit=1 we need to drop + // the flag related to savepoint + mc->set_status(false, STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT); + } return false; } proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session %p , client: %d , backend: %d\n", this, client_myds->myconn->options.autocommit, mybe->server_myds->myconn->options.autocommit); @@ -7035,17 +7045,29 @@ bool MySQL_Session::SetEventInOfflineBackends() { return ret; } -int MySQL_Session::FindOneActiveTransaction() { +int MySQL_Session::FindOneActiveTransaction(bool check_savepoint) { int ret=-1; if (mybes==0) return ret; MySQL_Backend *_mybe; unsigned int i; for (i=0; i < mybes->len; i++) { _mybe=(MySQL_Backend *)mybes->index(i); - if (_mybe->server_myds) - if (_mybe->server_myds->myconn) - if (_mybe->server_myds->myconn->IsActiveTransaction()) + if (_mybe->server_myds) { + if (_mybe->server_myds->myconn) { + if (_mybe->server_myds->myconn->IsActiveTransaction()) { return (int)_mybe->server_myds->myconn->parent->myhgc->hid; + } else { + // we use check_savepoint to check if we shouldn't ignore COMMIT or ROLLBACK due + // to MySQL bug https://bugs.mysql.com/bug.php?id=107875 related to + // SAVEPOINT and autocommit=0 + if (check_savepoint) { + if (_mybe->server_myds->myconn->AutocommitFalse_AndSavepoint() == true) { + return (int)_mybe->server_myds->myconn->parent->myhgc->hid; + } + } + } + } + } } return ret; } diff --git a/test/tap/tests/savepoint-948-t.cpp b/test/tap/tests/savepoint-948-t.cpp index 4b33a28c5..3ec1a2168 100644 --- a/test/tap/tests/savepoint-948-t.cpp +++ b/test/tap/tests/savepoint-948-t.cpp @@ -19,37 +19,19 @@ #include "utils.h" #include "command_line.h" +#include "json.hpp" +using nlohmann::json; -unsigned long long monotonic_time() { - struct timespec ts; - //clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); // this is faster, but not precise - clock_gettime(CLOCK_MONOTONIC, &ts); - return (((unsigned long long) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000); -} - -struct cpu_timer -{ - cpu_timer() { - begin = monotonic_time(); - } - ~cpu_timer() - { - unsigned long long end = monotonic_time(); - std::cerr << double( end - begin ) / 1000000 << " secs.\n" ; - begin=end-begin; - }; - unsigned long long begin; -}; bool debug_diag=true; -/* unsigned int num_threads=4; int count=10; int transactions=200; -*/ +/* unsigned int num_threads=1; int count=1; -int transactions=200; +int transactions=5; +*/ char *username=NULL; char *password=NULL; char *host=(char *)"localhost"; @@ -97,6 +79,7 @@ void * my_conn_thread(void *arg) { unsigned int select_ERR=0; int i, j; MYSQL **mysqlconns=(MYSQL **)malloc(sizeof(MYSQL *)*count); + bool ac[count]; if (mysqlconns==NULL) { exit(EXIT_FAILURE); @@ -118,6 +101,7 @@ void * my_conn_thread(void *arg) { } mysqlconns[i]=mysql; __sync_add_and_fetch(&status_connections,1); + ac[i]=true; } __sync_fetch_and_add(&connect_phase_completed,1); @@ -135,6 +119,11 @@ void * my_conn_thread(void *arg) { int sleepDelay; for (int i=0; i= cnt_transactions+cnt_SELECT_outside_transactions-10) , "Number of transactions [%d] , Queries outside transaction [%d] , total connections returned [%d]", cnt_transactions.load(std::memory_order_relaxed), cnt_SELECT_outside_transactions.load(std::memory_order_relaxed), MyHGM_myconnpoll_get); ok((MyHGM_myconnpoll_get == cnt_transactions+cnt_SELECT_outside_transactions) , "Number of transactions [%d] , Queries outside transaction [%d] , total connections returned [%d]", cnt_transactions.load(std::memory_order_relaxed), cnt_SELECT_outside_transactions.load(std::memory_order_relaxed), MyHGM_myconnpoll_get); + + + MYSQL_QUERY(mysqladmin, "DELETE FROM mysql_query_rules"); MYSQL_QUERY(mysqladmin, "INSERT INTO mysql_query_rules SELECT * FROM mysql_query_rules_948"); MYSQL_QUERY(mysqladmin, "LOAD MYSQL QUERY RULES TO RUNTIME");