/*************************************************************//**
Set detailed error message for the transaction from a file. Note that the
file is rewinded before reading from it. */ void
trx_set_detailed_error_from_file( /*=============================*/
trx_t* trx, /*!< in: transaction struct */
FILE* file) /*!< in: file to read message from */
{
os_file_read_string(file, trx->detailed_error, MAX_DETAILED_ERROR_LEN);
}
/** For managing the life-cycle of the trx_t instance that we get
from the pool. */ struct TrxFactory {
/** Initializes a transaction object. It must be explicitly started withtrx_start_if_not_started()beforeusingit.Thedefaultisolation levelisTRX_ISO_REPEATABLE_READ.
@param trx Transaction instance to initialise */ staticvoid init(trx_t* trx)
{ /* Explicitly call the constructor of the already allocatedobject.trx_tobjectsareallocatedby ut_zalloc_nokey()inPool::Pool()whichwouldnotcall
the constructors of the trx_t members. */ new(&trx->autoinc_locks) trx_t::autoinc_lock_vector();
/** Release resources held by the transaction object.
@param trx the transaction for which to release resources */ staticvoid destroy(trx_t* trx)
{ #ifdef __SANITIZE_ADDRESS__ /* Unpoison the memory for AddressSanitizer */
MEM_MAKE_ADDRESSABLE(trx, sizeof *trx); #elif !__has_feature(memory_sanitizer) /* In Valgrind, we cannot cancel MEM_NOACCESS() without changingthestateoftheVbits(whichindicate whichbitsareinitialized). Wewilldeclarethecontentsasinitialized.
We did invoke MEM_CHECK_DEFINED() in trx_t::free(). */
MEM_MAKE_DEFINED(trx, sizeof *trx); #endif
/** Use explicit mutexes for the trx_t pool and its manager. */ typedef Pool<trx_t, TrxFactory, TrxPoolLock> trx_pool_t; typedef PoolManager<trx_pool_t, TrxPoolManagerLock > trx_pools_t;
/** The trx_t pool manager */ static trx_pools_t* trx_pools;
/** Size of on trx_t pool in bytes. */ staticconst ulint MAX_TRX_BLOCK_SIZE = 1024 * 1024 * 4;
/** Create the trx_t pool */ void
trx_pool_init()
{
trx_pools = UT_NEW_NOKEY(trx_pools_t(MAX_TRX_BLOCK_SIZE));
ut_a(trx_pools != 0);
}
/** Destroy the trx_t pool */ void
trx_pool_close()
{
UT_DELETE(trx_pools);
#ifdef __SANITIZE_ADDRESS__ /* Unpoison the memory for AddressSanitizer.
It may have been poisoned in trx_t::free().*/
MEM_MAKE_ADDRESSABLE(trx, sizeof *trx); #elif !__has_feature(memory_sanitizer) /* In Valgrind, we cannot cancel MEM_NOACCESS() without changingthestateoftheVbits(whichindicate whichbitsareinitialized). Wewilldeclarethecontentsasinitialized.
We did invoke MEM_CHECK_DEFINED() in trx_t::free(). */
MEM_MAKE_DEFINED(trx, sizeof *trx); #endif
trx->assert_freed();
/* We just got trx from pool, it should be non locking */
ut_ad(!trx->will_lock);
ut_ad(!trx->rw_trx_hash_pins);
/** Transition to committed state, to release implicit locks. */
TRANSACTIONAL_INLINE inlinevoid trx_t::commit_state()
{
ut_d(auto trx_state= state);
ut_ad(trx_state == TRX_STATE_PREPARED ||
trx_state == TRX_STATE_PREPARED_RECOVERED ||
trx_state == TRX_STATE_ACTIVE); /* This makes the transaction committed in memory and makes its changestodatavisibletoothertransactions.NOTEthatthereisa smalldiscrepancyfromthestrictformalvisibilityruleshere:a userofthedatabasecanseemodificationsmadebyanother transactionTevenbeforethenecessaryredologsegmenthasbeen flushedtothedisk.Ifthedatabasehappenstocrashbeforethe flush,theuserhasseenmodificationsfromTwhichwillneverbea committedtransaction.However,anytransactionT2whichseesthe modificationsofthecommittingtransactionT,andwhichalsoitself makesmodificationstothedatabase,willgetanlsnlargerthanthe committingtransactionT.Inthecasewherethelogflushfails,and
T never gets committed, also T2 will never get committed. */
TMTrxGuard tg{*this};
state= TRX_STATE_COMMITTED_IN_MEMORY;
ut_ad(id || !is_referenced());
}
/** Release any explicit locks of a committing transaction. */ inlinevoid trx_t::release_locks()
{
DEBUG_SYNC_C("trx_t_release_locks_enter");
DBUG_ASSERT(state == TRX_STATE_COMMITTED_IN_MEMORY);
DBUG_ASSERT(!is_referenced());
if (srv_operation == SRV_OPERATION_RESTORE) { /* mariabackup --prepare only deals with theredologandthedatafiles,notwith
transactions or the data dictionary. */ return trx_rseg_array_init();
}
if (srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN) { return DB_SUCCESS;
}
if (constauto size = trx_sys.rw_trx_hash.size()) {
ib::info() << size
<< " transaction(s) which must be rolled back or" " cleaned up in total " << rows_to_undo
<< " row operations to undo";
ib::info() << "Trx id counter is " << trx_sys.get_max_trx_id();
}
goto func_exit;
}
/** Assign a persistent rollback segment in a round-robin fashion, evenlydistributedbetween0andinnodb_undo_logs-1
@param trx transaction */ staticvoid trx_assign_rseg_low(trx_t *trx)
{
ut_ad(!trx->rsegs.m_redo.rseg);
ut_ad(srv_available_undo_logs == TRX_SYS_N_RSEGS);
/* The first slot is always assigned to the system tablespace. */
ut_ad(trx_sys.rseg_array[0].space == fil_system.sys_space);
trx_sys.register_rw(trx);
ut_ad(trx->id);
/* Choose a rollback segment evenly distributed between 0 and innodb_undo_logs-1inaround-robinfashion,skippingthose
undo tablespaces that are scheduled for truncation. */ static Atomic_counter<unsigned> rseg_slot; unsigned slot = rseg_slot++ % TRX_SYS_N_RSEGS;
DBUG_EXECUTE_IF("assign_same_rseg", slot= 0;);
ut_d(constauto start_scan_slot = slot);
ut_d(bool look_for_rollover = false);
trx_rseg_t* rseg;
if (rseg->space != fil_system.sys_space) { if (rseg->skip_allocation()) { continue;
}
} elseif (const fil_space_t *space =
trx_sys.rseg_array[slot].space) { if (space != fil_system.sys_space
&& srv_undo_tablespaces > 0) { /** If dedicated innodb_undo_tablespaceshave beenconfigured,trytousethem
instead of the system tablespace. */ continue;
}
}
break;
}
/* By now we have only selected the rseg but not marked it allocated.Bymarkingitallocatedweareensuringthatitwill
never be selected for UNDO truncate purge. */
allocated = rseg->acquire_if_available();
} while (!allocated);
trx->rsegs.m_redo.rseg = rseg;
}
/** Assign a rollback segment for modifying temporary tables.
@return the assigned rollback segment */
trx_rseg_t *trx_t::assign_temp_rseg()
{
ut_ad(!rsegs.m_noredo.rseg);
ut_ad(!is_autocommit_non_locking());
compile_time_assert(ut_is_2pow(TRX_SYS_N_RSEGS));
/* Choose a temporary rollback segment between 0 and 127
in a round-robin fashion. */ static Atomic_counter<unsigned> rseg_slot;
trx_rseg_t* rseg = &trx_sys.temp_rsegs[
rseg_slot++ & (TRX_SYS_N_RSEGS - 1)];
ut_ad(!rseg->is_persistent());
rsegs.m_noredo.rseg = rseg;
/* No other thread can access this trx object through rw_trx_hash, stillitcanbefoundthroughtrx_sys.trx_list.Sometimesit's possibletoindirectlyprotecttrx_t::statebyfreezing trx_sys.trx_list.
Fornowweupdateitwithoutmutexprotection,becauseoriginalcode
did it this way. It has to be reviewed and fixed properly. */
trx->state = TRX_STATE_ACTIVE;
/* By default all transactions are in the read-only list unless they arenon-lockingauto-commitreadonlytransactionsorbackground (internal)transactions.Note:Transactionsmarkedexplicitlyas readonlycanwritetotemporarytables,weputthoseontheRO
list too. */
if (!trx->read_only
&& (!trx->mysql_thd || read_write || trx->dict_operation)) { /* Temporary rseg is assigned only if the transaction
updates a temporary table */ if (!high_level_read_only && !recv_sys.rpo) {
trx_assign_rseg_low(trx);
}
} else { if (!trx->is_autocommit_non_locking()) {
/* If this is a read-only transaction that is writing toatemporarytablethenitneedsatransactionid
to write to the temporary table. */
/** Release an empty undo log that was associated with a transaction. */
ATTRIBUTE_COLD void trx_t::commit_empty(mtr_t *mtr)
{
trx_rseg_t *rseg= rsegs.m_redo.rseg;
trx_undo_t *&undo= rsegs.m_redo.undo;
IfwesimplychangedtheundopagestatetoTRX_UNDO_CACHED, thentrx_undo_reuse_cached()couldrunoutofspace.Wewill
release the space consumed by our empty undo log to avoid that. */ for (byte *last= &u->page.frame[TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE],
*prev= nullptr;;)
{ /* TRX_UNDO_PREV_LOG is only being read in debug assertions, and writtenintrx_undo_header_create().Toremaincompatiblewith possiblycorruptedolddatafiles,wewillnotreadthefield
TRX_UNDO_PREV_LOG but instead rely on TRX_UNDO_NEXT_LOG. */
ut_ad(mach_read_from_2(TRX_UNDO_PREV_LOG + last) ==
(reinterpret_cast<size_t>(prev) & (srv_page_size - 1)));
/* We may have updated PAGE_MAX_TRX_ID on secondary index pages tothis->id.Ensurethattrx_sys.m_max_trx_idwillberecovered correctly,eventhoughweremovedourundologrecordalong
with the TRX_UNDO_TRX_ID above. */
/* Below, we are acquiring rseg_header->page.lock after u->page.lock(theoppositeoftrx_purge_add_undo_to_history()). Thisisfine,becausebothfunctionsareholdingexclusive
rseg->latch. */
do
{ if (UNIV_UNLIKELY(mach_read_from_4(TRX_RSEG + TRX_RSEG_FORMAT +
rseg_header->page.frame))) /* This must have been upgraded from before MariaDB 10.3.5. */
trx_rseg_format_upgrade(rseg_header, mtr); elseif (mach_read_from_8(m) >= id) continue;
mtr->write<8>(*rseg_header, m, id);
} while (0);
}
} else /* Our undo log header was right after the undo log segment header. Thispageshouldhavebeencreatedbytrx_undo_create(),not returnedbytrx_undo_reuse_cached().
/* We must assign an "end" identifier even though we are not going topersistentlywriteitanywhere,tomakesurethatthepurgeof
history will not be stuck. */
trx_sys.assign_new_trx_no(this);
}
/** Assign the transaction its history serialisation number and write the UNDOlogtotheassignedrollbacksegment.
@param mtr mini-transaction */ inlinevoid trx_t::write_serialisation_history(mtr_t *mtr)
{
ut_ad(!read_only);
trx_rseg_t *rseg= rsegs.m_redo.rseg;
trx_undo_t *&undo= rsegs.m_redo.undo;
binlog_oob_context *binlog_ctx= nullptr; if (UNIV_LIKELY(undo != nullptr))
{
trx_id_t end;
MONITOR_INC(MONITOR_TRX_COMMIT_UNDO);
/* We have to hold exclusive rseg->latch because undo log headers have tobeputtothehistorylistinthe(serialisation)orderofthe
UNDO trx number. This is required for purge_sys too. */
rseg->latch.wr_lock(SRW_LOCK_CALL);
ut_ad(undo->rseg == rseg); /* Assign the transaction serialisation number and add any
undo log to the purge queue. */ if (UNIV_UNLIKELY(!undo_no))
{ /* The transaction was rolled back. */
commit_empty(mtr); goto done;
} elseif (rseg->last_page_no == FIL_NULL)
{ /* trx_sys.assign_new_trx_no() and purge_sys.enqueue()mustbeinvokedinthesame criticalsectionprotectedwithpurgequeuemutextoavoidrsegwith greaterlastcommitnumbertobepushedtopurgequeuepriortorsegwith lesserlastcommitnumber.Inotherwordspushingtopurgequeuemustbe serializedalongwithassigningtrx_no.Otherwisepurgecoordinator threadcanalsofetchredologrecordsfromrsegwithgreaterlastcommit
number before rseg with lesser one. */
purge_sys.queue_lock();
end= trx_sys.assign_new_trx_no(this);
rseg->last_page_no= undo->hdr_page_no; /* end cannot be less than anything in rseg. User threads only
produce events when a rollback segment is empty. */
rseg->set_last_commit(undo->hdr_offset, end);
purge_sys.enqueue(end, *rseg);
purge_sys.queue_unlock();
} else
end= trx_sys.assign_new_trx_no(this);
/* Include binlog data in the commit record, if any. */ if (active_commit_ordered)
binlog_ctx= innodb_binlog_trx(this, mtr);
UT_LIST_REMOVE(rseg->undo_list, undo); /* Change the undo log segment state from TRX_UNDO_ACTIVE, to definethetransactionascommittedinthefilebaseddomain,
at mtr->commit_lsn() obtained in mtr->commit() below. */
trx_purge_add_undo_to_history(this, undo, mtr, end);
done:
rseg->release();
rseg->latch.wr_unlock();
} else
rseg->release();
mtr->commit();
commit_lsn= undo_no || !xid.is_null() ? mtr->commit_lsn() : 0;
innodb_binlog_post_commit(mtr, binlog_ctx);
}
/** Process tables that were modified by the committing transaction. */ inlinevoid trx_t::commit_tables()
{ if (undo_no && !mod_tables.empty())
{ const trx_id_t max_trx_id= trx_sys.get_max_trx_id(); constauto now= start_time;
/** Evict a table definition due to the rollback of ALTER TABLE. @paramtable_idtableidentifier
@param reset_only whether to only reset dict_table_t::def_trx_id */ void trx_t::evict_table(table_id_t table_id, bool reset_only)
{
ut_ad(in_rollback);
dict_table_t* table = dict_sys.find_table(table_id); if (!table) { return;
}
table->def_trx_id = 0;
if (auto ref_count = table->get_ref_count()) { /* This must be a DDL operation that is being rolled
back in an active connection. */
ut_a(ref_count == 1);
ut_ad(!is_recovered);
ut_ad(mysql_thd); return;
}
if (reset_only) { return;
}
/* This table should only be locked by this transaction, if at all. */
ut_ad(UT_LIST_GET_LEN(table->locks) <= 1); constbool locked = UT_LIST_GET_LEN(table->locks);
ut_ad(!locked || UT_LIST_GET_FIRST(table->locks)->trx == this);
dict_sys.remove(table, true, locked); if (locked) {
UT_LIST_ADD_FIRST(lock.evicted_tables, table);
}
}
/** Free temporary undo log after commit or rollback. @parammtrmini-transaction
@param undo temporary undo log */
ATTRIBUTE_NOINLINE staticvoid trx_commit_cleanup(mtr_t *mtr,
trx_undo_t *&undo)
{
trx_rseg_t *const rseg= undo->rseg;
ut_ad(rseg->space == fil_system.temp_space);
rseg->latch.wr_lock(SRW_LOCK_CALL);
UT_LIST_REMOVE(rseg->undo_list, undo);
ut_ad(undo->state == TRX_UNDO_ACTIVE || undo->state == TRX_UNDO_PREPARED);
ut_ad(undo->id < TRX_RSEG_N_SLOTS); /* Delete first the undo log segment in the file */ bool finished; do
{
mtr->start();
mtr->set_log_mode(MTR_LOG_NO_REDO);
/* Note: We do not have to hold any lock_sys latch here, because
this is a non-locking transaction. */
ut_a(UT_LIST_GET_LEN(lock.trx_locks) == 0);
ut_ad(UT_LIST_GET_LEN(lock.evicted_tables) == 0);
/* This state change is not protected by any mutex, therefore thereisaninherentraceherearoundstatetransitionduring printouts.Weignorethisraceforthesakeofefficiency. However,thefreezingoftrx_sys.trx_listwillprotectthetrx_t instanceanditcannotberemovedfromthetrx_listandfreed
without first unfreezing trx_list. */
state= TRX_STATE_NOT_STARTED;
MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT);
DBUG_LOG("trx", "Autocommit in memory: " << this);
} else
{ #ifdef UNIV_DEBUG if (!UT_LIST_GET_LEN(lock.trx_locks)) for (auto l : lock.table_locks)
ut_ad(!l); #endif/* UNIV_DEBUG */
commit_state();
DEBUG_SYNC_C("trx_after_commit_state");
if (id)
{
trx_sys.deregister_rw(this);
/* Wait for any implicit-to-explicit lock conversions to cease,
so that there will be no race condition in lock_release(). */ while (UNIV_UNLIKELY(is_referenced()))
LF_BACKOFF();
} else
ut_ad(read_only || !rsegs.m_redo.rseg);
if (UNIV_LIKELY(!dict_operation))
release_locks();
}
if (commit_lsn)
{ /* Depending on the my.cnf options, we may now write the log buffertothelogfiles,makingthetransactiondurableiftheOS doesnotcrash.Wemayalsoflushthelogfilestodisk,making thetransactiondurablealsoatanOScrashorapoweroutage.
if (fts_trx)
trx_finalize_for_fts(this, undo_no != 0);
#ifdef WITH_WSREP /* Assert that any transaction which is started as a wsrep transaction,alsocommitsorrollsbackasawsrep transaction.Wsrepappliermayturnwsrepofftemporarilyfor
rollback when the applying of wsrep transactions is retried. */
ut_ad(is_wsrep() == wsrep_on(mysql_thd) || wsrep_thd_in_rollback(mysql_thd));
/* Serialization history has been written and the transaction is committedinmemory,whichmakesthiscommitordered.Releasecommit
order critical section. */ if (wsrep)
{
wsrep= false;
wsrep_commit_ordered(mysql_thd);
} #endif/* WITH_WSREP */
lock.was_chosen_as_deadlock_victim= false;
}
/** Commit the transaction in the file system. */
TRANSACTIONAL_TARGET void trx_t::commit_persist() noexcept
{
mtr_t mtr{this};
mtr.start();
if (fts_trx && undo_no)
{
ut_a(!is_autocommit_non_locking()); /* MDEV-24088 FIXME: Invoke fts_commit() earlier (before possible XAPREPARE),sothatwewillbeabletoreturnanerrorandrollback thetransaction,insteadofviolatingconsistency!
TheoriginalclaimaboutDB_DUPLICATEKEYwas: Thisisapossiblescenarioifthereisacrashbetween inserttoDELETEDtablecommittingandtransactioncommitting.The
fix would be able to return error from this function */ if (ut_d(dberr_t error=) fts_commit(this))
ut_ad(error == DB_DUPLICATE_KEY || error == DB_LOCK_WAIT_TIMEOUT);
}
if (has_logged_persistent())
{ if (UNIV_UNLIKELY(apply_online_log))
apply_log();
/* The following call commits the mini-transaction, making the wholetransactioncommittedinthefile-basedworld,atthislog sequencenumber.Thetransactionbecomes'durable'whenwewrite thelogtodisk,butinthelogicalsensethecommitinthe file-baseddatastructures(undologsetc.)happenshere.
NOTEthattransactionnumbersdonotnecessarilycomein exactlythesameorderascommitlsn's,ifthetransactionshave differentrollbacksegments.However,ifatransactionT2is abletoseemodificationsmadebyatransactionT1,T2willalways
get a bigger transaction number and a bigger commit lsn than T1. */
write_serialisation_history(&mtr);
} elseif (trx_rseg_t *rseg= rsegs.m_redo.rseg)
{
ut_ad(id);
ut_ad(!rsegs.m_redo.undo);
rseg->release();
}
#ifdef ENABLED_DEBUG_SYNC if (debug_sync)
DEBUG_SYNC_C("before_trx_state_committed_in_memory"); #endif
/****************************************************************//**
Prepares a transaction for commit/rollback. */ void
trx_commit_or_rollback_prepare( /*===========================*/
trx_t* trx) /*!< in/out: transaction */
{ /* We are reading trx->state without holding trx->mutex here,becausethecommitorrollbackshouldbeinvokedfora running(orrecoveredprepared)transactionthatisassociated
with the current thread. */
switch (trx->state) { case TRX_STATE_NOT_STARTED:
trx_start_low(trx, true); /* fall through */
case TRX_STATE_ACTIVE: case TRX_STATE_PREPARED: case TRX_STATE_PREPARED_RECOVERED:
trx->lock.wait_thr = NULL; return;
case TRX_STATE_COMMITTED_IN_MEMORY: case TRX_STATE_ABORTED: break;
}
ut_error;
}
/*********************************************************************//**
Creates a commit command node struct.
@return own: commit node struct */
commit_node_t*
trx_commit_node_create( /*===================*/
mem_heap_t* heap) /*!< in: mem heap where created */
{
commit_node_t* node;
/***********************************************************//**
Performs an execution step for a commit type node in a query graph.
@return query thread to run next, or NULL */
que_thr_t*
trx_commit_step( /*============*/
que_thr_t* thr) /*!< in: query thread */
{
commit_node_t* node;
void trx_commit_for_mysql(trx_t *trx) noexcept
{ switch (trx->state) { case TRX_STATE_ABORTED:
trx->state= TRX_STATE_NOT_STARTED; /* fall through */ case TRX_STATE_NOT_STARTED:
trx->will_lock= false; break; case TRX_STATE_ACTIVE: case TRX_STATE_PREPARED: case TRX_STATE_PREPARED_RECOVERED:
trx->op_info= "committing";
trx->commit();
trx->op_info= ""; break; case TRX_STATE_COMMITTED_IN_MEMORY:
ut_error; break;
}
}
/** Durably write log until trx->commit_lsn
(if trx_t::commit_in_memory() was invoked with flush_log_later=true). */ void trx_commit_complete_for_mysql(trx_t *trx)
{ const lsn_t lsn= trx->commit_lsn; if (!lsn) return; switch (srv_flush_log_at_trx_commit) { case0: goto func_exit; case1: if (trx->active_commit_ordered && trx->active_prepare) return;
}
trx_flush_log_if_needed(lsn, trx);
func_exit:
trx->commit_lsn= 0;
}
/**********************************************************************//**
Prints info about a transaction. */ void
trx_print_low( /*==========*/
FILE* f, /*!< in: output stream */ const trx_t* trx, /*!< in: transaction */
ulint n_rec_locks, /*!< in: trx->lock.n_rec_locks */
ulint n_trx_locks, /*!< in: length of trx->lock.trx_locks */
ulint heap_size) /*!< in: mem_heap_get_size(trx->lock.lock_heap) */
{ if (const trx_id_t id = trx->id) {
fprintf(f, "TRANSACTION " TRX_ID_FMT, id);
} else {
my_fprintf(f, "TRANSACTION (%p)", trx);
}
THD* thd = trx->mysql_thd;
switch (trx->state) { case TRX_STATE_NOT_STARTED:
fputs(", not started", f);
thd = nullptr; goto state_ok; case TRX_STATE_ABORTED:
fputs(", forced rollback done", f);
thd = nullptr; goto state_ok; case TRX_STATE_ACTIVE:
fprintf(f, ", ACTIVE %lu sec",
(ulong) difftime(time(NULL), trx->start_time)); goto state_ok; case TRX_STATE_PREPARED: case TRX_STATE_PREPARED_RECOVERED:
fprintf(f, ", ACTIVE (PREPARED) %lu sec",
(ulong) difftime(time(NULL), trx->start_time)); goto state_ok; case TRX_STATE_COMMITTED_IN_MEMORY:
fputs(", COMMITTED IN MEMORY", f); goto state_ok;
}
fprintf(f, ", state %lu", (ulong) trx->state);
ut_ad(0);
state_ok: constchar* op_info = trx->op_info;
if (*op_info) {
putc(' ', f);
fputs(op_info, f);
}
if (trx->is_recovered) {
fputs(" recovered trx", f);
}
putc('\n', f);
if (trx->n_mysql_tables_in_use > 0 || trx->mysql_n_tables_locked > 0) {
fprintf(f, "mysql tables in use %lu, locked %lu\n",
(ulong) trx->n_mysql_tables_in_use,
(ulong) trx->mysql_n_tables_locked);
}
bool newline = true;
if (trx->in_rollback) { /* dirty read for performance reasons */
fputs("ROLLING BACK ", f);
} elseif (trx->lock.wait_lock) {
fputs("LOCK WAIT ", f);
} else {
newline = false;
}
/**********************************************************************//**
Prints info about a transaction.
The caller must hold lock_sys.latch. */ void
trx_print_latched( /*==============*/
FILE* f, /*!< in: output stream */ const trx_t* trx) /*!< in: transaction */
{
lock_sys.assert_locked();
/** Prepare a transaction. @returnlogsequencenumberthatmakestheXAPREPAREdurable
@retval 0 if no changes needed to be made durable */ static lsn_t trx_prepare_low(trx_t *trx)
{
ut_ad(!trx->is_recovered);
mtr_t mtr{trx};
if (trx_undo_t* undo = trx->rsegs.m_noredo.undo) {
ut_ad(undo->rseg == trx->rsegs.m_noredo.rseg);
if (!undo) { /* There were no changes to persistent tables. */ return(0);
}
ut_ad(undo->rseg == trx->rsegs.m_redo.rseg);
mtr.start();
/* Change the undo log segment states from TRX_UNDO_ACTIVE to TRX_UNDO_PREPARED:thesemodificationstothefiledata structuredefinethetransactionaspreparedinthefile-based
world, at the serialization point of lsn. */
trx_undo_set_state_at_prepare(undo, false, &mtr);
/* Make the XA PREPARE durable. */
mtr.commit();
ut_ad(mtr.commit_lsn() > 0); return(mtr.commit_lsn());
}
/****************************************************************//**
Prepares a transaction. */
TRANSACTIONAL_TARGET static void
trx_prepare( /*========*/
trx_t* trx) /*!< in/out: transaction */
{ /* Only fresh user transactions can be prepared.
Recovered transactions cannot. */
ut_a(!trx->is_recovered);
if (lsn) { /* Depending on the my.cnf options, we may now write the log buffertothelogfiles,makingthepreparedstateofthe transactiondurableiftheOSdoesnotcrash.Wemayalso flushthelogfilestodisk,makingthepreparedstateofthe transactiondurablealsoatanOScrashorapoweroutage.
We must not be holding any mutexes or latches here. */ if (auto f = srv_flush_log_at_trx_commit) {
log_write_up_to(lsn, f & 1);
}
if (!UT_LIST_GET_LEN(trx->lock.trx_locks)
|| trx->isolation_level == TRX_ISO_SERIALIZABLE) { /* Do not release any locks at the
SERIALIZABLE isolation level. */
} elseif (!trx->mysql_thd
|| trx->mysql_thd->lex->sql_command
!= SQLCOM_XA_PREPARE) { /* Do not release locks for XA COMMIT ONE PHASE orforinternaldistributedtransactions
(XID::get_my_xid() would be nonzero). */
} else {
lock_release_on_prepare(trx);
}
}
}
/** XA PREPARE a transaction.
@param[in,out] trx transaction to prepare */ void trx_prepare_for_mysql(trx_t* trx)
{
trx_start_if_not_started_xa(trx, false);
/* Fill xid_list with PREPARED transactions. */
trx_sys.rw_trx_hash.iterate_no_dups(trx_recover_for_mysql_callback, &arg); if (arg.count)
{
ib::info() << arg.count
<< " transactions in prepared state after recovery"; /* After returning the full list, reset the state, because init_server_components()wantstorecoverthecollectionof transactionstwice,byfirstcallingtc_log->open()andthen
ha_recover() directly. */ if (arg.count <= len)
trx_sys.rw_trx_hash.iterate(trx_recover_reset_callback);
} returnint(std::min(arg.count, len));
}
static my_bool trx_get_trx_by_xid_callback(void *el, void *a)
{ auto element= static_cast<rw_trx_hash_element_t*>(el); auto arg= static_cast<trx_get_trx_by_xid_callback_arg*>(a);
my_bool found= 0;
element->mutex.wr_lock(); if (trx_t *trx= element->trx)
{
trx->mutex_lock(); if (trx->is_recovered &&
(trx_state_eq(trx, TRX_STATE_PREPARED) ||
trx_state_eq(trx, TRX_STATE_PREPARED_RECOVERED)) &&
arg->xid->eq(&trx->xid))
{ #ifdef WITH_WSREP /* The commit of a prepared recovered Galera transactionneedsavalidtrx->xidfor
invoking trx_sys_update_wsrep_checkpoint(). */ if (!wsrep_is_wsrep_xid(&trx->xid)) #endif/* WITH_WSREP */ /* Invalidate the XID, so that subsequent calls will not find it. */
trx->xid.null();
arg->trx= trx;
found= 1;
}
trx->mutex_unlock();
}
element->mutex.wr_unlock(); return found;
}
/** Look up an X/Open distributed transaction in XA PREPARE state. @param[in]xidX/OpenXAtransactionidentifier @returntransactiononmatch(thetrx_t::xidwillbeinvalidated); notethatthetrxmayhavebeencommittedbeforethecalleracquires 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 };
if (xid)
trx_sys.rw_trx_hash.iterate(trx_get_trx_by_xid_callback, &arg); return arg.trx;
}
/*************************************************************//**
Starts the transaction if it is not yet started. */ void
trx_start_if_not_started_xa_low( /*============================*/
trx_t* trx, /*!< in/out: transaction */ bool read_write) /*!< in: true if read write transaction */
{ switch (trx->state) { case TRX_STATE_ABORTED: case TRX_STATE_NOT_STARTED:
trx_start_low(trx, read_write); return;
case TRX_STATE_ACTIVE: if (trx->id == 0 && read_write) { /* If the transaction is tagged as read-only then itcanonlywritetotemptablesandforsuch transactionswedon'twanttomovethemtothe
trx_sys_t::rw_trx_hash. */ if (!trx->read_only) {
trx_set_rw_mode(trx);
}
} return; case TRX_STATE_PREPARED: case TRX_STATE_PREPARED_RECOVERED: case TRX_STATE_COMMITTED_IN_MEMORY: break;
}
ut_error;
}
/*************************************************************//**
Starts the transaction if it is not yet started. */ void
trx_start_if_not_started_low( /*==========================*/
trx_t* trx, /*!< in: transaction */ bool read_write) /*!< in: true if read write transaction */
{ switch (trx->state) { case TRX_STATE_NOT_STARTED:
trx_start_low(trx, read_write); return;
case TRX_STATE_ACTIVE: if (read_write && trx->id == 0 && !trx->read_only) {
trx_set_rw_mode(trx);
} return;
case TRX_STATE_ABORTED: case TRX_STATE_PREPARED: case TRX_STATE_PREPARED_RECOVERED: case TRX_STATE_COMMITTED_IN_MEMORY: break;
}
ut_error;
}
/** Startatransactionforinternalprocessing. @paramtrxtransaction
@param read_write whether writes may be performed */ void trx_start_internal_low(trx_t *trx, bool read_write)
{
trx->will_lock= true;
trx_start_low(trx, read_write);
}
/** Start a transaction for a DDL operation.
@param trx transaction */ void trx_start_for_ddl_low(trx_t *trx)
{ /* Flag this transaction as a dictionary operation, so that
the data dictionary will be locked in crash recovery. */
trx->dict_operation= true;
trx_start_internal_low(trx, true);
}
/*************************************************************//**
Set the transaction as a read-write transaction if it is not already
tagged as such. Read-only transactions that are writing to temporary
tables are assigned an ID and a rollback segment but are not added
to the trx read-write list because their updates should not be visible
to other transactions and therefore their changes can be ignored by
by MVCC. */ void
trx_set_rw_mode( /*============*/
trx_t* trx) /*!< in/out: transaction that is RW */
{
ut_ad(trx->rsegs.m_redo.rseg == 0);
ut_ad(!trx->is_autocommit_non_locking());
ut_ad(!trx->read_only);
ut_ad(trx->id == 0);
if (high_level_read_only || recv_sys.rpo) { return;
}
trx_assign_rseg_low(trx);
/* So that we can see our own changes. */ if (trx->read_view.is_open()) {
trx->read_view.set_creator_trx_id(trx->id);
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.61 Sekunden
(vorverarbeitet am 2026-10-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.