From 81cffda2e68ea5a155b74f24ae4345388afa963c Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Fri, 14 Feb 2020 16:38:49 +0100 Subject: perfschema transaction instrumentation related changes --- sql/handler.cc | 90 +++++++++++++++++++++++++++++++++-- sql/log.cc | 14 +++--- sql/sql_class.cc | 13 +---- sql/sql_class.h | 19 ++++++++ sql/transaction.cc | 39 ++++++++++++++- sql/xa.cc | 26 +++++++++- storage/federatedx/ha_federatedx.cc | 8 ++-- storage/innobase/handler/ha_innodb.cc | 8 +--- storage/rocksdb/ha_rocksdb.cc | 4 +- storage/spider/spd_trx.cc | 8 ++-- 10 files changed, 188 insertions(+), 41 deletions(-) diff --git a/sql/handler.cc b/sql/handler.cc index 35efde7a78c..60089d348f2 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -40,6 +40,8 @@ #include "myisam.h" #include "probes_mysql.h" #include +#include +#include #include "debug_sync.h" // DEBUG_SYNC #include "sql_audit.h" #include "ha_sequence.h" @@ -62,6 +64,39 @@ #include "wsrep_trans_observer.h" /* wsrep transaction hooks */ #endif /* WITH_WSREP */ +/** + @def MYSQL_TABLE_LOCK_WAIT + Instrumentation helper for table io_waits. + @param OP the table operation to be performed + @param FLAGS per table operation flags. + @param PAYLOAD the code to instrument. + @sa MYSQL_END_TABLE_WAIT. +*/ +#ifdef HAVE_PSI_TABLE_INTERFACE + #define MYSQL_TABLE_LOCK_WAIT(OP, FLAGS, PAYLOAD) \ + { \ + if (m_psi != NULL) \ + { \ + PSI_table_locker *locker; \ + PSI_table_locker_state state; \ + locker= PSI_TABLE_CALL(start_table_lock_wait) \ + (& state, m_psi, OP, FLAGS, \ + __FILE__, __LINE__); \ + PAYLOAD \ + if (locker != NULL) \ + PSI_TABLE_CALL(end_table_lock_wait)(locker); \ + } \ + else \ + { \ + PAYLOAD \ + } \ + } +#else + #define MYSQL_TABLE_LOCK_WAIT(OP, FLAGS, PAYLOAD) \ + PAYLOAD +#endif + + /* While we have legacy_db_type, we have this array to check for dups and to find handlerton from legacy_db_type. @@ -1201,7 +1236,8 @@ void ha_pre_shutdown() times per transaction. */ -void trans_register_ha(THD *thd, bool all, handlerton *ht_arg) +void trans_register_ha(THD *thd, bool all, handlerton *ht_arg, + const ulonglong *trxid) { THD_TRANS *trans; Ha_trx_info *ha_info; @@ -1232,6 +1268,25 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg) if (thd->transaction.implicit_xid.is_null()) thd->transaction.implicit_xid.set(thd->query_id); +/* + Register transaction start in performance schema if not done already. + By doing this, we handle cases when the transaction is started implicitly in + autocommit=0 mode, and cases when we are in normal autocommit=1 mode and the + executed statement is a single-statement transaction. + + Explicitly started transactions are handled in trans_begin(). + + Do not register transactions in which binary log is the only participating + transactional storage engine. +*/ + if (thd->m_transaction_psi == NULL && ht_arg->db_type != DB_TYPE_BINLOG) + { + thd->m_transaction_psi= MYSQL_START_TRANSACTION(&thd->m_transaction_state, + thd->get_xid(), trxid, thd->tx_isolation, thd->tx_read_only, + !thd->in_multi_stmt_transaction_mode()); + DEBUG_SYNC(thd, "after_set_transaction_psi_before_set_transaction_gtid"); + //gtid_set_performance_schema_values(thd); + } DBUG_VOID_RETURN; } @@ -1457,7 +1512,11 @@ int ha_commit_trans(THD *thd, bool all) Free resources and perform other cleanup even for 'empty' transactions. */ if (is_real_trans) + { thd->transaction.cleanup(); + MYSQL_COMMIT_TRANSACTION(thd->m_transaction_psi); + thd->m_transaction_psi= NULL; + } #ifdef WITH_WSREP if (wsrep_is_active(thd) && is_real_trans && !error) wsrep_commit_empty(thd, all); @@ -1651,12 +1710,15 @@ int ha_commit_trans(THD *thd, bool all) #endif /* WITH_WSREP */ DBUG_EXECUTE_IF("crash_commit_before_unlog", DBUG_SUICIDE();); if (tc_log->unlog(cookie, xid)) - { error= 2; /* Error during commit */ - goto end; - } done: + if (is_real_trans) + { + MYSQL_COMMIT_TRANSACTION(thd->m_transaction_psi); + thd->m_transaction_psi= NULL; + } + DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE();); mysql_mutex_assert_not_owner(&LOCK_prepare_ordered); @@ -1694,6 +1756,8 @@ err: ha_rollback_trans(thd, all); else { + MYSQL_ROLLBACK_TRANSACTION(thd->m_transaction_psi); + thd->m_transaction_psi= NULL; WSREP_DEBUG("rollback skipped %p %d",thd->rgi_slave, thd->rgi_slave->is_parallel_exec); } @@ -1913,6 +1977,13 @@ int ha_rollback_trans(THD *thd, bool all) } (void) wsrep_after_rollback(thd, all); #endif /* WITH_WSREP */ + + if (all || !thd->in_active_multi_stmt_transaction()) + { + MYSQL_ROLLBACK_TRANSACTION(thd->m_transaction_psi); + thd->m_transaction_psi= NULL; + } + /* Always cleanup. Even if nht==0. There may be savepoints. */ if (is_real_trans) { @@ -2360,6 +2431,10 @@ int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv) ha_info->reset(); /* keep it conveniently zero-filled */ } trans->ha_list= sv->ha_list; + + if (thd->m_transaction_psi != NULL) + MYSQL_INC_TRANSACTION_ROLLBACK_TO_SAVEPOINT(thd->m_transaction_psi, 1); + DBUG_RETURN(error); } @@ -2411,6 +2486,9 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv) */ sv->ha_list= trans->ha_list; + if (!error && thd->m_transaction_psi != NULL) + MYSQL_INC_TRANSACTION_SAVEPOINTS(thd->m_transaction_psi, 1); + DBUG_RETURN(error); } @@ -2435,6 +2513,10 @@ int ha_release_savepoint(THD *thd, SAVEPOINT *sv) error=1; } } + + if (thd->m_transaction_psi != NULL) + MYSQL_INC_TRANSACTION_RELEASE_SAVEPOINT(thd->m_transaction_psi, 1); + DBUG_RETURN(error); } diff --git a/sql/log.cc b/sql/log.cc index 58f85be5ce6..b0254850176 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2212,8 +2212,8 @@ void MYSQL_BIN_LOG::set_write_error(THD *thd, bool is_transactional) if (WSREP_EMULATE_BINLOG(thd)) { if (is_transactional) - trans_register_ha(thd, TRUE, binlog_hton); - trans_register_ha(thd, FALSE, binlog_hton); + trans_register_ha(thd, TRUE, binlog_hton, NULL); + trans_register_ha(thd, FALSE, binlog_hton, NULL); } #endif /* WITH_WSREP */ DBUG_VOID_RETURN; @@ -5713,8 +5713,8 @@ THD::binlog_start_trans_and_stmt() } #endif if (mstmt_mode) - trans_register_ha(this, TRUE, binlog_hton); - trans_register_ha(this, FALSE, binlog_hton); + trans_register_ha(this, TRUE, binlog_hton, NULL); + trans_register_ha(this, FALSE, binlog_hton, NULL); /* Mark statement transaction as read/write. We never start a binary log transaction and keep it read-only, @@ -5758,7 +5758,7 @@ binlog_start_consistent_snapshot(handlerton *hton, THD *thd) strmake_buf(cache_mngr->last_commit_pos_file, mysql_bin_log.last_commit_pos_file); cache_mngr->last_commit_pos_offset= mysql_bin_log.last_commit_pos_offset; - trans_register_ha(thd, TRUE, hton); + trans_register_ha(thd, TRUE, binlog_hton, NULL); DBUG_RETURN(err); } @@ -10749,8 +10749,8 @@ void wsrep_register_binlog_handler(THD *thd, bool trx) Set callbacks in order to be able to call commmit or rollback. */ if (trx) - trans_register_ha(thd, TRUE, binlog_hton); - trans_register_ha(thd, FALSE, binlog_hton); + trans_register_ha(thd, TRUE, binlog_hton, NULL); + trans_register_ha(thd, FALSE, binlog_hton, NULL); /* Set the binary log as read/write otherwise callbacks are not called. diff --git a/sql/sql_class.cc b/sql/sql_class.cc index de4eaf86133..56f2cad24ad 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -643,6 +643,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) accessed_rows_and_keys(0), m_digest(NULL), m_statement_psi(NULL), + m_transaction_psi(NULL), m_idle_psi(NULL), col_access(NO_ACL), thread_id(id), @@ -1189,19 +1190,9 @@ void *thd_memdup(MYSQL_THD thd, const void* str, size_t size) extern "C" void thd_get_xid(const MYSQL_THD thd, MYSQL_XID *xid) { -#ifdef WITH_WSREP - if (!thd->wsrep_xid.is_null()) - { - *xid = *(MYSQL_XID *) &thd->wsrep_xid; - return; - } -#endif /* WITH_WSREP */ - *xid= thd->transaction.xid_state.is_explicit_XA() ? - *(MYSQL_XID *) thd->transaction.xid_state.get_xid() : - *(MYSQL_XID *) &thd->transaction.implicit_xid; + *xid = *(MYSQL_XID *) thd->get_xid(); } - extern "C" my_time_t thd_TIME_to_gmt_sec(MYSQL_THD thd, const MYSQL_TIME *ltime, unsigned int *errcode) diff --git a/sql/sql_class.h b/sql/sql_class.h index dc6e12a5c01..0af1734df82 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3053,6 +3053,14 @@ public: /** Current statement instrumentation state. */ PSI_statement_locker_state m_statement_state; #endif /* HAVE_PSI_STATEMENT_INTERFACE */ + + /** Current transaction instrumentation. */ + PSI_transaction_locker *m_transaction_psi; +#ifdef HAVE_PSI_TRANSACTION_INTERFACE + /** Current transaction instrumentation state. */ + PSI_transaction_locker_state m_transaction_state; +#endif /* HAVE_PSI_TRANSACTION_INTERFACE */ + /** Idle instrumentation. */ PSI_idle_locker *m_idle_psi; #ifdef HAVE_PSI_IDLE_INTERFACE @@ -4819,6 +4827,17 @@ public: LF_PINS *xid_hash_pins; bool fix_xid_hash_pins(); + const XID *get_xid() const + { +#ifdef WITH_WSREP + if (!wsrep_xid.is_null()) + return &wsrep_xid; +#endif /* WITH_WSREP */ + return transaction.xid_state.is_explicit_XA() ? + transaction.xid_state.get_xid() : + &transaction.implicit_xid; + } + /* Members related to temporary tables. */ public: /* Opened table states. */ diff --git a/sql/transaction.cc b/sql/transaction.cc index 330baaf3fc7..5138b275673 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -25,6 +25,8 @@ #include "debug_sync.h" // DEBUG_SYNC #include "sql_acl.h" #include "semisync_master.h" +#include +#include #ifdef WITH_WSREP #include "wsrep_trans_observer.h" #endif /* WITH_WSREP */ @@ -209,6 +211,23 @@ bool trans_begin(THD *thd, uint flags) #endif //EMBEDDED_LIBRARY res= ha_start_consistent_snapshot(thd); } + /* + Register transaction start in performance schema if not done already. + We handle explicitly started transactions here, implicitly started + transactions (and single-statement transactions in autocommit=1 mode) + are handled in trans_register_ha(). + We can't handle explicit transactions in the same way as implicit + because we want to correctly attribute statements which follow + BEGIN but do not touch any transactional tables. + */ + if (thd->m_transaction_psi == NULL) + { + thd->m_transaction_psi= MYSQL_START_TRANSACTION(&thd->m_transaction_state, + NULL, NULL, thd->tx_isolation, + thd->tx_read_only, false); + DEBUG_SYNC(thd, "after_set_transaction_psi_before_set_transaction_gtid"); + //gtid_set_performance_schema_values(thd); + } DBUG_RETURN(MY_TEST(res)); } @@ -255,6 +274,8 @@ bool trans_commit(THD *thd) thd->transaction.all.reset(); thd->lex->start_transaction_opt= 0; + /* The transaction should be marked as complete in P_S. */ + DBUG_ASSERT(thd->m_transaction_psi == NULL); trans_track_end_trx(thd); DBUG_RETURN(MY_TEST(res)); @@ -299,6 +320,9 @@ bool trans_commit_implicit(THD *thd) thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); thd->transaction.all.reset(); + /* The transaction should be marked as complete in P_S. */ + DBUG_ASSERT(thd->m_transaction_psi == NULL); + /* Upon implicit commit, reset the current transaction isolation level and access mode. We do not care about @@ -343,6 +367,9 @@ bool trans_rollback(THD *thd) thd->transaction.all.reset(); thd->lex->start_transaction_opt= 0; + /* The transaction should be marked as complete in P_S. */ + DBUG_ASSERT(thd->m_transaction_psi == NULL); + trans_track_end_trx(thd); DBUG_RETURN(MY_TEST(res)); @@ -389,7 +416,9 @@ bool trans_rollback_implicit(THD *thd) thd->transaction.all.reset(); /* Rollback should clear transaction_rollback_request flag. */ - DBUG_ASSERT(! thd->transaction_rollback_request); + DBUG_ASSERT(!thd->transaction_rollback_request); + /* The transaction should be marked as complete in P_S. */ + DBUG_ASSERT(thd->m_transaction_psi == NULL); trans_track_end_trx(thd); @@ -457,6 +486,10 @@ bool trans_commit_stmt(THD *thd) #endif } + /* In autocommit=1 mode the transaction should be marked as complete in P_S */ + DBUG_ASSERT(thd->in_active_multi_stmt_transaction() || + thd->m_transaction_psi == NULL); + thd->transaction.stmt.reset(); DBUG_RETURN(MY_TEST(res)); @@ -496,6 +529,10 @@ bool trans_rollback_stmt(THD *thd) repl_semisync_master.wait_after_rollback(thd, FALSE); #endif + /* In autocommit=1 mode the transaction should be marked as complete in P_S */ + DBUG_ASSERT(thd->in_active_multi_stmt_transaction() || + thd->m_transaction_psi == NULL); + thd->transaction.stmt.reset(); DBUG_RETURN(FALSE); diff --git a/sql/xa.cc b/sql/xa.cc index 020af8cb784..4d9846d2f4d 100644 --- a/sql/xa.cc +++ b/sql/xa.cc @@ -19,7 +19,8 @@ #include "mariadb.h" #include "sql_class.h" #include "transaction.h" - +#include +#include /*************************************************************************** Handling of XA id caching @@ -425,7 +426,10 @@ bool trans_xa_start(THD *thd) if (not_equal) my_error(ER_XAER_NOTA, MYF(0)); else + { thd->transaction.xid_state.xid_cache_element->xa_state= XA_ACTIVE; + MYSQL_SET_TRANSACTION_XA_STATE(thd->m_transaction_psi, XA_ACTIVE); + } DBUG_RETURN(not_equal); } @@ -440,6 +444,7 @@ bool trans_xa_start(THD *thd) my_error(ER_XAER_OUTSIDE, MYF(0)); else if (!trans_begin(thd)) { + MYSQL_SET_TRANSACTION_XID(thd->m_transaction_psi, thd->lex->xid, XA_ACTIVE); if (xid_cache_insert(thd, &thd->transaction.xid_state, thd->lex->xid)) { trans_rollback(thd); @@ -474,7 +479,10 @@ bool trans_xa_end(THD *thd) else if (!thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid)) my_error(ER_XAER_NOTA, MYF(0)); else if (!xa_trans_rolled_back(thd->transaction.xid_state.xid_cache_element)) + { thd->transaction.xid_state.xid_cache_element->xa_state= XA_IDLE; + MYSQL_SET_TRANSACTION_XA_STATE(thd->m_transaction_psi, XA_IDLE); + } DBUG_RETURN(thd->is_error() || thd->transaction.xid_state.xid_cache_element->xa_state != XA_IDLE); @@ -505,7 +513,10 @@ bool trans_xa_prepare(THD *thd) my_error(ER_XA_RBROLLBACK, MYF(0)); } else + { thd->transaction.xid_state.xid_cache_element->xa_state= XA_PREPARED; + MYSQL_SET_TRANSACTION_XA_STATE(thd->m_transaction_psi, XA_PREPARED); + } DBUG_RETURN(thd->is_error() || thd->transaction.xid_state.xid_cache_element->xa_state != XA_PREPARED); @@ -608,6 +619,16 @@ bool trans_xa_commit(THD *thd) res= MY_TEST(ha_commit_one_phase(thd, 1)); if (res) my_error(ER_XAER_RMERR, MYF(0)); + else + { + /* + Since we don't call ha_commit_trans() for prepared transactions, + we need to explicitly mark the transaction as committed. + */ + MYSQL_COMMIT_TRANSACTION(thd->m_transaction_psi); + } + + thd->m_transaction_psi= NULL; } } else @@ -624,7 +645,8 @@ bool trans_xa_commit(THD *thd) xid_cache_delete(thd, &thd->transaction.xid_state); trans_track_end_trx(thd); - + /* The transaction should be marked as complete in P_S. */ + DBUG_ASSERT(thd->m_transaction_psi == NULL || res); DBUG_RETURN(res); } diff --git a/storage/federatedx/ha_federatedx.cc b/storage/federatedx/ha_federatedx.cc index b042fa720dc..81f6ee99f82 100644 --- a/storage/federatedx/ha_federatedx.cc +++ b/storage/federatedx/ha_federatedx.cc @@ -3492,7 +3492,7 @@ int ha_federatedx::start_stmt(MYSQL_THD thd, thr_lock_type lock_type) if (!txn->in_transaction()) { txn->stmt_begin(); - trans_register_ha(thd, FALSE, ht); + trans_register_ha(thd, FALSE, ht, NULL); } DBUG_RETURN(0); } @@ -3515,12 +3515,12 @@ int ha_federatedx::external_lock(MYSQL_THD thd, int lock_type) if (!thd_test_options(thd, (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { txn->stmt_begin(); - trans_register_ha(thd, FALSE, ht); + trans_register_ha(thd, FALSE, ht, NULL); } else { txn->txn_begin(); - trans_register_ha(thd, TRUE, ht); + trans_register_ha(thd, TRUE, ht, NULL); } } } @@ -3538,7 +3538,7 @@ int ha_federatedx::savepoint_set(handlerton *hton, MYSQL_THD thd, void *sv) if (txn && txn->has_connections()) { if (txn->txn_begin()) - trans_register_ha(thd, TRUE, hton); + trans_register_ha(thd, TRUE, hton, NULL); txn->sp_acquire((ulong *) sv); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index c2094b23d3a..4072e88efdd 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -2786,19 +2786,15 @@ innobase_register_trx( THD* thd, /* in: MySQL thd (connection) object */ trx_t* trx) /* in: transaction to register */ { - /* JAN: TODO: MySQL 5.7 PSI - const ulonglong trx_id = static_cast( + const ulonglong trx_id = static_cast( trx_get_id_for_print(trx)); trans_register_ha(thd, FALSE, hton, &trx_id); - */ - trans_register_ha(thd, FALSE, hton); if (!trx_is_registered_for_2pc(trx) && thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { - //trans_register_ha(thd, TRUE, hton, &trx_id); - trans_register_ha(thd, TRUE, hton); + trans_register_ha(thd, TRUE, hton, &trx_id); } trx_register_for_2pc(trx); diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc index 71ec5eb4634..711aaaaa173 100644 --- a/storage/rocksdb/ha_rocksdb.cc +++ b/storage/rocksdb/ha_rocksdb.cc @@ -4788,10 +4788,10 @@ static inline void rocksdb_register_tx(handlerton *const hton, THD *const thd, Rdb_transaction *const tx) { DBUG_ASSERT(tx != nullptr); - trans_register_ha(thd, FALSE, rocksdb_hton); + trans_register_ha(thd, FALSE, rocksdb_hton, NULL); if (my_core::thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { tx->start_stmt(); - trans_register_ha(thd, TRUE, rocksdb_hton); + trans_register_ha(thd, TRUE, rocksdb_hton, NULL); } } diff --git a/storage/spider/spd_trx.cc b/storage/spider/spd_trx.cc index d3ff8424f9d..1273bf64fc8 100644 --- a/storage/spider/spd_trx.cc +++ b/storage/spider/spd_trx.cc @@ -1990,9 +1990,9 @@ int spider_internal_start_trx( trx->trx_consistent_snapshot ? "TRUE" : "FALSE")); if (!trx->trx_consistent_snapshot) { - trans_register_ha(thd, FALSE, spider_hton_ptr); + trans_register_ha(thd, FALSE, spider_hton_ptr, NULL); if (thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) - trans_register_ha(thd, TRUE, spider_hton_ptr); + trans_register_ha(thd, TRUE, spider_hton_ptr, NULL); } trx->trx_start = TRUE; trx->trx_xa_prepared = FALSE; @@ -3335,8 +3335,8 @@ int spider_start_consistent_snapshot( trx->trx_consistent_snapshot = TRUE; trx->use_consistent_snapshot = TRUE; trx->internal_xa_snapshot = spider_param_internal_xa_snapshot(trx->thd); - trans_register_ha(trx->thd, FALSE, spider_hton_ptr); - trans_register_ha(trx->thd, TRUE, spider_hton_ptr); + trans_register_ha(trx->thd, FALSE, spider_hton_ptr, NULL); + trans_register_ha(trx->thd, TRUE, spider_hton_ptr, NULL); if (spider_param_use_all_conns_snapshot(trx->thd)) { trx->internal_xa = FALSE; -- cgit v1.2.1