summaryrefslogtreecommitdiff
path: root/storage/innobase/trx/trx0trx.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/trx/trx0trx.cc')
-rw-r--r--storage/innobase/trx/trx0trx.cc118
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 };