From 7b3757a273f82228a88bcdac65fb09720aea4159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Mon, 2 Apr 2018 16:15:07 +0200 Subject: [PATCH] Remember if the current connection has an unknown transaction status The fix for bug #1038 was to not return a connection to the connection pool if it has an error. Although this is correct, it also has the side effect that connections coming from connection pool and failing during the first query because the connection was already broken, would be considered as possibly to run a transaction. That is incorrect. Now at connection level it is tracked if the transaction status is known or not. --- include/mysql_connection.h | 2 ++ lib/MySQL_Session.cpp | 5 ++++ lib/mysql_connection.cpp | 55 +++++++++++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/include/mysql_connection.h b/include/mysql_connection.h index 4f7573cd3..a0bb1f5e3 100644 --- a/include/mysql_connection.h +++ b/include/mysql_connection.h @@ -92,6 +92,8 @@ class MySQL_Connection { bool processing_prepared_statement_execute; bool processing_multi_statement; bool multiplex_delayed; + bool unknown_transaction_status; + void compute_unknown_transaction_status(); MySQL_Connection(); ~MySQL_Connection(); bool set_autocommit(bool); diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index c5862fb95..14d8b3467 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -942,6 +942,7 @@ int MySQL_Session::handler_again___status_PINGING_SERVER() { int rc=myconn->async_ping(myds->revents); if (rc==0) { myconn->async_state_machine=ASYNC_IDLE; + myconn->unknown_transaction_status = false; if (mysql_thread___multiplexing && (myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { myds->return_MySQL_Connection_To_Pool(); } else { @@ -2730,6 +2731,7 @@ handler_again: enum session_status st=status; size_t sts=previous_status.size(); if (sts) { + myconn->unknown_transaction_status = false; myconn->async_state_machine=ASYNC_IDLE; myds->DSS=STATE_MARIADB_GENERIC; st=previous_status.top(); @@ -2769,6 +2771,7 @@ handler_again: if (mysql_thread___multiplexing && (myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { if (mysql_thread___connection_delay_multiplex_ms && mirror==false) { myds->wait_until=thread->curtime+mysql_thread___connection_delay_multiplex_ms*1000; + myconn->unknown_transaction_status = false; myconn->async_state_machine=ASYNC_IDLE; myconn->multiplex_delayed=true; myds->DSS=STATE_MARIADB_GENERIC; @@ -2796,6 +2799,7 @@ handler_again: } } else { myconn->multiplex_delayed=false; + myconn->unknown_transaction_status = false; myconn->async_state_machine=ASYNC_IDLE; myds->DSS=STATE_MARIADB_GENERIC; if (transaction_persistent==true) { @@ -2813,6 +2817,7 @@ handler_again: } else { if (rc==-1) { int myerr=mysql_errno(myconn->mysql); + myconn->compute_unknown_transaction_status(); char *errmsg = NULL; if (myerr == 0) { if (CurrentQuery.mysql_stmt) { diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index e566f10aa..d88b517cc 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -49,6 +49,29 @@ MySQL_Connection_userinfo::~MySQL_Connection_userinfo() { if (schemaname) free(schemaname); } +void MySQL_Connection::compute_unknown_transaction_status() { + if (mysql) { + int _myerrno=mysql_errno(mysql); + if (_myerrno == 0) { + unknown_transaction_status = false; // no error + return; + } + if (_myerrno >= 2000 && _myerrno < 3000) { // client error + // do not change it + return; + } + if (_myerrno >= 1000 && _myerrno < 2000) { // server error + unknown_transaction_status = true; + return; + } + if (_myerrno >= 3000 && _myerrno < 4000) { // server error + unknown_transaction_status = true; + return; + } + // all other cases, server error + } +} + uint64_t MySQL_Connection_userinfo::compute_hash() { int l=0; if (username) @@ -189,6 +212,7 @@ MySQL_Connection::MySQL_Connection() { largest_query_length=0; multiplex_delayed=false; MyRS=NULL; + unknown_transaction_status = false; creation_time=0; processing_multi_statement=false; proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "Creating new MySQL_Connection %p\n", this); @@ -981,6 +1005,14 @@ handler_again: } break; case ASYNC_QUERY_END: + if (mysql) { + int _myerrno=mysql_errno(mysql); + if (_myerrno == 0) { + unknown_transaction_status = false; + } else { + compute_unknown_transaction_status(); + } + } if (mysql_result) { mysql_free_result(mysql_result); mysql_result=NULL; @@ -1137,6 +1169,7 @@ int MySQL_Connection::async_connect(short event) { return 0; } if (async_state_machine==ASYNC_CONNECT_SUCCESSFUL) { + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; myds->wait_until=0; creation_time = monotonic_time(); @@ -1145,6 +1178,7 @@ int MySQL_Connection::async_connect(short event) { handler(event); switch (async_state_machine) { case ASYNC_CONNECT_SUCCESSFUL: + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; myds->wait_until=0; return 0; @@ -1224,8 +1258,10 @@ int MySQL_Connection::async_query(short event, char *stmt, unsigned long length, if (async_state_machine==ASYNC_QUERY_END) { if (mysql_errno(mysql)) { + compute_unknown_transaction_status(); return -1; } else { + unknown_transaction_status = false; return 0; } } @@ -1233,17 +1269,21 @@ int MySQL_Connection::async_query(short event, char *stmt, unsigned long length, query.stmt_meta=NULL; async_state_machine=ASYNC_QUERY_END; if (mysql_stmt_errno(query.stmt)) { + compute_unknown_transaction_status(); return -1; } else { + unknown_transaction_status = false; return 0; } } if (async_state_machine==ASYNC_STMT_PREPARE_SUCCESSFUL || async_state_machine==ASYNC_STMT_PREPARE_FAILED) { query.stmt_meta=NULL; if (async_state_machine==ASYNC_STMT_PREPARE_FAILED) { + compute_unknown_transaction_status(); return -1; } else { *_stmt=query.stmt; + unknown_transaction_status = false; return 0; } } @@ -1273,6 +1313,7 @@ int MySQL_Connection::async_ping(short event) { assert(ret_mysql); switch (async_state_machine) { case ASYNC_PING_SUCCESSFUL: + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; return 0; break; @@ -1292,6 +1333,7 @@ int MySQL_Connection::async_ping(short event) { // check again switch (async_state_machine) { case ASYNC_PING_SUCCESSFUL: + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; return 0; break; @@ -1314,6 +1356,7 @@ int MySQL_Connection::async_change_user(short event) { assert(ret_mysql); switch (async_state_machine) { case ASYNC_CHANGE_USER_SUCCESSFUL: + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; return 0; break; @@ -1333,6 +1376,7 @@ int MySQL_Connection::async_change_user(short event) { // check again switch (async_state_machine) { case ASYNC_CHANGE_USER_SUCCESSFUL: + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; return 0; break; @@ -1355,6 +1399,7 @@ int MySQL_Connection::async_select_db(short event) { assert(ret_mysql); switch (async_state_machine) { case ASYNC_INITDB_SUCCESSFUL: + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; return 0; break; @@ -1371,6 +1416,7 @@ int MySQL_Connection::async_select_db(short event) { // check again switch (async_state_machine) { case ASYNC_INITDB_SUCCESSFUL: + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; return 0; break; @@ -1390,6 +1436,7 @@ int MySQL_Connection::async_set_autocommit(short event, bool ac) { assert(ret_mysql); switch (async_state_machine) { case ASYNC_SET_AUTOCOMMIT_SUCCESSFUL: + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; return 0; break; @@ -1407,6 +1454,7 @@ int MySQL_Connection::async_set_autocommit(short event, bool ac) { // check again switch (async_state_machine) { case ASYNC_SET_AUTOCOMMIT_SUCCESSFUL: + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; return 0; break; @@ -1426,6 +1474,7 @@ int MySQL_Connection::async_set_names(short event, uint8_t c) { assert(ret_mysql); switch (async_state_machine) { case ASYNC_SET_NAMES_SUCCESSFUL: + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; return 0; break; @@ -1443,6 +1492,7 @@ int MySQL_Connection::async_set_names(short event, uint8_t c) { // check again switch (async_state_machine) { case ASYNC_SET_NAMES_SUCCESSFUL: + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; return 0; break; @@ -1487,6 +1537,7 @@ void MySQL_Connection::async_free_result() { mysql_result=NULL; } } + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; if (MyRS) { delete MyRS; @@ -1499,7 +1550,7 @@ bool MySQL_Connection::IsActiveTransaction() { bool ret=false; if (mysql) { ret = (mysql->server_status & SERVER_STATUS_IN_TRANS); - if (ret == false && (mysql)->net.last_errno) { + if (ret == false && (mysql)->net.last_errno && unknown_transaction_status == true) { ret = true; } if (ret == false) { @@ -1685,8 +1736,10 @@ int MySQL_Connection::async_send_simple_command(short event, char *stmt, unsigne } if (async_state_machine==ASYNC_QUERY_END) { if (mysql_errno(mysql)) { + compute_unknown_transaction_status(); return -1; } else { + unknown_transaction_status = false; async_state_machine=ASYNC_IDLE; return 0; }