Commit 0f717d03 authored by Thirunarayanan Balathandayuthapani's avatar Thirunarayanan Balathandayuthapani Committed by Marko Mäkelä

MDEV-28443: MDEV-15250 causes latch order violation

Problem:
=======
Index page latches must be acquired before undo page latches.
In trx_t::apply_log(), InnoDB acquired undo log page latch
before an index page latch.

Solution:
========
In trx_t::apply_log(), InnoDB should copy the undo log
record and release the undo log block before applying
it on online indexes.
parent 8c1c6130
...@@ -232,8 +232,6 @@ must hold a latch on the index page of the clustered index record. ...@@ -232,8 +232,6 @@ must hold a latch on the index page of the clustered index record.
@param v_status status determine if it is going into this @param v_status status determine if it is going into this
function by purge thread or not. function by purge thread or not.
And if we read "after image" of undo log And if we read "after image" of undo log
@param undo_block undo log block which was cached during
online dml apply or nullptr
@retval true if previous version was built, or if it was an insert @retval true if previous version was built, or if it was an insert
or the table has been rebuilt or the table has been rebuilt
@retval false if the previous version is earlier than purge_view, @retval false if the previous version is earlier than purge_view,
...@@ -249,8 +247,7 @@ trx_undo_prev_version_build( ...@@ -249,8 +247,7 @@ trx_undo_prev_version_build(
rec_t **old_vers, rec_t **old_vers,
mem_heap_t *v_heap, mem_heap_t *v_heap,
dtuple_t **vrow, dtuple_t **vrow,
ulint v_status, ulint v_status);
const buf_block_t *undo_block= nullptr);
/** Read from an undo log record a non-virtual column value. /** Read from an undo log record a non-virtual column value.
@param[in,out] ptr pointer to remaining part of the undo record @param[in,out] ptr pointer to remaining part of the undo record
......
...@@ -332,14 +332,14 @@ parse the undo log record and store the record type, update vector ...@@ -332,14 +332,14 @@ parse the undo log record and store the record type, update vector
and compiler information */ and compiler information */
class UndorecApplier class UndorecApplier
{ {
/** undo log block which was latched */ /** Undo log block page id */
const buf_block_t *block; page_id_t page_id;
/** Undo log record pointer */ /** Undo log record pointer */
trx_undo_rec_t *undo_rec; trx_undo_rec_t *undo_rec;
/** Offset of the undo log record within the block */ /** Offset of the undo log record within the block */
ulint offset; uint16_t offset;
/** Transaction id of the undo log */ /** Transaction id of the undo log */
trx_id_t trx_id; const trx_id_t trx_id;
/** Undo log record type */ /** Undo log record type */
ulint type; ulint type;
/** compiler information */ /** compiler information */
...@@ -353,21 +353,23 @@ class UndorecApplier ...@@ -353,21 +353,23 @@ class UndorecApplier
mtr_t mtr; mtr_t mtr;
public: public:
UndorecApplier(const buf_block_t *block, trx_id_t trx_id) UndorecApplier(page_id_t page_id, trx_id_t trx_id) :
: block(block), trx_id(trx_id) page_id(page_id), trx_id(trx_id), heap(mem_heap_create(100))
{ {
ut_ad(block->page.lock.have_any());
heap= mem_heap_create(100);
} }
/** Assign the undo log block */ /** Assign the next page id */
void assign_block(const buf_block_t *undo_block) void assign_next(const page_id_t next_page_id)
{ {
block= undo_block; page_id= next_page_id;
} }
/** Assign the undo log record and offset */ /** Assign the undo log record and offset */
void assign_rec(trx_undo_rec_t *rec); inline void assign_rec(const buf_block_t &block, uint16_t offset);
uint16_t get_offset() const { return offset; }
page_id_t get_page_id() const { return page_id; }
/** Handle the DML undo log and apply it on online indexes */ /** Handle the DML undo log and apply it on online indexes */
void apply_undo_rec(); void apply_undo_rec();
...@@ -396,7 +398,7 @@ class UndorecApplier ...@@ -396,7 +398,7 @@ class UndorecApplier
{ {
uint16_t offset= static_cast<uint16_t>(roll_ptr); uint16_t offset= static_cast<uint16_t>(roll_ptr);
uint32_t page_no= static_cast<uint32_t>(roll_ptr >> 16); uint32_t page_no= static_cast<uint32_t>(roll_ptr >> 16);
return page_no == block->page.id().page_no() && offset == this->offset; return page_no == page_id.page_no() && offset == this->offset;
} }
/** Clear the undo log record information */ /** Clear the undo log record information */
...@@ -406,7 +408,6 @@ class UndorecApplier ...@@ -406,7 +408,6 @@ class UndorecApplier
cmpl_info= 0; cmpl_info= 0;
type= 0; type= 0;
update= nullptr; update= nullptr;
offset= 0;
mem_heap_empty(heap); mem_heap_empty(heap);
} }
......
...@@ -3829,7 +3829,7 @@ UndorecApplier::get_old_rec(const dtuple_t &tuple, dict_index_t *index, ...@@ -3829,7 +3829,7 @@ UndorecApplier::get_old_rec(const dtuple_t &tuple, dict_index_t *index,
return version; return version;
trx_undo_prev_version_build(*clust_rec, &mtr, version, index, trx_undo_prev_version_build(*clust_rec, &mtr, version, index,
*offsets, heap, &prev_version, nullptr, *offsets, heap, &prev_version, nullptr,
nullptr, 0, block); nullptr, 0);
version= prev_version; version= prev_version;
} }
while (version); while (version);
...@@ -3983,7 +3983,7 @@ void UndorecApplier::log_update(const dtuple_t &tuple, ...@@ -3983,7 +3983,7 @@ void UndorecApplier::log_update(const dtuple_t &tuple,
heap, rec_offs_size(offsets)), match_rec, offsets); heap, rec_offs_size(offsets)), match_rec, offsets);
trx_undo_prev_version_build(rec, &mtr, match_rec, clust_index, trx_undo_prev_version_build(rec, &mtr, match_rec, clust_index,
offsets, heap, &prev_version, nullptr, offsets, heap, &prev_version, nullptr,
nullptr, 0, block); nullptr, 0);
prev_offsets= rec_get_offsets(prev_version, clust_index, prev_offsets, prev_offsets= rec_get_offsets(prev_version, clust_index, prev_offsets,
clust_index->n_core_fields, clust_index->n_core_fields,
......
...@@ -2230,15 +2230,12 @@ trx_undo_report_row_operation( ...@@ -2230,15 +2230,12 @@ trx_undo_report_row_operation(
/** Copy an undo record to heap. /** Copy an undo record to heap.
@param[in] roll_ptr roll pointer to a record that exists @param[in] roll_ptr roll pointer to a record that exists
@param[in,out] heap memory heap where copied @param[in,out] heap memory heap where copied */
@param[in] undo_block undo log block which was cached during
DML online log apply */
static static
trx_undo_rec_t* trx_undo_rec_t*
trx_undo_get_undo_rec_low( trx_undo_get_undo_rec_low(
roll_ptr_t roll_ptr, roll_ptr_t roll_ptr,
mem_heap_t* heap, mem_heap_t* heap)
const buf_block_t* undo_block= nullptr)
{ {
trx_undo_rec_t* undo_rec; trx_undo_rec_t* undo_rec;
ulint rseg_id; ulint rseg_id;
...@@ -2254,11 +2251,6 @@ trx_undo_get_undo_rec_low( ...@@ -2254,11 +2251,6 @@ trx_undo_get_undo_rec_low(
trx_rseg_t* rseg = &trx_sys.rseg_array[rseg_id]; trx_rseg_t* rseg = &trx_sys.rseg_array[rseg_id];
ut_ad(rseg->is_persistent()); ut_ad(rseg->is_persistent());
if (undo_block
&& undo_block->page.id().page_no() == page_no) {
undo_rec = trx_undo_rec_copy(
undo_block->page.frame + offset, heap);
} else {
mtr.start(); mtr.start();
buf_block_t *undo_page = trx_undo_page_get_s_latched( buf_block_t *undo_page = trx_undo_page_get_s_latched(
...@@ -2268,7 +2260,6 @@ trx_undo_get_undo_rec_low( ...@@ -2268,7 +2260,6 @@ trx_undo_get_undo_rec_low(
undo_page->page.frame + offset, heap); undo_page->page.frame + offset, heap);
mtr.commit(); mtr.commit();
}
return(undo_rec); return(undo_rec);
} }
...@@ -2281,8 +2272,6 @@ trx_undo_get_undo_rec_low( ...@@ -2281,8 +2272,6 @@ trx_undo_get_undo_rec_low(
undo log of this transaction undo log of this transaction
@param[in] name table name @param[in] name table name
@param[out] undo_rec own: copy of the record @param[out] undo_rec own: copy of the record
@param[in] undo_block undo log block which was cached during
DML online log apply
@retval true if the undo log has been @retval true if the undo log has been
truncated and we cannot fetch the old version truncated and we cannot fetch the old version
@retval false if the undo log record is available @retval false if the undo log record is available
...@@ -2294,15 +2283,13 @@ trx_undo_get_undo_rec( ...@@ -2294,15 +2283,13 @@ trx_undo_get_undo_rec(
mem_heap_t* heap, mem_heap_t* heap,
trx_id_t trx_id, trx_id_t trx_id,
const table_name_t& name, const table_name_t& name,
trx_undo_rec_t** undo_rec, trx_undo_rec_t** undo_rec)
const buf_block_t* undo_block)
{ {
purge_sys.latch.rd_lock(SRW_LOCK_CALL); purge_sys.latch.rd_lock(SRW_LOCK_CALL);
bool missing_history = purge_sys.changes_visible(trx_id, name); bool missing_history = purge_sys.changes_visible(trx_id, name);
if (!missing_history) { if (!missing_history) {
*undo_rec = trx_undo_get_undo_rec_low( *undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
roll_ptr, heap, undo_block);
} }
purge_sys.latch.rd_unlock(); purge_sys.latch.rd_unlock();
...@@ -2356,8 +2343,7 @@ trx_undo_prev_version_build( ...@@ -2356,8 +2343,7 @@ trx_undo_prev_version_build(
rec_t **old_vers, rec_t **old_vers,
mem_heap_t *v_heap, mem_heap_t *v_heap,
dtuple_t **vrow, dtuple_t **vrow,
ulint v_status, ulint v_status)
const buf_block_t*undo_block)
{ {
trx_undo_rec_t* undo_rec = NULL; trx_undo_rec_t* undo_rec = NULL;
dtuple_t* entry; dtuple_t* entry;
...@@ -2396,7 +2382,7 @@ trx_undo_prev_version_build( ...@@ -2396,7 +2382,7 @@ trx_undo_prev_version_build(
if (trx_undo_get_undo_rec( if (trx_undo_get_undo_rec(
roll_ptr, heap, rec_trx_id, index->table->name, roll_ptr, heap, rec_trx_id, index->table->name,
&undo_rec, undo_block)) { &undo_rec)) {
if (v_status & TRX_UNDO_PREV_IN_PURGE) { if (v_status & TRX_UNDO_PREV_IN_PURGE) {
/* We are fetching the record being purged */ /* We are fetching the record being purged */
undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap); undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
......
...@@ -290,10 +290,12 @@ trx_undo_get_first_rec(const fil_space_t &space, uint32_t page_no, ...@@ -290,10 +290,12 @@ trx_undo_get_first_rec(const fil_space_t &space, uint32_t page_no,
mtr); mtr);
} }
void UndorecApplier::assign_rec(trx_undo_rec_t *rec) inline void UndorecApplier::assign_rec(const buf_block_t &block,
uint16_t offset)
{ {
this->undo_rec= rec; ut_ad(block.page.lock.have_s());
this->offset= page_offset(rec); this->offset= offset;
this->undo_rec= trx_undo_rec_copy(block.page.frame + offset, heap);
} }
void UndorecApplier::apply_undo_rec() void UndorecApplier::apply_undo_rec()
...@@ -355,12 +357,11 @@ ATTRIBUTE_COLD void trx_t::apply_log() ...@@ -355,12 +357,11 @@ ATTRIBUTE_COLD void trx_t::apply_log()
page_id_t page_id{rsegs.m_redo.rseg->space->id, undo->hdr_page_no}; page_id_t page_id{rsegs.m_redo.rseg->space->id, undo->hdr_page_no};
page_id_t next_page_id(page_id); page_id_t next_page_id(page_id);
mtr_t mtr; mtr_t mtr;
mem_heap_t *heap= mem_heap_create(100);
mtr.start(); mtr.start();
buf_block_t *block= buf_page_get(page_id, 0, RW_S_LATCH, &mtr); buf_block_t *block= buf_page_get(page_id, 0, RW_S_LATCH, &mtr);
ut_ad(block); ut_ad(block);
UndorecApplier log_applier(block, id); UndorecApplier log_applier(page_id, id);
for (;;) for (;;)
{ {
...@@ -368,9 +369,12 @@ ATTRIBUTE_COLD void trx_t::apply_log() ...@@ -368,9 +369,12 @@ ATTRIBUTE_COLD void trx_t::apply_log()
undo->hdr_offset); undo->hdr_offset);
while (rec) while (rec)
{ {
log_applier.assign_rec(rec); log_applier.assign_rec(*block, page_offset(rec));
mtr.commit();
log_applier.apply_undo_rec(); log_applier.apply_undo_rec();
rec= trx_undo_page_get_next_rec(block, page_offset(rec), mtr.start();
block= buf_page_get(log_applier.get_page_id(), 0, RW_S_LATCH, &mtr);
rec= trx_undo_page_get_next_rec(block, log_applier.get_offset(),
page_id.page_no(), undo->hdr_offset); page_id.page_no(), undo->hdr_offset);
} }
...@@ -382,12 +386,11 @@ ATTRIBUTE_COLD void trx_t::apply_log() ...@@ -382,12 +386,11 @@ ATTRIBUTE_COLD void trx_t::apply_log()
next_page_id.set_page_no(next); next_page_id.set_page_no(next);
mtr.commit(); mtr.commit();
mtr.start(); mtr.start();
block= buf_page_get(next_page_id, 0, RW_S_LATCH, &mtr); block= buf_page_get_gen(next_page_id, 0, RW_S_LATCH, block, BUF_GET, &mtr);
log_applier.assign_block(block); log_applier.assign_next(next_page_id);
ut_ad(block); ut_ad(block);
} }
mtr.commit(); mtr.commit();
mem_heap_free(heap);
apply_online_log= false; apply_online_log= false;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment