diff options
-rw-r--r-- | sql/wsrep_thd.cc | 2 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 27 | ||||
-rw-r--r-- | storage/innobase/include/trx0trx.h | 15 | ||||
-rw-r--r-- | storage/innobase/lock/lock0lock.cc | 104 |
4 files changed, 84 insertions, 64 deletions
diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index 640401cb1bf..69b0d2e0bcb 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -896,7 +896,7 @@ void wsrep_report_bf_lock_wait(THD *thd, { if (thd) { - WSREP_ERROR("Thread %s trx_id: %llu thread: %ld " + WSREP_INFO("Thread %s trx_id: %llu thread: %ld " "seqno: %lld query_state: %s conf_state: %s exec_mode: %s " "applier: %d query: %s", wsrep_thd_is_BF(thd, false) ? "BF" : "normal", diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 5074855af64..a6b5d90cdbf 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -18801,14 +18801,25 @@ static void bg_wsrep_kill_trx( if (thd) { wsrep_thd_LOCK(thd); victim_trx = thd_to_trx(thd); - lock_mutex_enter(); - trx_mutex_enter(victim_trx); - wsrep_thd_UNLOCK(thd); - if (victim_trx->id != arg->trx_id) - { - trx_mutex_exit(victim_trx); - lock_mutex_exit(); - victim_trx = NULL; + /* Victim trx might not exists e.g. on MDL-conflict. */ + if (victim_trx) { + lock_mutex_enter(); + trx_mutex_enter(victim_trx); + wsrep_thd_UNLOCK(thd); + if (victim_trx->id != arg->trx_id || + victim_trx->state == TRX_STATE_COMMITTED_IN_MEMORY) + { + /* Victim was meanwhile rolled back or + committed */ + trx_mutex_exit(victim_trx); + lock_mutex_exit(); + victim_trx = NULL; + wsrep_thd_kill_UNLOCK(thd); + } + } else { + /* find_thread_by_id locked + THD::LOCK_thd_kill */ + wsrep_thd_UNLOCK(thd); wsrep_thd_kill_UNLOCK(thd); } } diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index c1572a0d07f..69db3a99722 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -1122,7 +1122,20 @@ public: /** Free the memory to trx_pools */ void free(); - +#if defined(WITH_WSREP) && defined(UNIV_DEBUG) + inline const char* trx_state_str() + { + switch(state) + { + case TRX_STATE_NOT_STARTED : return "TRX_STATE_NOT_STARTED"; break; + case TRX_STATE_ACTIVE : return "TRX_STATE_ACTIVE"; break; + case TRX_STATE_PREPARED : return "TRX_STATE_PREPARED"; break; + case TRX_STATE_PREPARED_RECOVERED : return "TRX_STATE_PREPARED"; break; + case TRX_STATE_COMMITTED_IN_MEMORY : return "TRX_STATE_COMMITTED_IN_MEMORY"; break; + default: ut_error; return " "; break; // for compiler + } + } +#endif /* WITH_WSREP && UNIV_DEBUG */ private: /** Assign a rollback segment for modifying temporary tables. @return the assigned rollback segment */ diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index ee57a493119..1d9d5c5eae7 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -637,6 +637,7 @@ lock_rec_get_insert_intention( return(lock->type_mode & LOCK_INSERT_INTENTION); } +#ifdef UNIV_DEBUG #ifdef WITH_WSREP /** Check if both conflicting lock and other record lock are brute force (BF). This case is a bug so report lock information and wsrep state. @@ -647,7 +648,7 @@ lock_rec_get_insert_intention( static void wsrep_assert_no_bf_bf_wait( const lock_t* lock_rec1, const lock_t* lock_rec2, - const trx_t* trx1) + trx_t* trx1) { ut_ad(!lock_rec1 || lock_get_type_low(lock_rec1) == LOCK_REC); ut_ad(lock_get_type_low(lock_rec2) == LOCK_REC); @@ -664,6 +665,22 @@ static void wsrep_assert_no_bf_bf_wait( if (UNIV_LIKELY(!wsrep_thd_is_BF(lock_rec2->trx->mysql_thd, FALSE))) return; + ut_ad(!trx_mutex_own(trx1)); + ut_ad(!trx_mutex_own(lock_rec2->trx)); + trx_mutex_enter(trx1); + trx_mutex_enter(lock_rec2->trx); + + + /* If transaction is already committed in memory we can wait */ + if (trx_state_eq(trx1, TRX_STATE_COMMITTED_IN_MEMORY) || + trx_state_eq(lock_rec2->trx, TRX_STATE_COMMITTED_IN_MEMORY)) { + trx_mutex_exit(trx1); + trx_mutex_exit(lock_rec2->trx); + return; + } + trx_mutex_exit(trx1); + trx_mutex_exit(lock_rec2->trx); + /* if BF - BF order is honored, we can keep trx1 waiting for the lock */ if (wsrep_trx_order_before(trx1->mysql_thd, lock_rec2->trx->mysql_thd)) return; @@ -674,6 +691,10 @@ static void wsrep_assert_no_bf_bf_wait( if (wsrep_trx_is_aborting(lock_rec2->trx->mysql_thd)) return; + WSREP_INFO("Trx state trx_id: " TRX_ID_FMT " state: %s" + " trx_id2: " TRX_ID_FMT " state: %s", + trx1->id, trx1->trx_state_str(), + lock_rec2->trx->id, lock_rec2->trx->trx_state_str()); mtr_t mtr; if (lock_rec1) { @@ -702,6 +723,7 @@ static void wsrep_assert_no_bf_bf_wait( ut_error; } #endif /* WITH_WSREP */ +#endif /* UNIV_DEBUG */ /*********************************************************************//** Checks if a lock request for a new lock has to wait for request lock2. @@ -824,9 +846,13 @@ lock_rec_has_to_wait( return false; } - /* There should not be two conflicting locks that are - brute force. If there is it is a bug. */ - wsrep_assert_no_bf_bf_wait(NULL, lock2, trx); +#ifdef UNIV_DEBUG + /* We very well can let bf to wait normally as other + BF will be replayed in case of conflict. For debug + builds we will do additional sanity checks to catch + unsupported bf wait if any. */ + wsrep_assert_no_bf_bf_wait(NULL, lock2, const_cast<trx_t*>(trx)); +#endif #endif /* WITH_WSREP */ return true; @@ -1104,57 +1130,35 @@ wsrep_kill_victim( { ut_ad(lock_mutex_own()); ut_ad(trx_mutex_own(lock->trx)); + ut_ad(trx->is_wsrep()); + ut_ad(lock->trx != trx); - /* quit for native mysql */ - if (!trx->is_wsrep()) return; + if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) { + return; + } - my_bool bf_this = wsrep_thd_is_BF(trx->mysql_thd, FALSE); - my_bool bf_other = wsrep_thd_is_BF(lock->trx->mysql_thd, FALSE); - mtr_t mtr; + trx_t* lock_trx = lock->trx; + + if (lock_trx->state == TRX_STATE_COMMITTED_IN_MEMORY + || lock_trx->lock.was_chosen_as_deadlock_victim) { + return; + } + + my_bool bf_other = wsrep_thd_is_BF(lock_trx->mysql_thd, FALSE); - if ((bf_this && !bf_other) || - (bf_this && bf_other && wsrep_trx_order_before( - trx->mysql_thd, lock->trx->mysql_thd))) { + if ((!bf_other) || + (wsrep_trx_order_before( + trx->mysql_thd, lock_trx->mysql_thd))) { if (lock->trx->lock.que_state == TRX_QUE_LOCK_WAIT) { if (UNIV_UNLIKELY(wsrep_debug)) { - ib::info() << "WSREP: BF victim waiting\n"; + WSREP_INFO("BF victim waiting"); } /* cannot release lock, until our lock is in the queue*/ - } else if (lock->trx != trx) { - if (wsrep_log_conflicts) { - if (bf_this) { - ib::info() << "*** Priority TRANSACTION:"; - } else { - ib::info() << "*** Victim TRANSACTION:"; - } - - trx_print_latched(stderr, trx, 3000); - - if (bf_other) { - ib::info() << "*** Priority TRANSACTION:"; - } else { - ib::info() << "*** Victim TRANSACTION:"; - } - trx_print_latched(stderr, lock->trx, 3000); - - ib::info() << "*** WAITING FOR THIS LOCK TO BE GRANTED:"; - - if (lock_get_type(lock) == LOCK_REC) { - lock_rec_print(stderr, lock, mtr); - } else { - lock_table_print(stderr, lock); - } - - ib::info() << " SQL1: " - << wsrep_thd_query(trx->mysql_thd); - ib::info() << " SQL2: " - << wsrep_thd_query(lock->trx->mysql_thd); - } - - wsrep_innobase_kill_one_trx(trx->mysql_thd, - trx, lock->trx, TRUE); + } else { + wsrep_innobase_kill_one_trx(trx->mysql_thd, trx, + lock_trx, true); } } } @@ -2248,10 +2252,6 @@ static void lock_rec_dequeue_from_page(lock_t* in_lock) /* Grant the lock */ ut_ad(lock->trx != in_lock->trx); lock_grant(lock); -#ifdef WITH_WSREP - } else { - wsrep_assert_no_bf_bf_wait(c, lock, c->trx); -#endif /* WITH_WSREP */ } } } else { @@ -4204,10 +4204,6 @@ released: /* Grant the lock */ ut_ad(trx != lock->trx); lock_grant(lock); -#ifdef WITH_WSREP - } else { - wsrep_assert_no_bf_bf_wait(c, lock, c->trx); -#endif /* WITH_WSREP */ } } } else { |