mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 04:22:27 +01:00
de4030e4d4
This also fixes part of MDEV-29835 Partial server freeze which is caused by violations of the latching order that was defined in https://dev.mysql.com/worklog/task/?id=6326 (WL#6326: InnoDB: fix index->lock contention). Unless the current thread is holding an exclusive dict_index_t::lock, it must acquire page latches in a strict parent-to-child, left-to-right order. Not all cases of MDEV-29835 are fixed yet. Failure to follow the correct latching order will cause deadlocks of threads due to lock order inversion. As part of these changes, the BTR_MODIFY_TREE mode is modified so that an Update latch (U a.k.a. SX) will be acquired on the root page, and eXclusive latches (X) will be acquired on all pages leading to the leaf page, as well as any left and right siblings of the pages along the path. The DEBUG_SYNC test innodb.innodb_wl6326 will be removed, because at the time the DEBUG_SYNC point is hit, the thread is actually holding several page latches that will be blocking a concurrent SELECT statement. We also remove double bookkeeping that was caused due to excessive information hiding in mtr_t::m_memo. We simply let mtr_t::m_memo store information of latched pages, and ensure that mtr_memo_slot_t::object is never a null pointer. The tree_blocks[] and tree_savepoints[] were redundant. buf_page_get_low(): If innodb_change_buffering_debug=1, to avoid a hang, do not try to evict blocks if we are holding a latch on a modified page. The test innodb.innodb-change-buffer-recovery will be removed, because change buffering may no longer be forced by debug injection when the change buffer comprises multiple pages. Remove a debug assertion that could fail when innodb_change_buffering_debug=1 fails to evict a page. For other cases, the assertion is redundant, because we already checked that right after the got_block: label. The test innodb.innodb-change-buffering-recovery will be removed, because due to this change, we will be unable to evict the desired page. mtr_t::lock_register(): Register a change of a page latch on an unmodified buffer-fixed block. mtr_t::x_latch_at_savepoint(), mtr_t::sx_latch_at_savepoint(): Replaced by the use of mtr_t::upgrade_buffer_fix(), which now also handles RW_S_LATCH. mtr_t::set_modified(): For temporary tables, invoke buf_page_t::set_modified() here and not in mtr_t::commit(). We will never set the MTR_MEMO_MODIFY flag on other than persistent data pages, nor set mtr_t::m_modifications when temporary data pages are modified. mtr_t::commit(): Only invoke the buf_flush_note_modification() loop if persistent data pages were modified. mtr_t::get_already_latched(): Look up a latched page in mtr_t::m_memo. This avoids many redundant entries in mtr_t::m_memo, as well as redundant calls to buf_page_get_gen() for blocks that had already been looked up in a mini-transaction. btr_get_latched_root(): Return a pointer to an already latched root page. This replaces btr_root_block_get() in cases where the mini-transaction has already latched the root page. btr_page_get_parent(): Fetch a parent page that was already latched in BTR_MODIFY_TREE, by invoking mtr_t::get_already_latched(). If needed, upgrade the root page U latch to X. This avoids bloating mtr_t::m_memo as well as performing redundant buf_pool.page_hash lookups. For non-QUICK CHECK TABLE as well as for B-tree defragmentation, we will invoke btr_cur_search_to_nth_level(). btr_cur_search_to_nth_level(): This will only be used for non-leaf (level>0) B-tree searches that were formerly named BTR_CONT_SEARCH_TREE or BTR_CONT_MODIFY_TREE. In MDEV-29835, this function could be removed altogether, or retained for the case of CHECK TABLE without QUICK. btr_cur_t::left_block: Remove. btr_pcur_move_backward_from_page() can retrieve the left sibling from the end of mtr_t::m_memo. btr_cur_t::open_leaf(): Some clean-up. btr_cur_t::search_leaf(): Replaces btr_cur_search_to_nth_level() for searches to level=0 (the leaf level). We will never release parent page latches before acquiring leaf page latches. If we need to temporarily release the level=1 page latch in the BTR_SEARCH_PREV or BTR_MODIFY_PREV latch_mode, we will reposition the cursor on the child node pointer so that we will land on the correct leaf page. btr_cur_t::pessimistic_search_leaf(): Implement new BTR_MODIFY_TREE latching logic in the case that page splits or merges will be needed. The parent pages (and their siblings) should already be latched on the first dive to the leaf and be present in mtr_t::m_memo; there should be no need for BTR_CONT_MODIFY_TREE. This pre-latching almost suffices; it must be revised in MDEV-29835 and work-arounds removed for cases where mtr_t::get_already_latched() fails to find a block. rtr_search_to_nth_level(): A SPATIAL INDEX version of btr_search_to_nth_level() that can search to any level (including the leaf level). rtr_search_leaf(), rtr_insert_leaf(): Wrappers for rtr_search_to_nth_level(). rtr_search(): Replaces rtr_pcur_open(). rtr_latch_leaves(): Replaces btr_cur_latch_leaves(). Note that unlike in the B-tree code, there is no error handling in case the sibling pages are corrupted. rtr_cur_restore_position(): Remove an unused constant parameter. btr_pcur_open_on_user_rec(): Remove the constant parameter mode=PAGE_CUR_GE. row_ins_clust_index_entry_low(): Use a new mode=BTR_MODIFY_ROOT_AND_LEAF to gain access to the root page when mode!=BTR_MODIFY_TREE, to write the PAGE_ROOT_AUTO_INC. BTR_SEARCH_TREE, BTR_CONT_SEARCH_TREE: Remove. BTR_CONT_MODIFY_TREE: Note that this is only used by rtr_search_to_nth_level(). btr_pcur_optimistic_latch_leaves(): Replaces btr_cur_optimistic_latch_leaves(). ibuf_delete_rec(): Acquire exclusive ibuf.index->lock in order to avoid a deadlock with ibuf_insert_low(BTR_MODIFY_PREV). btr_blob_log_check_t(): Acquire a U latch on the root page, so that btr_page_alloc() in btr_store_big_rec_extern_fields() will avoid a deadlock. btr_store_big_rec_extern_fields(): Assert that the root page latch is being held. Tested by: Matthias Leich Reviewed by: Vladislav Lesin
100 lines
3 KiB
C++
100 lines
3 KiB
C++
/*****************************************************************************
|
|
|
|
Copyright (c) 2023, MariaDB Corporation.
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
#pragma once
|
|
/* A normally small vector, inspired by llvm::SmallVector */
|
|
#include "my_global.h"
|
|
#include <iterator>
|
|
#include <memory>
|
|
|
|
class small_vector_base
|
|
{
|
|
protected:
|
|
typedef uint32_t Size_T;
|
|
void *BeginX;
|
|
Size_T Size= 0, Capacity;
|
|
small_vector_base()= delete;
|
|
small_vector_base(void *small, size_t small_size)
|
|
: BeginX(small), Capacity(Size_T(small_size)) {}
|
|
ATTRIBUTE_COLD void grow_by_1(void *small, size_t element_size);
|
|
public:
|
|
size_t size() const { return Size; }
|
|
size_t capacity() const { return Capacity; }
|
|
bool empty() const { return !Size; }
|
|
void clear() { Size= 0; }
|
|
protected:
|
|
void set_size(size_t N) { Size= Size_T(N); }
|
|
};
|
|
|
|
template <typename T, unsigned N>
|
|
class small_vector : public small_vector_base
|
|
{
|
|
/** The fixed storage allocation */
|
|
T small[N];
|
|
|
|
using small_vector_base::set_size;
|
|
|
|
void grow_if_needed()
|
|
{
|
|
if (unlikely(size() >= capacity()))
|
|
grow_by_1(small, sizeof *small);
|
|
}
|
|
|
|
public:
|
|
small_vector() : small_vector_base(small, N)
|
|
{
|
|
TRASH_ALLOC(small, sizeof small);
|
|
}
|
|
~small_vector()
|
|
{
|
|
if (small != begin())
|
|
my_free(begin());
|
|
MEM_MAKE_ADDRESSABLE(small, sizeof small);
|
|
}
|
|
|
|
using iterator= T *;
|
|
using const_iterator= const T *;
|
|
using reverse_iterator= std::reverse_iterator<iterator>;
|
|
using reference= T &;
|
|
using const_reference= const T&;
|
|
|
|
iterator begin() { return static_cast<iterator>(BeginX); }
|
|
const_iterator begin() const { return static_cast<const_iterator>(BeginX); }
|
|
iterator end() { return begin() + size(); }
|
|
const_iterator end() const { return begin() + size(); }
|
|
|
|
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
|
reverse_iterator rend() { return reverse_iterator(begin()); }
|
|
|
|
reference operator[](size_t i) { assert(i < size()); return begin()[i]; }
|
|
const_reference operator[](size_t i) const
|
|
{ return const_cast<small_vector&>(*this)[i]; }
|
|
|
|
void erase(const_iterator S, const_iterator E)
|
|
{
|
|
set_size(std::move(const_cast<iterator>(E), end(),
|
|
const_cast<iterator>(S)) - begin());
|
|
}
|
|
|
|
void emplace_back(T &&arg)
|
|
{
|
|
grow_if_needed();
|
|
::new (end()) T(arg);
|
|
set_size(size() + 1);
|
|
}
|
|
};
|