diff options
Diffstat (limited to 'storage/innobase/trx/trx0trx.cc')
-rw-r--r-- | storage/innobase/trx/trx0trx.cc | 118 |
1 files changed, 81 insertions, 37 deletions
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 2eb3161f3b7..6cbf0c273d9 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -460,10 +460,57 @@ void trx_free(trx_t*& trx) trx = NULL; } +/** Transition to committed state, to release implicit locks. */ +inline void trx_t::commit_state() +{ + /* This makes the transaction committed in memory and makes its + changes to data visible to other transactions. NOTE that there is a + small discrepancy from the strict formal visibility rules here: a + user of the database can see modifications made by another + transaction T even before the necessary redo log segment has been + flushed to the disk. If the database happens to crash before the + flush, the user has seen modifications from T which will never be a + committed transaction. However, any transaction T2 which sees the + modifications of the committing transaction T, and which also itself + makes modifications to the database, will get an lsn larger than the + committing transaction T. In the case where the log flush fails, and + T never gets committed, also T2 will never get committed. */ + ut_ad(trx_mutex_own(this)); + ut_ad(state != TRX_STATE_NOT_STARTED); + ut_ad(state != TRX_STATE_COMMITTED_IN_MEMORY + || (is_recovered && !UT_LIST_GET_LEN(lock.trx_locks))); + state= TRX_STATE_COMMITTED_IN_MEMORY; + + /* If the background thread trx_rollback_or_clean_recovered() + is still active then there is a chance that the rollback + thread may see this trx as COMMITTED_IN_MEMORY and goes ahead + to clean it up calling trx_cleanup_at_db_startup(). This can + happen in the case we are committing a trx here that is left + in PREPARED state during the crash. Note that commit of the + rollback of a PREPARED trx happens in the recovery thread + while the rollback of other transactions happen in the + background thread. To avoid this race we unconditionally unset + the is_recovered flag. */ + is_recovered= false; + ut_ad(id || !is_referenced()); +} + +/** Release any explicit locks of a committing transaction. */ +inline void trx_t::release_locks() +{ + DBUG_ASSERT(state == TRX_STATE_COMMITTED_IN_MEMORY); + + if (UT_LIST_GET_LEN(lock.trx_locks)) + lock_trx_release_locks(this); + else + lock.table_locks.clear(); // Work around a bug +} + /** At shutdown, frees a transaction object. */ void trx_free_at_shutdown(trx_t *trx) { + trx_mutex_enter(trx); ut_ad(trx->is_recovered); ut_a(trx_state_eq(trx, TRX_STATE_PREPARED) || trx_state_eq(trx, TRX_STATE_PREPARED_RECOVERED) @@ -477,21 +524,16 @@ trx_free_at_shutdown(trx_t *trx) && !srv_undo_sources && srv_fast_shutdown)))); ut_a(trx->magic_n == TRX_MAGIC_N); - lock_trx_release_locks(trx); + trx->commit_state(); + trx_mutex_exit(trx); + trx->release_locks(); trx_undo_free_at_shutdown(trx); ut_a(!trx->read_only); DBUG_LOG("trx", "Free prepared: " << trx); trx->state = TRX_STATE_NOT_STARTED; - - /* Undo trx_resurrect_table_locks(). */ - lock_trx_lock_list_init(&trx->lock.trx_locks); - - /* Note: This vector is not guaranteed to be empty because the - transaction was never committed and therefore lock_trx_release() - was not called. */ - trx->lock.table_locks.clear(); + ut_ad(!UT_LIST_GET_LEN(trx->lock.trx_locks)); trx->id = 0; trx_free(trx); @@ -1308,8 +1350,8 @@ trx_commit_in_memory( /* Note: We are asserting without holding the lock mutex. But that is OK because this transaction is not waiting and cannot - be rolled back and no new locks can (or should not) be added - becuase it is flagged as a non-locking read-only transaction. */ + be rolled back and no new locks can (or should) be added + because it is flagged as a non-locking read-only transaction. */ ut_a(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0); @@ -1327,30 +1369,35 @@ trx_commit_in_memory( DBUG_LOG("trx", "Autocommit in memory: " << trx); trx->state = TRX_STATE_NOT_STARTED; } else { - if (trx->id > 0) { - /* For consistent snapshot, we need to remove current - transaction from rw_trx_hash before doing commit and - releasing locks. */ + trx_mutex_enter(trx); + trx->commit_state(); + trx_mutex_exit(trx); + + if (trx->id) { trx_sys.deregister_rw(trx); - } - lock_trx_release_locks(trx); - ut_ad(trx->read_only || !trx->rsegs.m_redo.rseg || trx->id); + /* Wait for any implicit-to-explicit lock + conversions to cease, so that there will be no + race condition in lock_release(). */ + while (UNIV_UNLIKELY(trx->is_referenced())) { + ut_delay(srv_spin_wait_delay); + } - /* Remove the transaction from the list of active - transactions now that it no longer holds any user locks. */ + trx->release_locks(); + trx->id = 0; + } else { + ut_ad(trx->read_only || !trx->rsegs.m_redo.rseg); + trx->release_locks(); + } - ut_ad(trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)); DEBUG_SYNC_C("after_trx_committed_in_memory"); - if (trx->read_only || trx->rsegs.m_redo.rseg == NULL) { + if (trx->read_only || !trx->rsegs.m_redo.rseg) { MONITOR_INC(MONITOR_TRX_RO_COMMIT); } else { trx_update_mod_tables_timestamp(trx); MONITOR_INC(MONITOR_TRX_RW_COMMIT); } - - trx->id = 0; } ut_ad(!trx->rsegs.m_redo.undo); @@ -2166,7 +2213,7 @@ int trx_recover_for_mysql(XID *xid_list, uint len) struct trx_get_trx_by_xid_callback_arg { - XID *xid; + const XID *xid; trx_t *trx; }; @@ -2178,6 +2225,7 @@ static my_bool trx_get_trx_by_xid_callback(rw_trx_hash_element_t *element, mutex_enter(&element->mutex); if (trx_t *trx= element->trx) { + trx_mutex_enter(trx); if (trx->is_recovered && (trx_state_eq(trx, TRX_STATE_PREPARED) || trx_state_eq(trx, TRX_STATE_PREPARED_RECOVERED)) && @@ -2194,23 +2242,19 @@ static my_bool trx_get_trx_by_xid_callback(rw_trx_hash_element_t *element, arg->trx= trx; found= 1; } + trx_mutex_exit(trx); } mutex_exit(&element->mutex); return found; } - -/** - Finds PREPARED XA transaction by xid. - - trx may have been committed, unless the caller is holding lock_sys.mutex. - - @param[in] xid X/Open XA transaction identifier - - @return trx or NULL; on match, the trx->xid will be invalidated; -*/ - -trx_t *trx_get_trx_by_xid(XID *xid) +/** Look up an X/Open distributed transaction in XA PREPARE state. +@param[in] xid X/Open XA transaction identifier +@return transaction on match (the trx_t::xid will be invalidated); +note that the trx may have been committed before the caller acquires +trx_t::mutex +@retval NULL if no match */ +trx_t* trx_get_trx_by_xid(const XID* xid) { trx_get_trx_by_xid_callback_arg arg= { xid, 0 }; |