MDEV-35049: Fix bogus BTR_CUR_HASH_FAIL on contention

btr_search_guess_on_hash(): Only set BTR_CUR_HASH_FAIL on actual mismatch.
If the page latch cannot be acquired, the hash search might very well
have succeeded. Do not count that as a failure, that is, do not
unnecessarily invoke btr_search_update_hash_ref() after a normal search.
Set cursor->flag=BTR_CUR_HASH_ABORT if the current parameters of the
adaptive hash index are not suitable for the search and a call to
btr_cur_t::search_info_update() might help.

btr_cur_t::search_leaf(): Do not invoke search_info_update()
if btr_search_guess_on_hash() failed due to contention.

btr_cur_t::pessimistic_search_leaf(): Do not invoke search_info_update()
on the change buffer tree. Preivously, this condition was being checked
inside search_info_update().
This commit is contained in:
Marko Mäkelä 2025-01-10 16:40:30 +02:00
parent 68cac26108
commit 6b58ee769f
3 changed files with 31 additions and 21 deletions

View file

@ -1423,18 +1423,14 @@ release_tree:
goto need_opposite_intention;
#ifdef BTR_CUR_HASH_ADAPT
/* We do a dirty read of btr_search.enabled here. We will recheck in
btr_search_build_page_hash_index() before building a page hash
index, while holding search latch. */
if (!btr_search.enabled);
else if (tuple->info_bits & REC_INFO_MIN_REC_FLAG)
/* This may be a search tuple for btr_pcur_t::restore_position(). */
ut_ad(tuple->is_metadata() ||
(tuple->is_metadata(tuple->info_bits ^ REC_STATUS_INSTANT)));
else if (index()->table->is_temporary());
else if (!rec_is_metadata(page_cur.rec, *index()) &&
index()->search_info.hash_analysis_useful())
search_info_update();
if (flag != BTR_CUR_BINARY)
{
ut_ad(!(tuple->info_bits & REC_INFO_MIN_REC_FLAG));
ut_ad(!index()->table->is_temporary());
if (!rec_is_metadata(page_cur.rec, *index()) &&
index()->search_info.hash_analysis_useful())
search_info_update();
}
#endif /* BTR_CUR_HASH_ADAPT */
goto func_exit;

View file

@ -501,10 +501,11 @@ skip:
@retval 0 if the adaptive hash index should not be rebuilt */
static uint32_t btr_search_info_update_hash(const btr_cur_t &cursor) noexcept
{
ut_ad(cursor.flag != BTR_CUR_HASH);
ut_ad(cursor.flag == BTR_CUR_HASH_FAIL ||
cursor.flag == BTR_CUR_HASH_ABORT ||
cursor.flag == BTR_CUR_BINARY);
dict_index_t *const index= cursor.index();
const uint16_t n_uniq{dict_index_get_n_unique_in_tree(index)};
dict_index_t::ahi &info= index->search_info;
@ -1046,12 +1047,20 @@ btr_search_guess_on_hash(
ut_ad(mtr->is_active());
ut_ad(index->is_btree());
ut_ad(latch_mode == BTR_SEARCH_LEAF || latch_mode == BTR_MODIFY_LEAF);
ut_ad(cursor->flag == BTR_CUR_BINARY);
if ((tuple->info_bits & REC_INFO_MIN_REC_FLAG) ||
!index->search_info.last_hash_succ ||
!index->search_info.n_hash_potential)
if ((tuple->info_bits & REC_INFO_MIN_REC_FLAG))
return false;
if (!index->search_info.last_hash_succ ||
!index->search_info.n_hash_potential)
{
ahi_unusable:
if (!index->table->is_temporary() && btr_search.enabled)
cursor->flag= BTR_CUR_HASH_ABORT;
return false;
}
ut_ad(!index->table->is_temporary());
static_assert(ulint{BTR_SEARCH_LEAF} == ulint{RW_S_LATCH}, "");
@ -1061,7 +1070,7 @@ btr_search_guess_on_hash(
~buf_block_t::LEFT_SIDE;
if (dtuple_get_n_fields(tuple) < btr_search_get_n_fields(cursor))
return false;
goto ahi_unusable;
const index_id_t index_id= index->id;
@ -1070,7 +1079,6 @@ btr_search_guess_on_hash(
#endif
const uint32_t fold= dtuple_fold(tuple, cursor);
cursor->fold= fold;
cursor->flag= BTR_CUR_HASH;
btr_sea::partition &part= btr_search.get_part(*index);
part.latch.rd_lock(SRW_LOCK_CALL);
@ -1080,8 +1088,6 @@ btr_search_guess_on_hash(
ahi_release_and_fail:
part.latch.rd_unlock();
fail:
cursor->flag= BTR_CUR_HASH_FAIL;
#ifdef UNIV_SEARCH_PERF_STAT
++index->search_info.n_hash_fail;
if (index->search_info.n_hash_succ > 0)
@ -1096,7 +1102,10 @@ btr_search_guess_on_hash(
{ return node->fold == fold; });
if (!node)
{
cursor->flag= BTR_CUR_HASH_FAIL;
goto ahi_release_and_fail;
}
const rec_t *rec= node->rec;
buf_block_t *block= buf_pool.block_from_ahi(rec);
@ -1127,6 +1136,7 @@ btr_search_guess_on_hash(
block->page.lock.s_unlock();
else
block->page.lock.x_unlock();
cursor->flag= BTR_CUR_HASH_FAIL;
goto ahi_release_and_fail;
}
@ -1137,6 +1147,7 @@ btr_search_guess_on_hash(
if (index != block_index && index_id == block_index->id)
{
ut_a(block_index->freed());
cursor->flag= BTR_CUR_HASH_FAIL;
goto block_and_ahi_release_and_fail;
}
@ -1169,6 +1180,7 @@ btr_search_guess_on_hash(
default:
mismatch:
mtr->release_last_page();
cursor->flag= BTR_CUR_HASH_FAIL;
goto fail;
}
@ -1183,6 +1195,7 @@ btr_search_guess_on_hash(
index->search_info.n_hash_potential= n_hash_potential;
index->search_info.last_hash_succ= true;
cursor->flag= BTR_CUR_HASH;
#ifdef UNIV_SEARCH_PERF_STAT
btr_search_n_succ++;

View file

@ -649,6 +649,7 @@ enum btr_cur_method {
#ifdef BTR_CUR_HASH_ADAPT
BTR_CUR_HASH, /*!< successful shortcut using
the hash index */
BTR_CUR_HASH_ABORT, /*!< the hash index could not be used */
BTR_CUR_HASH_FAIL, /*!< failure using hash, success using
binary search: the misleading hash
reference is stored in the field