switch (type) { case MTR_MEMO_S_LOCK: static_cast<index_lock*>(object)->s_unlock(); break; case MTR_MEMO_X_LOCK: case MTR_MEMO_SX_LOCK: static_cast<index_lock*>(object)->
u_or_x_unlock(type == MTR_MEMO_SX_LOCK); break; case MTR_MEMO_SPACE_X_LOCK: static_cast<fil_space_t*>(object)->set_committed_size(); static_cast<fil_space_t*>(object)->x_unlock(); break; default:
buf_page_t *bpage= static_cast<buf_page_t*>(object);
ut_d(constauto s=)
bpage->unfix();
ut_ad(s < buf_page_t::READ_FIX || s >= buf_page_t::WRITE_FIX); switch (type) { case MTR_MEMO_PAGE_S_FIX:
bpage->lock.s_unlock(); break; case MTR_MEMO_BUF_FIX: break; default:
ut_ad(type == MTR_MEMO_PAGE_SX_FIX ||
type == MTR_MEMO_PAGE_X_FIX ||
type == MTR_MEMO_PAGE_SX_MODIFY ||
type == MTR_MEMO_PAGE_X_MODIFY);
bpage->lock.u_or_x_unlock(type & MTR_MEMO_PAGE_SX_FIX);
}
}
}
/** Prepare to insert a modified blcok into flush_list. @paramlsnstartLSNofthemini-transaction
@return insert position for insert_into_flush_list() */ inline buf_page_t *buf_pool_t::prepare_insert_into_flush_list(lsn_t lsn)
noexcept
{
ut_ad(recv_recovery_is_on() || log_sys.latch_have_any());
ut_ad(lsn >= log_sys.last_checkpoint_lsn);
mysql_mutex_assert_owner(&flush_list_mutex);
static_assert(log_t::FIRST_LSN >= 2, "compatibility");
rescan:
buf_page_t *prev= UT_LIST_GET_FIRST(flush_list); if (prev)
{
lsn_t om= prev->oldest_modification(); if (om == 1)
{
delete_from_flush_list(prev); goto rescan;
}
ut_ad(om > 2); if (om <= lsn) return nullptr; while (buf_page_t *next= UT_LIST_GET_NEXT(list, prev))
{
om= next->oldest_modification(); if (om == 1)
{
delete_from_flush_list(next); continue;
}
ut_ad(om > 2); if (om <= lsn) break;
prev= next;
}
flush_hp.adjust(prev);
} return prev;
}
/** Insert a modified block into the flush list. @paramprevinsertposition(fromprepare_insert_into_flush_list()) @paramblockmodifiedblock
@param lsn start LSN of the mini-transaction that modified the block */ inlinevoid buf_pool_t::insert_into_flush_list(buf_page_t *prev,
buf_block_t *block, lsn_t lsn)
noexcept
{
ut_ad(!fsp_is_system_temporary(block->page.id().space()));
mysql_mutex_assert_owner(&flush_list_mutex);
/** Handle any pages that were freed during the mini-transaction. */ void mtr_t::process_freed_pages()
{ if (m_freed_pages)
{
ut_ad(!m_freed_pages->empty());
ut_ad(m_freed_space);
ut_ad(m_freed_space->is_owner());
ut_ad(is_named_space(m_freed_space));
/* Update the last freed lsn */
m_freed_space->freed_range_mutex.lock();
m_freed_space->update_last_freed_lsn(m_commit_lsn); if (!m_trim_pages) for (constauto &range : *m_freed_pages)
m_freed_space->add_free_range(range); else
m_freed_space->clear_freed_ranges();
m_freed_space->freed_range_mutex.unlock();
ATTRIBUTE_COLD __attribute__((noinline)) /** Insert a modified block into buf_pool.flush_list on IMPORT TABLESPACE. */ staticvoid insert_imported(buf_block_t *block)
{ if (block->page.oldest_modification() <= 1)
{
log_sys.latch.wr_lock(); /* For unlogged mtrs (MTR_LOG_NO_REDO), we use the current system LSN. The mtrthatgeneratedtheLSNiseitheralreadycommittedorinmtr_t::commit. Sharedlatchandrelaxedatomicsshouldbefinehereasitisguaranteed thatboththecurrentmtrandthemtrthatgeneratedtheLSNwouldhave addedthedirtypagestoflushlistbeforeweaccesstheminimumLSNduring checkpoint.log_checkpoint_low()acquiresexclusivelog_sys.latchbefore
commencing. */ const lsn_t lsn= log_sys.get_lsn();
mysql_mutex_lock(&buf_pool.flush_list_mutex);
buf_pool.insert_into_flush_list
(buf_pool.prepare_insert_into_flush_list(lsn), block, lsn);
log_sys.latch.wr_unlock();
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
}
}
/** Release modified pages when no log was written. */ void mtr_t::release_unlogged()
{
ut_ad(m_log_mode == MTR_LOG_NO_REDO);
ut_ad(m_log.empty());
process_freed_pages();
for (auto it= m_memo.rbegin(); it != m_memo.rend(); it++)
{
mtr_memo_slot_t &slot= *it;
ut_ad(slot.object); switch (slot.type) { case MTR_MEMO_S_LOCK: static_cast<index_lock*>(slot.object)->s_unlock(); break; case MTR_MEMO_SPACE_X_LOCK: static_cast<fil_space_t*>(slot.object)->set_committed_size(); static_cast<fil_space_t*>(slot.object)->x_unlock(); break; case MTR_MEMO_X_LOCK: case MTR_MEMO_SX_LOCK: static_cast<index_lock*>(slot.object)->
u_or_x_unlock(slot.type == MTR_MEMO_SX_LOCK); break; default:
buf_block_t *block= static_cast<buf_block_t*>(slot.object);
ut_d(constauto s=) block->page.unfix();
ut_ad(s >= buf_page_t::FREED);
ut_ad(s < buf_page_t::READ_FIX);
/** Commit a mini-transaction. */ void mtr_t::commit()
{
ut_ad(is_active());
/* This is a dirty read, for debugging. */
ut_ad(!m_modifications || !recv_no_log_write);
ut_ad(!m_modifications || m_log_mode != MTR_LOG_NONE);
ut_ad(!m_latch_ex);
if (m_modifications && (m_log_mode == MTR_LOG_NO_REDO || !m_log.empty()))
{ if (UNIV_UNLIKELY(!is_logged()))
{
release_unlogged(); goto func_exit;
}
if (checkpoint_lsn)
DBUG_PRINT("ib_log",
("FILE_CHECKPOINT(" LSN_PF ") written at " LSN_PF,
checkpoint_lsn, m_commit_lsn));
return m_commit_lsn;
}
#ifdef UNIV_DEBUG /** Check if a tablespace is associated with the mini-transaction (neededforgeneratingaFILE_MODIFYrecord) @param[in]spacetablespace
@return whether the mini-transaction is associated with the space */ bool
mtr_t::is_named_space(uint32_t space) const
{
ut_ad(!m_user_space || m_user_space->id != TRX_SYS_SPACE); return !is_logged() || m_user_space_id == space ||
is_predefined_tablespace(space);
} /** Check if a tablespace is associated with the mini-transaction (neededforgeneratingaFILE_MODIFYrecord) @param[in]spacetablespace
@return whether the mini-transaction is associated with the space */ bool mtr_t::is_named_space(const fil_space_t* space) const
{
ut_ad(!m_user_space || m_user_space->id != TRX_SYS_SPACE);
/** Display a warning that the log tail is overwriting the head,
making the server crash-unsafe. */
ATTRIBUTE_COLD staticvoid log_overwrite_warning(lsn_t lsn)
{ if (log_sys.overwrite_warned || log_sys.archive) return;
time_t t= time(nullptr); if (difftime(t, log_close_warn_time) < 15) return;
if (!log_sys.overwrite_warned)
log_sys.overwrite_warned= lsn;
log_close_warn_time= t;
sql_print_error("InnoDB: Crash recovery is broken due to" " insufficient innodb_log_file_size;" " last checkpoint LSN=" LSN_PF ", current LSN=" LSN_PF "%s.",
lsn_t{log_sys.last_checkpoint_lsn}, lsn,
srv_shutdown_state > SRV_SHUTDOWN_INITIATED
? ". Shutdown is in progress" : "");
}
ATTRIBUTE_COLD void log_t::append_prepare_wait(bool late, bool ex) noexcept
{ if (UNIV_LIKELY(!ex))
{
latch.rd_unlock(); if (!late)
{ /* Wait for all threads to back off. */
latch.wr_lock(); goto got_ex;
}
for (;;)
{
HMT_low(); for (auto r= rounds + 1; r--; )
{ if (write_lsn_offset.load(std::memory_order_relaxed) & WRITE_BACKOFF)
{ for (auto d= delay; d--; )
MY_RELAX_CPU();
} else
{
HMT_medium(); goto done;
}
}
HMT_medium();
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
} else
{
got_ex: const uint64_t l= write_lsn_offset.load(std::memory_order_relaxed); const lsn_t lsn= base_lsn.load(std::memory_order_relaxed) +
(l & (WRITE_BACKOFF - 1));
waits++; #ifdef HAVE_PMEM constbool is_pmem{is_mmap()}; if (is_pmem)
{
ut_ad(is_mmap_writeable());
ut_ad(!archive);
ut_ad(lsn - get_flushed_lsn(std::memory_order_relaxed) < capacity() ||
overwrite_warned);
persist(lsn);
} #endif
latch.wr_unlock(); /* write_buf() or persist() will clear the WRITE_BACKOFF flag,
which our caller will recheck. */ #ifdef HAVE_PMEM if (!is_pmem) #endif
log_write_up_to(lsn, false); if (ex)
{
latch.wr_lock(); return;
}
}
done:
latch.rd_lock();
}
/** Reserve space in the log buffer for appending data. @tparammodehowtowritelog @paramsizetotallengthofthedatatoappend(),inbytes @paramexwhetherlog_sys.latchisexclusivelylocked
@return the start LSN and the buffer position for append() */ template<log_t::write mode> inline
std::pair<lsn_t,byte*> log_t::append_prepare(size_t size, bool ex) noexcept
{
ut_ad(ex ? latch_have_wr() : latch_have_rd());
static_assert(!bool(WRITE_NORMAL), "");
static_assert(bool(CIRCULAR_MMAP), "");
static_assert(mode == WRITE_NORMAL || mode == CIRCULAR_MMAP, "");
ut_ad(bool(mode) == is_mmap());
ut_ad(is_mmap() == is_mmap_writeable());
uint64_t l;
static_assert(WRITE_TO_BUF == WRITE_BACKOFF << 1, ""); while (UNIV_UNLIKELY((l= write_lsn_offset.fetch_add(size + WRITE_TO_BUF) &
(WRITE_TO_BUF - 1)) >=
(mode ? capacity() : buf_size) - size))
{ /* The following is inlined here instead of being part of append_prepare_wait(),inordertoincreasethelocalityofreference
and to set the WRITE_BACKOFF flag as soon as possible. */ bool late(write_lsn_offset.fetch_or(WRITE_BACKOFF) & WRITE_BACKOFF); /* Subtract our LSN overshoot. */
write_lsn_offset.fetch_sub(size);
append_prepare_wait(late, ex);
}
/** Finish appending data to the log. @paramlsntheendLSNofthelogrecord @returnlsnforinvokingbuf_flush_ahead()on,with"furious"flagintheLSB
@retval 0 if buf_flush_ahead() will not have to be invoked */ static lsn_t log_close(lsn_t lsn) noexcept
{
ut_ad(log_sys.latch_have_any());
if (UNIV_UNLIKELY(checkpoint_age >= log_sys.log_capacity) && /* silence message on create_log_file() after the log had been deleted */
checkpoint_age != lsn)
log_overwrite_warning(lsn); elseif (UNIV_LIKELY(checkpoint_age <= max_age)) return0;
/* The last checkpoint is too old. Let us set an appropriate checkpointagetarget,thatis,acheckpointLSNtargetthatisthe currentLSNminusthemaximumage.Letusseeifareexceedingthe log_t::checkpoint_margin()limitthatwillinvolveasynchronouswait
in each write operation. */
/* If furious==true, we could set a less aggressive target (lsn-log_sys.max_checkpoint_age)insteadofwhatwewillbeusing inbothcases(lsn-log_sys.max_checkpoint_age_async).
if (UNIV_LIKELY_NULL(bpage.zip.data))
{
size= (UNIV_ZIP_SIZE_MIN >> 1) << bpage.zip.ssize; switch (fil_page_get_type(bpage.zip.data)) { case FIL_PAGE_TYPE_ALLOCATED: case FIL_PAGE_INODE: case FIL_PAGE_IBUF_BITMAP: case FIL_PAGE_TYPE_FSP_HDR: case FIL_PAGE_TYPE_XDES: /* These are essentially uncompressed pages. */ break; default:
page= bpage.zip.data;
}
}
/* We have to exclude from the checksum the normal pagechecksumthatiswrittenbybuf_flush_init_for_writing() andFIL_PAGE_LSNwhichwouldbeupdatedoncewehaveactually allocatedtheLSN.
#ifdef HAVE_PMEM if (!resize_flush_buf)
{
ut_ad(is_mmap());
resize_wrap_mutex.wr_lock(); const size_t resize_capacity{resize_target - START_OFFSET};
{ const lsn_t resizing{resize_in_progress()}; /* For memory-mapped log, log_t::resize_start() would never setlog_sys.resize_lsntolessthanlog_sys.lsn.Itcannot executeconcurrentlywiththisthread,becauseweareholding
log_sys.latch and it would hold an exclusive log_sys.latch. */ if (UNIV_UNLIKELY(lsn < resizing))
{ /* This function may execute in multiple concurrent threads thatholdasharedlog_sys.latch.Beforewegotresize_wrap_mutex, anotherthreadcouldhaveexecutedresize_lsn.store(lsn)below withalargerlsnthanours.
if (UNIV_UNLIKELY(end < &buf[START_OFFSET]))
{ /* The source buffer (log_sys.buf) wrapped around */
ut_ad(end + capacity() < &buf[file_size]);
ut_ad(end + len >= &buf[START_OFFSET]);
ut_ad(end + capacity() + len >= &buf[file_size]);
size_t l= size_t(buf - (end - START_OFFSET));
memcpy(resize_buf + s, end + capacity(), l);
memcpy(resize_buf + s + l, &buf[START_OFFSET], len - l);
} else
{
ut_ad(end + len <= &buf[file_size]);
memcpy(resize_buf + s, end, len);
}
s+= len - seq;
/* Always set the sequence bit. If the resized log were to wrap around,
we will advance resize_lsn. */
ut_ad(resize_buf[s] <= 1);
resize_buf[s]= 1;
mmap_done:
resize_wrap_mutex.wr_unlock();
} else #endif
{
ut_ad(resize_flush_buf);
s= end - buf;
ut_ad(s + len <= buf_size);
memcpy(resize_buf + s, end, len);
s+= len - seq; /* Always set the sequence bit. If the resized log were to wrap around,
we will advance resize_lsn. */
ut_ad(resize_buf[s] <= 1);
resize_buf[s]= 1;
}
}
}
#ifdef UNIV_DEBUG /** Check if we are holding an rw-latch in this mini-transaction @paramlocklatchtosearchfor @paramtypeheldlatchtype
@return whether (lock,type) is contained */ bool mtr_t::memo_contains(const index_lock &lock, mtr_memo_type_t type) const
{
ut_ad(type == MTR_MEMO_X_LOCK || type == MTR_MEMO_S_LOCK ||
type == MTR_MEMO_SX_LOCK);
for (const mtr_memo_slot_t &slot : m_memo)
{ if (slot.object == &lock && slot.type == type)
{ switch (type) { case MTR_MEMO_X_LOCK:
ut_ad(lock.have_x()); break; case MTR_MEMO_SX_LOCK:
ut_ad(lock.have_u_or_x()); break; case MTR_MEMO_S_LOCK:
ut_ad(lock.have_s()); break; default: break;
} returntrue;
}
}
returnfalse;
}
/** Check if memo contains the given item. @paramobjectobjecttosearch @paramflagsspecifytypesofobject(canbeORred)of MTR_MEMO_PAGE_S_FIX...values
@return true if contains */ bool mtr_t::memo_contains_flagged(constvoid *object, ulint flags) const
{
ut_ad(is_active());
ut_ad(flags); /* Look for rw-lock-related and page-related flags. */
ut_ad(!(flags & ulint(~(MTR_MEMO_PAGE_S_FIX | MTR_MEMO_PAGE_X_FIX |
MTR_MEMO_PAGE_SX_FIX | MTR_MEMO_BUF_FIX |
MTR_MEMO_MODIFY | MTR_MEMO_X_LOCK |
MTR_MEMO_SX_LOCK | MTR_MEMO_S_LOCK)))); /* Either some rw-lock-related or page-related flags
must be specified, but not both at the same time. */
ut_ad(!(flags & (MTR_MEMO_PAGE_S_FIX | MTR_MEMO_PAGE_X_FIX |
MTR_MEMO_PAGE_SX_FIX | MTR_MEMO_BUF_FIX |
MTR_MEMO_MODIFY)) ==
!!(flags & (MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK | MTR_MEMO_S_LOCK)));
for (const mtr_memo_slot_t &slot : m_memo)
{ if (object != slot.object) continue;
/** Mark the given latched page as modified.
@param block page that will be modified */ void mtr_t::set_modified(const buf_block_t &block)
{ if (block.page.id().space() >= SRV_TMP_SPACE_ID)
{ const_cast<buf_block_t&>(block).page.set_temp_modified(); return;
}
m_modifications= true;
if (UNIV_UNLIKELY(m_log_mode == MTR_LOG_NONE)) return;
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.