/** Monotonic-max lock-free bump. @paramlsnnewtarget
@return whether the target was bumped */ bool bump(lsn_t lsn) noexcept
{
lsn_t snapshot= m_lsn.load(); while (snapshot < lsn) if (m_lsn.compare_exchange_weak(snapshot, lsn)) returntrue; returnfalse;
}
/** Clear via snapshot+CAS iff the freshly snapshotted target is at mostthreshold;aconcurrentbump()betweenthesnapshotandtheCAS ispreservedacrosstheclear.
@param threshold clear iff the fresh snapshot is at most this value */ void try_clear_if_at_most(lsn_t threshold) noexcept
{
lsn_t snapshot= m_lsn.load(); if (!snapshot) return; /* already cleared: avoid a redundant atomic CAS */ if (threshold >= snapshot)
m_lsn.compare_exchange_strong(snapshot, 0);
}
/** Clear via CAS using the caller's expected value; a concurrent bump()thatmovedthetargetawayfromexpectedispreservedacross
the clear. */ void try_clear(lsn_t expected) noexcept
{ m_lsn.compare_exchange_strong(expected, 0); }
};
} /** Target oldest_modification for the page cleaner background flushing. Bumpedlock-freeviamonotonic-maxCAS-loopfrombuf_flush_ahead(); clearedwithCASunderbuf_pool.flush_list_mutexfrom
buf_flush_page_cleaner() and buf_flush_sync_for_checkpoint(). */ static async_flush_lsn buf_flush_async_lsn; /** Target oldest_modification for the page cleaner furious flushing;
writes are protected by buf_pool.flush_list_mutex */ static Atomic_relaxed<lsn_t> buf_flush_sync_lsn;
/** Page cleaner structure */ staticstruct
{ /** total elapsed time in adaptive flushing, in seconds */
ulint flush_time; /** number of adaptive flushing passes */
ulint flush_pass;
} page_cleaner;
/** Validates the flush list some of the time. */ staticvoid buf_flush_validate_skip() noexcept
{ /** Try buf_flush_validate_low() every this many times */ # define BUF_FLUSH_VALIDATE_SKIP 23
/** The buf_flush_validate_low() call skip counter.
Use a signed type because of the race condition below. */ staticint buf_flush_validate_count = BUF_FLUSH_VALIDATE_SKIP;
/* There is a race condition below, but it does not matter, becausethiscallisonlyforheuristicpurposes.Wewantto reducethecallfrequencyofthecostlybuf_flush_validate_low()
check in debug builds. */ if (--buf_flush_validate_count > 0) { return;
}
void buf_pool_t::page_cleaner_wakeup(bool for_LRU) noexcept
{
mysql_mutex_assert_owner(&flush_list_mutex);
ut_d(buf_flush_validate_skip()); if (!page_cleaner_idle())
{ if (for_LRU) /* Ensure that the page cleaner is not in a timed wait. */
pthread_cond_signal(&do_flush_list); return;
} double dirty_pct= double(UT_LIST_GET_LEN(buf_pool.flush_list)) * 100.0 / double(UT_LIST_GET_LEN(buf_pool.LRU) + UT_LIST_GET_LEN(buf_pool.free)); double pct_lwm= srv_max_dirty_pages_pct_lwm;
/* if pct_lwm != 0.0, adaptive flushing is enabled. signalbufpagecleanerthread -ifpct_lwm<=dirty_pctthenitwillinvokeadaptiveflushingflow -ifpct_lwm>dirty_pctthenitwillinvokeidleflushingflow.
Howtoavoidthis? -byallowinglast_activity_counttoupdatedwhenpage-cleanerismade activeandhasworktodo.Thisensuresthatthelast_activitysignal
is consumed by the page-cleaner before the next one is generated. */ if (for_LRU ||
(pct_lwm != 0.0 && (pct_lwm <= dirty_pct ||
last_activity_count == srv_get_activity_count())) ||
srv_max_buf_pool_modified_pct <= dirty_pct)
{
page_cleaner_idle_flag= false;
pthread_cond_signal(&do_flush_list);
}
}
/** Remove all dirty pages belonging to a given tablespace when we are deletingthedatafileofthattablespace. ThepagesstillremainapartofLRUandareevictedfrom thelistastheyagetowardsthetailoftheLRU.
@param id tablespace identifier */ void buf_flush_remove_pages(uint32_t id) noexcept
{ const page_id_t first(id, 0), end(id + 1, 0);
ut_ad(id);
for (;;)
{
mysql_mutex_lock(&buf_pool.mutex); bool deferred= false;
/*******************************************************************//**
Relocates a buffer control block on the flush_list.
Note that it is assumed that the contents of bpage have already been
copied to dpage.
IMPORTANT: When this function is called bpage and dpage are not
exact copies of each other. For example, they both will have different
::state. Also the ::list pointers in dpage may be stale. We need to
use the current list node (bpage) to do the list manipulation because
the list pointers could have changed between the time that we copied
the contents of bpage to the dpage and the flush list manipulation
below. */
ATTRIBUTE_COLD void
buf_flush_relocate_on_flush_list( /*=============================*/
buf_page_t* bpage, /*!< in/out: control block being moved */
buf_page_t* dpage) /*!< in/out: destination block */
noexcept
{
buf_page_t* prev;
if (UNIV_LIKELY(!error))
{
ut_d(lsn_t om= oldest_modification());
ut_ad(om >= 2);
ut_ad(persistent == (om > 2)); /* We use release memory order to guarantee that callers of oldest_modification_acquire()willobservetheblockas
being detached from buf_pool.flush_list, after reading the value 0. */
oldest_modification_.store(persistent, std::memory_order_release);
}
zip.fix.fetch_sub((state >= WRITE_FIX_REINIT)
? (WRITE_FIX_REINIT - UNFIXED)
: (WRITE_FIX - UNFIXED));
lock.u_unlock(true);
}
/** Complete write of a file page from buf_pool. @paramrequestwriterequest
@param error whether the write may have failed */ void buf_page_write_complete(const IORequest &request, bool error) noexcept
{
ut_ad(request.is_write());
ut_ad(!srv_read_only_mode);
buf_page_t *bpage= request.bpage;
ut_ad(bpage); constauto state= bpage->state(); /* io-fix can only be cleared by buf_page_t::write_complete()
and buf_page_t::read_complete() */
ut_ad(state >= buf_page_t::WRITE_FIX);
ut_ad(!buf_dblwr.is_inside(bpage->id()));
ut_ad(request.node->space->id == bpage->id().space());
if (request.slot)
request.slot->release();
if (UNIV_UNLIKELY(MONITOR_IS_ON(MONITOR_MODULE_BUF_PAGE)))
buf_page_monitor(*bpage, false);
DBUG_PRINT("ib_buf", ("write page %u:%u",
bpage->id().space(), bpage->id().page_no()));
if (UNIV_UNLIKELY(!persistent) && UNIV_LIKELY(!error))
{ /* We must hold buf_pool.mutex while releasing the block, so that
no other thread can access it before we have freed it. */
mysql_mutex_lock(&buf_pool.mutex);
bpage->write_complete(persistent, error, state);
buf_LRU_free_page(bpage, true);
mysql_mutex_unlock(&buf_pool.mutex);
} else
{
bpage->write_complete(persistent, error, state); if (request.is_doublewritten())
{
ut_ad(state < buf_page_t::WRITE_FIX_REINIT);
ut_ad(persistent);
buf_dblwr.write_completed();
}
}
}
/** Initialize a page for writing to the tablespace. @param[in]blockbufferblock;NULLifbypassing thebufferpool @param[in,out]pagepageframe @param[in,out]page_zip_compressedpage,orNULLif uncompressed
@param[in] use_full_checksum whether tablespace uses full checksum */ void
buf_flush_init_for_writing( const buf_block_t* block,
byte* page, void* page_zip_, bool use_full_checksum) noexcept
{ if (block && block->page.frame != page) { /* If page is encrypted in full crc32 format then
checksum stored already as a part of fil_encrypt_buf() */
ut_ad(use_full_checksum); return;
}
switch (fil_page_get_type(page)) { 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. */
memcpy(page_zip->data, page, size); /* fall through */ case FIL_PAGE_TYPE_ZBLOB: case FIL_PAGE_TYPE_ZBLOB2: case FIL_PAGE_INDEX: case FIL_PAGE_RTREE:
buf_flush_update_zip_checksum(page_zip->data, size); return;
}
ib::error() << "The compressed page to be written" " seems corrupt:";
ut_print_buf(stderr, page, size);
fputs("\nInnoDB: Possibly older version of the page:", stderr);
ut_print_buf(stderr, page_zip->data, size);
putc('\n', stderr);
ut_error;
}
if (block && srv_page_size == 16384) { /* The page type could be garbage in old files createdbeforeMySQL5.5.Suchfilesalways
had a page size of 16 kilobytes. */
uint16_t page_type = fil_page_get_type(page);
uint16_t reset_type = page_type;
switch (block->page.id().page_no() % 16384) { case0:
reset_type = block->page.id().page_no() == 0
? FIL_PAGE_TYPE_FSP_HDR
: FIL_PAGE_TYPE_XDES; break; case1:
reset_type = FIL_PAGE_IBUF_BITMAP; break; case FSP_TRX_SYS_PAGE_NO: if (block->page.id()
== page_id_t(TRX_SYS_SPACE, TRX_SYS_PAGE_NO)) {
reset_type = FIL_PAGE_TYPE_TRX_SYS; break;
} /* fall through */ default: switch (page_type) { case FIL_PAGE_INDEX: case FIL_PAGE_TYPE_INSTANT: case FIL_PAGE_RTREE: case FIL_PAGE_UNDO_LOG: case FIL_PAGE_INODE: case FIL_PAGE_IBUF_FREE_LIST: case FIL_PAGE_TYPE_ALLOCATED: case FIL_PAGE_TYPE_SYS: case FIL_PAGE_TYPE_TRX_SYS: case FIL_PAGE_TYPE_BLOB: case FIL_PAGE_TYPE_ZBLOB: case FIL_PAGE_TYPE_ZBLOB2: break; case FIL_PAGE_TYPE_FSP_HDR: case FIL_PAGE_TYPE_XDES: case FIL_PAGE_IBUF_BITMAP: /* These pages should have predeterminedpagenumbers
(see above). */ default:
reset_type = FIL_PAGE_TYPE_UNKNOWN; break;
}
}
if (UNIV_UNLIKELY(page_type != reset_type)) {
ib::info()
<< "Resetting invalid page "
<< block->page.id() << " type "
<< page_type << " to "
<< reset_type << " when flushing.";
fil_page_set_type(page, reset_type);
}
}
/** Encryption and page_compression hook that is called just before apageiswrittentodisk. @param[in,out]spacetablespace @param[in,out]bpagebufferpage @param[in]sphysicalpageframethatisbeingencrypted @param[in,out]sizepayloadsizeinbytes @returnpageframetobewrittentofile
(may be src_frame or an encrypted/compressed copy of it) */ static byte *buf_page_encrypt(fil_space_t *space, buf_page_t *bpage, byte *s,
buf_tmp_buffer_t **slot, size_t *size) noexcept
{
ut_ad(!bpage->is_freed());
ut_ad(space->id == bpage->id().space());
ut_ad(!*slot);
const uint32_t page_no= bpage->id().page_no();
switch (page_no) { case TRX_SYS_PAGE_NO: if (bpage->id().space() != TRX_SYS_SPACE) break; /* The TRX_SYS page is neither encrypted nor compressed, because
it contains the address of the doublewrite buffer. */ /* fall through */ case0: /* Page 0 of a tablespace is not encrypted/compressed */ return s;
}
/** Write a flushable page to a file or free a freeable block. @paramspacetablespace
@return whether a page write was initiated and buf_pool.mutex released */ bool buf_page_t::flush(fil_space_t *space) noexcept
{
mysql_mutex_assert_not_owner(&buf_pool.flush_list_mutex);
ut_ad(in_file());
ut_ad(in_LRU_list);
ut_ad((space->is_temporary()) == (space == fil_system.temp_space));
ut_ad(space->referenced());
/** Check whether a page can be flushed from the buf_pool. @paramidpageidentifier @paramfoldid.fold()
@return whether the page can be flushed */ staticbool buf_flush_check_neighbor(const page_id_t id, ulint fold) noexcept
{
mysql_mutex_assert_owner(&buf_pool.mutex);
ut_ad(fold == id.fold());
/* FIXME: cell_get() is being invoked while holding buf_pool.mutex */ const buf_page_t *bpage=
buf_pool.page_hash.get(id, buf_pool.page_hash.cell_get(fold));
/** Check which neighbors of a page can be flushed from the buf_pool. @paramspacetablespace @paramidpageidentifierofadirtypage @paramcontiguouswhethertoconsidercontiguousareasofpages
@return last page number that can be flushed */ static page_id_t buf_flush_check_neighbors(const fil_space_t &space,
page_id_t &id, bool contiguous)
noexcept
{
ut_ad(id.page_no() < space.size +
(space.physical_size() == 2048 ? 1
: space.physical_size() == 1024 ? 3 : 0)); /* When flushed, dirty blocks are searched in neighborhoods of this
size, and flushed along with the original page. */ const ulint s= buf_pool.curr_size() / 16; const uint32_t read_ahead= buf_pool.read_ahead_area; const uint32_t buf_flush_area= read_ahead > s
? static_cast<uint32_t>(s) : read_ahead;
page_id_t low= id - (id.page_no() % buf_flush_area);
page_id_t high= low + buf_flush_area;
high.set_page_no(std::min(high.page_no(), space.last_page_number()));
/* Determine the contiguous dirty area around id. */ const ulint id_fold= id.fold();
mysql_mutex_lock(&buf_pool.mutex);
if (id > low)
{
ulint fold= id_fold; for (page_id_t i= id - 1;; --i)
{
fold--; if (!buf_flush_check_neighbor(i, fold))
{
low= i + 1; break;
} if (i == low) break;
}
}
page_id_t i= id;
id= low;
ulint fold= id_fold; while (++i < high)
{
++fold; if (!buf_flush_check_neighbor(i, fold)) break;
}
mysql_mutex_unlock(&buf_pool.mutex); return i;
}
MY_ATTRIBUTE((warn_unused_result)) /** Apply freed_ranges to the file. @paramwritablewhetherthefileiswritable
@return number of pages written or hole-punched */
uint32_t fil_space_t::flush_freed(bool writable) noexcept
{
mysql_mutex_assert_not_owner(&buf_pool.flush_list_mutex);
mysql_mutex_assert_not_owner(&buf_pool.mutex);
constbool punch_hole= chain.start->punch_hole == 1; if (!punch_hole && !srv_immediate_scrub_data_uncompressed) return0; if (srv_is_undo_tablespace(id)) /* innodb_undo_log_truncate=ON can take care of these better */ return0;
for (;;)
{
freed_range_mutex.lock(); if (freed_ranges.empty())
{
freed_range_mutex.unlock(); return0;
} const lsn_t flush_lsn= last_freed_lsn; if (log_sys.get_flushed_lsn() >= flush_lsn) break;
freed_range_mutex.unlock();
log_write_up_to(flush_lsn, true);
}
for (ulint id_fold= id.fold(); id < high; ++id, ++id_fold)
{ if (UNIV_UNLIKELY(space->is_stopping_writes()))
{ if (bpage)
bpage->lock.u_unlock(true); break;
}
if (count + n_flushed >= n_to_flush)
{ if (id > page_id) break; /* If the page whose neighbors we are flushing has not been
flushed yet, we must flush the page that we selected originally. */
id= page_id;
id_fold= id.fold();
}
/*******************************************************************//** This utility moves the uncompressed frames of pages to the free list.
Note that this function does not actually flush any data to disk. It
just detaches the uncompressed frames from the compressed pages at the
tail of the unzip_LRU and puts those freed frames in the free list.
@return number of blocks moved to the free list. */ static ulint buf_free_from_unzip_LRU_list_batch() noexcept
{
ulint scanned = 0;
ulint count = 0;
/** Start writing out pages for a tablespace. @paramidtablespaceidentifier
@return tablespace and number of pages written */ static std::pair<fil_space_t*, uint32_t> buf_flush_space(const uint32_t id)
noexcept
{ if (fil_space_t *space= fil_space_t::get_for_write(id)) return {space, space->flush_freed(true)}; return {nullptr, 0};
}
struct flush_counters_t
{ /** number of dirty pages flushed */
ulint flushed; /** number of clean pages evicted */
ulint evicted;
};
/** Discard a dirty page, and release buf_pool.flush_list_mutex.
@param bpage dirty page whose tablespace is not accessible */ staticvoid buf_flush_discard_page(buf_page_t *bpage) noexcept
{
ut_ad(bpage->in_file());
ut_ad(bpage->oldest_modification());
/* BUF_LRU_MIN_LEN (256) is too high value for low buffer pool(BP) size. For example,forBPsizelowerthan80Mand16Kpagesize,thelimitismorethan 5%oftotalBPandforlowestBP6M,itis80%oftheBP.Non-dataobjects likeexplicitlockscouldoccupypartoftheBPpoolreducingthepages availableforLRU.IfLRUreachesminimumlimitandifnofreepagesare available,serverwouldhangwithpagecleanernotabletofreeanymore pages.Toavoidsuchhang,weadjusttheLRUlimitlowerthanthelimitfor dataobjectsascheckedinbuf_LRU_check_size_of_non_data_objects()i.e.one
page less than 5% of BP. */ const size_t buf_lru_min_len=
std::min((buf_pool.usable_size()) / 20 - 1, size_t{BUF_LRU_MIN_LEN});
if (!bpage->oldest_modification())
{
evict: if (state != buf_page_t::FREED &&
(state >= buf_page_t::READ_FIX || (~buf_page_t::LRU_MASK & state))) continue; if (UNIV_UNLIKELY(to_withdraw != 0))
to_withdraw= buf_flush_LRU_to_withdraw(to_withdraw, *bpage);
buf_LRU_free_page(bpage, true);
++n->evicted; if (UNIV_LIKELY(scanned & 31)) continue;
mysql_mutex_unlock(&buf_pool.mutex);
reacquire_mutex:
mysql_mutex_lock(&buf_pool.mutex); continue;
}
if (state < buf_page_t::READ_FIX && bpage->lock.u_lock_try(true))
{
ut_ad(!bpage->is_io_fixed()); switch (bpage->oldest_modification()) { case2: /* LRU flushing will always evict pages of the temporary tablespace,
in buf_page_write_complete(). */
++n->evicted; break; case1:
mysql_mutex_lock(&buf_pool.flush_list_mutex); if (ut_d(lsn_t lsn=) bpage->oldest_modification())
{
ut_ad(lsn == 1); /* It must be clean while we hold bpage->lock */
buf_pool.delete_from_flush_list(bpage);
}
mysql_mutex_unlock(&buf_pool.flush_list_mutex); /* fall through */ case0:
bpage->lock.u_unlock(true); goto evict;
} /* Block is ready for flush. Dispatch an IO request. */ const page_id_t page_id(bpage->id()); const uint32_t space_id= page_id.space(); if (!space || space->id != space_id)
{ if (last_space_id != space_id)
{
buf_pool.lru_hp.set(bpage);
mysql_mutex_unlock(&buf_pool.mutex); if (space)
space->release(); auto p= buf_flush_space(space_id);
space= p.first;
last_space_id= space_id; if (!space)
{
mysql_mutex_lock(&buf_pool.mutex); goto no_space;
}
mysql_mutex_lock(&buf_pool.mutex);
buf_pool.stat.n_pages_written+= p.second;
} else
{
ut_ad(!space); goto no_space;
}
} elseif (space->is_stopping_writes())
{
space->release();
space= nullptr;
no_space:
mysql_mutex_lock(&buf_pool.flush_list_mutex);
buf_flush_discard_page(bpage);
++n->evicted; continue;
}
if (state < buf_page_t::UNFIXED) goto flush;
if (n->flushed >= max && !recv_recovery_is_on())
{
bpage->lock.u_unlock(true); break;
}
if (neighbors && space->is_rotational() && UNIV_LIKELY(!to_withdraw) && /* Skip neighbourhood flush from LRU list if we haven't yet reached
half of the free page target. */
UT_LIST_GET_LEN(buf_pool.free) * 2 >= free_limit)
n->flushed+= buf_flush_try_neighbors(space, page_id, bpage,
neighbors == 1,
n->flushed, max); else
{
flush: if (UNIV_UNLIKELY(to_withdraw != 0))
to_withdraw= buf_flush_LRU_to_withdraw(to_withdraw, *bpage); if (bpage->flush(space))
++n->flushed; else continue;
}
goto reacquire_mutex;
} else /* Can't evict or dispatch this block. Go to previous. */
ut_ad(buf_pool.lru_hp.is_hp(prev));
}
buf_pool.lru_hp.set(nullptr);
if (space)
space->release();
if (scanned)
{
MONITOR_INC_VALUE_CUMULATIVE(MONITOR_LRU_BATCH_SCANNED,
MONITOR_LRU_BATCH_SCANNED_NUM_CALL,
MONITOR_LRU_BATCH_SCANNED_PER_CALL,
scanned);
}
}
/** Flush and move pages from LRU or unzip_LRU list to the free list. WhetherLRUorunzip_LRUisuseddependsonthestateofthesystem. @parammaxmaximumnumberofblockstoflush
@param n counts of flushed and evicted pages */ staticvoid buf_do_LRU_batch(ulint max, flush_counters_t *n) noexcept
{ const size_t to_withdraw= buf_pool.to_withdraw(); if (!to_withdraw && buf_LRU_evict_from_unzip_LRU())
buf_free_from_unzip_LRU_list_batch();
n->evicted= 0;
n->flushed= 0;
buf_flush_LRU_list_batch(max, n, to_withdraw);
/** This utility flushes dirty blocks from the end of the flush_list. Thecallingthreadisnotallowedtoownanylatchesonpages! @parammax_nmaximummumberofblockstoflush @paramlsnonceanoldest_modification>=lsnisfound,terminatethebatch
@return number of blocks for which the write request was queued */ static ulint buf_do_flush_list_batch(ulint max_n, lsn_t lsn) noexcept
{
ulint count= 0;
ulint scanned= 0;
if (oldest_modification == 1)
{
clear:
buf_pool.delete_from_flush_list(bpage);
skip:
bpage= prev; if (scanned & 31) continue; /* Release the buf_pool.flush_list_mutex every now and then inordertoreducethewaittimeinbuf_flush_ahead(). Weattempttopreservethepointerpositionwhileyielding. Anythreadthatwouldremove'prev'frombuf_pool.flush_list
must adjust the hazard pointer. */
buf_pool.flush_hp.set(prev);
mysql_mutex_unlock(&buf_pool.flush_list_mutex); goto next;
}
ut_ad(oldest_modification > 2);
if (!bpage->lock.u_lock_try(true)) goto skip;
ut_ad(!bpage->is_io_fixed());
if (bpage->oldest_modification() == 1)
{
bpage->lock.u_unlock(true); goto clear;
}
/* In order not to degenerate this scan to O(n*n) we attempt to preservethepointerposition.Anythreadthatwouldremove'prev' frombuf_pool.flush_listmustadjustthehazardpointer.
Note:Aconcurrentexecutionofbuf_flush_list_space()may terminatethisscanprematurely.Thebuf_pool.flush_list_active shouldpreventmultiplethreadsfromexecuting buf_do_flush_list_batch()concurrently,
but buf_flush_list_space() is ignoring that. */
buf_pool.flush_hp.set(prev);
}
/** Write out dirty blocks from buf_pool.flush_list. @parammax_nwishedmaximummumberofblocksflushed @paramlsnbuf_pool.get_oldest_modification(LSN_MAX)target @returnthenumberofprocessedpages
@retval 0 if a buf_pool.flush_list batch is already running */ static ulint buf_flush_list(ulint max_n= ULINT_UNDEFINED,
lsn_t lsn= LSN_MAX) noexcept
{
mysql_mutex_lock(&buf_pool.mutex);
ulint n= buf_flush_list_holding_mutex(max_n, lsn);
mysql_mutex_unlock(&buf_pool.mutex);
buf_dblwr.flush_buffered_writes(); return n;
}
/** Try to flush all the dirty pages that belong to a given tablespace. @paramspacetablespace @paramn_flushednumberofpageswritten
@return whether the flush for some pages might not have been initiated */ bool buf_flush_list_space(fil_space_t *space, ulint *n_flushed) noexcept
{ constauto space_id= space->id;
ut_ad(space_id < SRV_SPACE_ID_UPPER_BOUND);
for (buf_page_t *bpage= UT_LIST_GET_LAST(buf_pool.flush_list); bpage; )
{
ut_ad(bpage->oldest_modification());
ut_ad(bpage->in_file());
buf_page_t *prev= UT_LIST_GET_PREV(list, bpage); if (bpage->oldest_modification() == 1)
clear:
buf_pool.delete_from_flush_list(bpage); elseif (bpage->id().space() != space_id); elseif (!bpage->lock.u_lock_try(true))
may_have_skipped= true; elseif (bpage->oldest_modification() == 1)
{
bpage->lock.u_unlock(true); goto clear;
} else
{ /* In order not to degenerate this scan to O(n*n) we attempt to preservethepointerposition.Anythreadthatwouldremove'prev' frombuf_pool.flush_listmustadjustthehazardpointer.
Note:Multipleexecutionsofbuf_flush_list_space()maybe interleaved,andalsobuf_do_flush_list_batch()mayberunning concurrently.Thismayterminateouriterationprematurely,
leading us to return may_have_skipped=true. */
buf_pool.flush_hp.set(prev);
if (!acquired)
was_freed:
buf_flush_discard_page(bpage); else
{ if (space->is_stopping_writes())
{
space->release();
acquired= false; goto was_freed;
}
mysql_mutex_unlock(&buf_pool.flush_list_mutex); if (bpage->flush(space))
{
++n_flush; if (!--max_n_flush)
{
mysql_mutex_lock(&buf_pool.mutex);
mysql_mutex_lock(&buf_pool.flush_list_mutex);
may_have_skipped= true; goto done;
}
mysql_mutex_lock(&buf_pool.mutex);
}
}
mysql_mutex_lock(&buf_pool.flush_list_mutex); if (!buf_pool.flush_hp.is_hp(prev))
may_have_skipped= true;
bpage= buf_pool.flush_hp.get(); continue;
}
bpage= prev;
}
/* Note: this loop may have been executed concurrently with buf_do_flush_list_batch()aswellasotherthreadsexecuting buf_flush_list_space().Weshouldalwaysreturntruefrom buf_flush_list_space()ifthatshouldbethecase;in
buf_do_flush_list_batch() we will simply perform less work. */
done:
buf_pool.flush_hp.set(nullptr);
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
if (space->is_being_imported())
os_aio_wait_until_no_pending_writes(true); else
buf_dblwr.flush_buffered_writes();
return may_have_skipped;
}
/** Write out dirty blocks from buf_pool.LRU, andmovecleanblockstobuf_pool.free. Thecallermustinvokebuf_dblwr.flush_buffered_writes() afterreleasingbuf_pool.mutex. @parammax_nwishedmaximummumberofblocksflushed
@return number of pages written */ static ulint buf_flush_LRU(ulint max_n) noexcept
{
mysql_mutex_assert_owner(&buf_pool.mutex);
flush_counters_t n;
buf_do_LRU_batch(max_n, &n);
ulint pages= n.flushed;
ulint evicted= n.evicted;
/* If we have exhausted flush quota, it is likely we exited before generatingenoughfreepages.Calloncemorewith0flushtogenerate
free pages immediately as required. */ if (pages >= max_n)
buf_do_LRU_batch(0, &n);
evicted+= n.evicted; if (evicted)
{
buf_pool.try_LRU_scan= true;
pthread_cond_broadcast(&buf_pool.done_free);
} elseif (!pages && !buf_pool.try_LRU_scan) /* For example, with the minimum innodb_buffer_pool_size=6M and thedefaultinnodb_page_size=16kthereareonlyalittleover316 pagesinthebufferpool.Thebufferpoolcaneasilybeexhausted byaworkloadofsomedozenconcurrentconnections.Thesystemcould reachadeadlocklikethefollowing:
(1)Manythreadsarewaitinginbuf_LRU_get_free_block() forbuf_pool.done_free. (2)Somethreadsarewaitingforapagelatchwhichisheldby anotherthreadthatiswaitinginbuf_LRU_get_free_block(). (3)Thisthreadistheonlyonethatcouldmakeprogress,but wefailtodosobecauseallthepagesthatwescannedare
buffer-fixed or latched by some thread. */
buf_pool.LRU_warn();
/** Initiate a log checkpoint, discarding the start of the log. @paramoldest_lsnthecheckpointLSN @paramend_lsnlog_sys.get_lsn() @returnthelsntoinvokelog_sys.archive_create(lsn,false)on
@retval 0 if log_sys.archive_create() should not be called */ static lsn_t log_checkpoint_low(lsn_t oldest_lsn, lsn_t end_lsn) noexcept
{
ut_ad(!srv_read_only_mode);
ut_ad(!recv_no_log_write);
ut_ad(log_sys.latch_have_wr());
ut_ad(oldest_lsn <= end_lsn);
ut_ad(end_lsn == log_sys.get_lsn());
if (oldest_lsn == log_sys.last_checkpoint_lsn ||
(oldest_lsn == end_lsn &&
!log_sys.resize_in_progress() &&
(!log_sys.archive || !log_sys.circular_recovery_from_0()) &&
oldest_lsn == log_sys.last_checkpoint_lsn +
log_sys.is_encrypted() * 8 + SIZE_OF_FILE_CHECKPOINT)) if (oldest_lsn != log_sys.get_first_lsn())
{ /* Do nothing, because nothing was logged (other than a
FILE_CHECKPOINT record) since the previous checkpoint. */
log_sys.latch.wr_unlock(); return0;
}
ut_ad(oldest_lsn > log_sys.last_checkpoint_lsn ||
oldest_lsn == log_sys.get_first_lsn()); /* Repeat the FILE_MODIFY records after the checkpoint, in case some logrecordsbetweenthecheckpointandlog_sys.lsnneedthem. Finally,writeaFILE_CHECKPOINTrecord.Redologapplyexpectsto seeaFILE_CHECKPOINTafterthecheckpoint,exceptonclean shutdown,wherethelogwillbeemptyafterthecheckpoint.
Itisimportantthatwewriteouttheredologbeforeanyfurther dirtypagesareflushedtothetablespacefiles.Atthispoint, becauseweholdexclusivelog_sys.latch, mtr_t::commit()inotherthreadswillbeblocked,
and no pages can be added to buf_pool.flush_list. */ const lsn_t flush_lsn{fil_names_clear(oldest_lsn)};
ut_ad(flush_lsn >= end_lsn +
SIZE_OF_FILE_CHECKPOINT + 8 * log_sys.is_encrypted());
ut_ad(flush_lsn > log_sys.get_flushed_lsn());
/** Make a checkpoint. Note that this function does not flush dirty blocksfromthebufferpool:itonlycheckswhatislsnoftheoldest modificationinthepool,andwritesinformationaboutthelsnin logfile.Uselog_make_checkpoint()toflushalsothepool. @returnthelsntoinvokelog_sys.archive_create(lsn,false)on
@retval 0 if log_sys.archive_create() should not be called */ static lsn_t log_checkpoint() noexcept
{
ut_ad(!recv_recovery_is_on());
#ifdefined HAVE_valgrind && !__has_feature(memory_sanitizer) /* The built-in scheduler in Valgrind may neglect some threads for a longtime.UnderValgrind,letusexplicitlywaitforpagewrite completioninordertoavoidaresultdifferenceinthetest
innodb.page_cleaner. */
os_aio_wait_until_no_pending_writes(false); #endif
/** Wait for all dirty pages up to an LSN to be written out. NOTE:Thecallingthreadisnotallowedtoholdanybufferpagelatches! @paramlsnthecheckpointtowaitfor
@param checkpoint whether an empty checkpoint needs to be written */
ATTRIBUTE_COLD void buf_flush_wait(lsn_t lsn, bool checkpoint) noexcept
{
mysql_mutex_assert_owner(&buf_pool.flush_list_mutex);
ut_ad(log_sys.latch_have_wr());
ut_ad(!checkpoint || !recv_sys.rpo);
if (oldest_lsn >= buf_flush_sync_lsn)
{
buf_flush_sync_lsn= 0;
pthread_cond_broadcast(&buf_pool.done_flush_list);
}
}
/** Initiate more eager page flushing if the log checkpoint age is too old. @paramlsnbuf_pool.get_oldest_modification(LSN_MAX)target
@param furious true=furious flushing, false=limit to innodb_io_capacity */
ATTRIBUTE_COLD void buf_flush_ahead(lsn_t lsn, bool furious) noexcept
{
ut_ad(!srv_read_only_mode);
ut_ad(!recv_sys.rpo);
DBUG_EXECUTE_IF("ib_log_checkpoint_avoid_hard", if (!log_sys.archive) return;);
if (!furious)
{ if (!buf_flush_async_lsn.bump(lsn)) return;
/* In non-furious mode, concurrent writes to the log will remain possible,andwearegentlyrequestingbuf_flush_page_cleaner() todomoreworktoavoidalatercallwithfurious=true. Wewillonlywakethebuf_flush_page_cleaner()fromanindefinite my_cond_wait(),butwewillnotdisturbtheregular1-secondsleep.
Ifthedecisiontoreturnearlywas(unlikely)wrong,subsequent wake-sidepaths(includingfurtherbuf_flush_ahead()callsunder
redo-log pressure) are expected to perform the wake eventually. */ if (!buf_pool.page_cleaner_idle()) return;
/* Signal under buf_pool.flush_list_mutex. */
mysql_mutex_lock(&buf_pool.flush_list_mutex); goto wake;
}
/* The furious path forces a log checkpoint via log_sys.set_check_for_checkpoint(true),sothebuf_flush_sync_lsn bump,thecheckpointflag,andthecleanerwakemustallbeobserved
together by any thread synchronising on buf_pool.flush_list_mutex. */ if (buf_flush_sync_lsn < lsn)
{
mysql_mutex_lock(&buf_pool.flush_list_mutex); if (buf_flush_sync_lsn < lsn)
{
buf_flush_sync_lsn= lsn; /* Request any concurrent threads to wait for this batch to complete,
in log_free_check(). */
log_sys.set_check_for_checkpoint(true); /* Immediately wake up buf_flush_page_cleaner(), even when it
is in the middle of a 1-second my_cond_timedwait(). */
wake:
buf_pool.page_cleaner_set_idle(false);
pthread_cond_signal(&buf_pool.do_flush_list);
}
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
}
}
/** Conduct checkpoint-related flushing for innodb_flush_sync=ON, andtrytoinitiatecheckpointsuntilthetargetismet. @paramlsntargetcheckpoint @returnthelsntoinvokelog_sys.archive_create(lsn,false)on
@retval 0 if log_sys.archive_create() should not be called */
ATTRIBUTE_COLD ATTRIBUTE_NOINLINE static lsn_t buf_flush_sync_for_checkpoint(lsn_t lsn) noexcept
{
ut_ad(!srv_read_only_mode);
mysql_mutex_assert_not_owner(&buf_pool.flush_list_mutex);
/* During furious flush, we need to keep generating free pages. Otherwise concurrentmtrscouldbeblockedholdinglatchesforthepagestobeflushed causingdeadlockinrareoccasion.
mysql_mutex_lock(&buf_pool.mutex); /* Confirm that eviction is needed after acquiring buffer pool mutex. */ if (buf_pool.need_LRU_eviction()) /* We intend to only evict pages keeping maximum flush bandwidth for flushlistpagesadvancingcheckpoint.However,iftheLRUtailisfull
of dirty pages, we might need some flushing. */
std::ignore= buf_flush_LRU(srv_io_capacity);
mysql_mutex_unlock(&buf_pool.mutex);
if (srv_shutdown_state > SRV_SHUTDOWN_INITIATED)
{
service_manager_extend_timeout(INNODB_EXTEND_TIMEOUT_INTERVAL, "Waiting to flush the buffer pool");
}
/* After attempting log checkpoint, check if we have reached our target. */ if (measure >= buf_flush_sync_lsn)
buf_flush_sync_lsn= 0; else
buf_flush_async_lsn.try_clear_if_at_most(measure);
log_sys.latch.wr_unlock(); /* wake up buf_flush_wait() */
pthread_cond_broadcast(&buf_pool.done_flush_list);
mysql_mutex_unlock(&buf_pool.flush_list_mutex); return create_spare_archive;
}
/** Check if the adaptive flushing threshold is recommended based on redologcapacityfilledthreshold. @paramoldest_lsnbuf_pool.get_oldest_modification()
@return true if adaptive flushing is recommended. */ staticbool af_needed_for_redo(lsn_t oldest_lsn) noexcept
{ /* We may have oldest_lsn > log_sys.get_lsn_approx() if log_t::write_buf()orlog_t::persist()areexecutingconcurrently withthis.Inthatcase,age>af_lwmshouldhold,and buf_flush_page_cleaner()wouldexecuteonemoretimedwait.(Nota
big problem.) */
/* if age > af_lwm adaptive flushing is recommended */ return (age > af_lwm);
}
/*********************************************************************//**
Calculates if flushing is required based on redo generation rate.
@return percent of io_capacity to flush to manage redo space */ static
ulint
af_get_pct_for_lsn( /*===============*/
lsn_t age) /*!< in: current age of LSN. */
{
lsn_t af_lwm = static_cast<lsn_t>(
srv_adaptive_flushing_lwm
* static_cast<double>(log_sys.log_capacity) / 100);
if (age < af_lwm) { /* No adaptive flushing. */ return(0);
}
/* We update our variables every innodb_flushing_avg_loops
iterations to smooth out transition in workload. */ if (++n_iterations >= srv_flushing_avg_loops
|| time_elapsed >= srv_flushing_avg_loops) {
/* Estimate pages to be flushed for the lsn progress */
lsn_t target_lsn = oldest_lsn
+ lsn_avg_rate * buf_flush_lsn_scan_factor;
ulint pages_for_lsn = 0;
mysql_mutex_lock(&buf_pool.flush_list_mutex);
for (buf_page_t* b = UT_LIST_GET_LAST(buf_pool.flush_list);
b != NULL;
b = UT_LIST_GET_PREV(list, b)) { if (b->oldest_modification() > target_lsn) { break;
} if (++pages_for_lsn >= srv_max_io_capacity) { break;
}
}
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
mysql_mutex_lock(&buf_pool.flush_list_mutex); if (!buf_pool.need_LRU_eviction())
{ if (UNIV_UNLIKELY(srv_shutdown_state >= SRV_SHUTDOWN_LAST_PHASE)) break;
if (buf_pool.page_cleaner_idle() &&
(!UT_LIST_GET_LEN(buf_pool.flush_list) ||
srv_max_dirty_pages_pct_lwm == 0.0))
{
buf_pool.LRU_warned_clear(); if (UNIV_UNLIKELY(create_spare_archive != 0)) goto timed_wait; /* We are idle; wait for buf_pool.page_cleaner_wakeup() */
my_cond_wait(&buf_pool.do_flush_list,
&buf_pool.flush_list_mutex.m_mutex);
} else
timed_wait:
my_cond_timedwait(&buf_pool.do_flush_list,
&buf_pool.flush_list_mutex.m_mutex, &abstime);
}
if (!oldest_lsn)
{
fully_unemployed: if (srv_shutdown_state < SRV_SHUTDOWN_CLEANUP)
buf_flush_sync_lsn= 0;
set_idle:
buf_pool.page_cleaner_set_idle(true);
set_almost_idle:
pthread_cond_broadcast(&buf_pool.done_flush_LRU);
pthread_cond_broadcast(&buf_pool.done_flush_list);
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
buf_dblwr.flush_buffered_writes();
do
{ if (recv_recovery_is_on()) continue;
create_spare_archive= buf_flush_archive_create(create_spare_archive); #ifdef WITH_WSREP extern Atomic_relaxed<bool> wsrep_sst_disable_writes; if (UNIV_UNLIKELY(wsrep_sst_disable_writes)) continue; /* See sst_disable_innodb_writes() */ #endif
IF_DBUG(if (log_sys.last_checkpoint_lsn > log_sys.get_first_lsn() &&
srv_shutdown_state < SRV_SHUTDOWN_CLEANUP &&
(_db_keyword_(nullptr, "ib_log_checkpoint_avoid", 1) ||
_db_keyword_(nullptr, "ib_log_checkpoint_avoid_hard", 1))) continue,); if (log_sys.check_for_checkpoint() ||
(!srv_startup_is_before_trx_rollback_phase &&
srv_operation <= SRV_OPERATION_EXPORT_RESTORED))
create_spare_archive= log_checkpoint();
} while (false);
if (UNIV_UNLIKELY(srv_shutdown_state >= SRV_SHUTDOWN_LAST_PHASE))
{
mysql_mutex_lock(&buf_pool.flush_list_mutex); break;
}
if (!buf_pool.need_LRU_eviction()) continue;
mysql_mutex_lock(&buf_pool.flush_list_mutex);
oldest_lsn= buf_pool.get_oldest_modification(0);
}
lsn_t soft_lsn_limit= buf_flush_async_lsn;
if (UNIV_UNLIKELY(lsn_limit != 0))
{ if (srv_flush_sync) goto do_furious_flush; if (oldest_lsn >= lsn_limit)
{ if (srv_shutdown_state < SRV_SHUTDOWN_CLEANUP)
buf_flush_sync_lsn= 0;
pthread_cond_broadcast(&buf_pool.done_flush_list);
} elseif (lsn_limit > soft_lsn_limit)
soft_lsn_limit= lsn_limit;
}
double pct_lwm= 0.0;
ulint n_flushed= 0, n;
if (UNIV_UNLIKELY(soft_lsn_limit != 0))
{ if (oldest_lsn >= soft_lsn_limit)
{
buf_flush_async_lsn.try_clear(soft_lsn_limit);
soft_lsn_limit= 0;
}
} elseif (buf_pool.need_LRU_eviction())
{
buf_pool.page_cleaner_set_idle(false);
buf_pool.n_flush_inc();
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
n= srv_max_io_capacity; /* We must urgently perform some LRU eviction. Page writes are onlytriggeredbythisthread,whetheritisforcheckpointor
LRU eviction. First, wait for any pending writes to complete. */
os_aio_wait_until_no_pending_writes(false);
mysql_mutex_lock(&buf_pool.mutex);
LRU_flush:
n= buf_flush_LRU(n);
mysql_mutex_unlock(&buf_pool.mutex);
last_pages+= n;
check_oldest_and_set_idle:
mysql_mutex_lock(&buf_pool.flush_list_mutex);
buf_pool.n_flush_dec();
oldest_lsn= buf_pool.get_oldest_modification(0); if (!oldest_lsn) goto fully_unemployed;
buf_flush_async_lsn.try_clear_if_at_most(oldest_lsn);
buf_pool.page_cleaner_set_idle(false); goto set_almost_idle;
} elseif (UNIV_UNLIKELY(srv_shutdown_state >= SRV_SHUTDOWN_LAST_PHASE)) break;
const ulint dirty_blocks= UT_LIST_GET_LEN(buf_pool.flush_list); /* We perform dirty reads of the LRU+free list lengths here. Divisionbyzeroisnotpossible,becausebuf_pool.flush_listis
guaranteed to be nonempty, and it is a subset of buf_pool.LRU. */ constdouble dirty_pct= double(dirty_blocks) * 100.0 / double(UT_LIST_GET_LEN(buf_pool.LRU) + UT_LIST_GET_LEN(buf_pool.free));
pct_lwm= srv_max_dirty_pages_pct_lwm; if (pct_lwm != 0.0)
{ const ulint activity_count= srv_get_activity_count(); if (activity_count != last_activity_count)
{
last_activity_count= activity_count; goto maybe_unemployed;
} elseif (buf_pool.page_cleaner_idle() && !os_aio_pending_reads())
{ /* reaching here means 3 things: -last_activity_count==activity_count:suggestingserverisidle (notrx_t::commit()activity) -pagecleanerisidle(dirty_pct<srv_max_dirty_pages_pct_lwm)
- there are no pending reads but there are dirty pages to flush */
buf_pool.update_last_activity_count(activity_count);
buf_pool.n_flush_inc();
mysql_mutex_unlock(&buf_pool.flush_list_mutex); goto idle_flush;
} else
{
maybe_unemployed: if (dirty_pct < pct_lwm)
{
pct_lwm= 0.0; goto possibly_unemployed;
}
}
} elseif (dirty_pct < srv_max_buf_pool_modified_pct)
possibly_unemployed: /* Snapshot-based; no coordination with concurrent lock-free buf_flush_ahead()bumpers.Abumperthatobserved page_cleaner_idle()==falseearlierinthisiterationtookits earlyreturn;flippingidle=truehereleavesitstarget unacknowledgeduntilanotherwakepathfires.Samepremiseasthe bumper'searlyreturn:undersustainedredo-logpressure, subsequentbuf_flush_ahead()callsobserve
page_cleaner_idle()==true and take the wake path. */ if (!soft_lsn_limit && !af_needed_for_redo(oldest_lsn)) goto set_idle;
if (UNIV_UNLIKELY(soft_lsn_limit != 0))
{
n= srv_max_io_capacity; goto background_flush;
}
if (!srv_adaptive_flushing)
{
idle_flush:
n= srv_io_capacity;
soft_lsn_limit= LSN_MAX;
background_flush:
mysql_mutex_lock(&buf_pool.mutex);
n_flushed= buf_flush_list_holding_mutex(n, soft_lsn_limit);
MONITOR_INC_VALUE_CUMULATIVE(MONITOR_FLUSH_BACKGROUND_TOTAL_PAGE,
MONITOR_FLUSH_BACKGROUND_COUNT,
MONITOR_FLUSH_BACKGROUND_PAGES,
n_flushed);
} elseif ((n= page_cleaner_flush_pages_recommendation(last_pages,
oldest_lsn,
pct_lwm,
dirty_blocks,
dirty_pct)) != 0)
{ const ulint tm= ut_time_ms();
mysql_mutex_lock(&buf_pool.mutex);
last_pages= n_flushed= buf_flush_list_holding_mutex(n);
page_cleaner.flush_time+= ut_time_ms() - tm;
MONITOR_INC_VALUE_CUMULATIVE(MONITOR_FLUSH_ADAPTIVE_TOTAL_PAGE,
MONITOR_FLUSH_ADAPTIVE_COUNT,
MONITOR_FLUSH_ADAPTIVE_PAGES,
n_flushed);
} elseif (buf_flush_async_lsn <= oldest_lsn &&
!buf_pool.need_LRU_eviction()) goto check_oldest_and_set_idle; else
{ /* Page writes are only triggered by this thread, whether it is forcheckpointorLRUevictionflushing.Waitforanypending writesofanearlierbatchtocompletebeforestartinganLRU
scan to improve the chances that some blocks are clean. */
os_aio_wait_until_no_pending_writes(false);
mysql_mutex_lock(&buf_pool.mutex);
}
n= srv_max_io_capacity;
n= n >= n_flushed ? n - n_flushed : 0; /* It is critical to generate free pages to keep the system alive. Make
sure we are not hindered by dirty pages in LRU tail. */
n= std::max<ulint>(n, std::min<ulint>(srv_max_io_capacity,
buf_pool.LRU_scan_depth)); goto LRU_flush;
}
ATTRIBUTE_COLD void buf_pool_t::LRU_warn() noexcept
{
mysql_mutex_assert_owner(&mutex);
try_LRU_scan= false; if (!LRU_warned)
{
LRU_warned= true;
sql_print_warning("InnoDB: Could not free any blocks in the buffer pool!" " Consider increasing innodb_buffer_pool_size.");
print_flush_info();
}
}
/** Make a checkpoint. */
ATTRIBUTE_COLD void log_make_checkpoint() noexcept
{
buf_flush_sync_batch(0, true);
}
/** Wait for the persistent data to be written out. NOTE:Thecallingthreadisnotallowedtoholdanybufferpagelatches! @paramlsnminimumcheckpointtowaitfor
@param checkpoint whether an empty checkpoint needs to be written */
ATTRIBUTE_COLD void buf_flush_sync_batch(lsn_t lsn, bool checkpoint) noexcept
{
thd_wait_begin(nullptr, THD_WAIT_DISKIO);
tpool::tpool_wait_begin();
log_sys.latch.wr_lock();
lsn= std::max(lsn, log_sys.get_lsn());
mysql_mutex_lock(&buf_pool.flush_list_mutex);
buf_flush_wait(lsn, checkpoint);
log_sys.latch.wr_unlock();
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
tpool::tpool_wait_end();
thd_wait_end(nullptr);
}
while (bpage != NULL) { const lsn_t om = bpage->oldest_modification(); /* A page in buf_pool.flush_list can be in BUF_BLOCK_REMOVE_HASHstate.Thishappenswhenapage isinthemiddleofbeingrelocated.Inthatcasethe originaldescriptorcanhavethisstateandstillbe intheflushlistwaitingtoacquirethe
buf_pool.flush_list_mutex to complete the relocation. */
ut_d(constauto s= bpage->state());
ut_ad(s >= buf_page_t::REMOVE_HASH);
ut_ad(om == 1 || om > 2);
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.