MDEV-15374 Server hangs and aborts with long semaphore wait or assertion `len...

MDEV-15374 Server hangs and aborts with long semaphore wait or assertion `len < ((ulint) srv_page_size)' fails in trx_undo_rec_copy upon ROLLBACK on temporary table

Problem:
=======
InnoDB cleans all temporary undo logs during commit. During rollback
of secondary index entry, InnoDB tries to build the previous version
of clustered index. It leads to access of freed undo page during
previous transaction commit and it leads to undo log corruption.

Solution:
=========
During rollback, temporary undo logs should not try to build
the previous version of the record.
parent 469a4b02
...@@ -428,7 +428,7 @@ row_undo_mod_del_mark_or_remove_sec_low( ...@@ -428,7 +428,7 @@ row_undo_mod_del_mark_or_remove_sec_low(
btr_pcur_t pcur; btr_pcur_t pcur;
btr_cur_t* btr_cur; btr_cur_t* btr_cur;
ibool success; ibool success;
ibool old_has; ibool old_has = FALSE;
dberr_t err = DB_SUCCESS; dberr_t err = DB_SUCCESS;
mtr_t mtr; mtr_t mtr;
mtr_t mtr_vers; mtr_t mtr_vers;
...@@ -504,10 +504,15 @@ row_undo_mod_del_mark_or_remove_sec_low( ...@@ -504,10 +504,15 @@ row_undo_mod_del_mark_or_remove_sec_low(
&mtr_vers); &mtr_vers);
ut_a(success); ut_a(success);
old_has = row_vers_old_has_index_entry(FALSE, /* For temporary table, we can skip to check older version of
btr_pcur_get_rec(&(node->pcur)), clustered index entry. Because the purge won't process
&mtr_vers, index, entry, any no-redo rollback segment undo logs. */
0, 0); if (!dict_table_is_temporary(node->table)) {
old_has = row_vers_old_has_index_entry(
FALSE, btr_pcur_get_rec(&(node->pcur)),
&mtr_vers, index, entry, 0, 0);
}
if (old_has) { if (old_has) {
err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG, err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
btr_cur, TRUE, thr, &mtr); btr_cur, TRUE, thr, &mtr);
......
...@@ -2087,13 +2087,11 @@ trx_undo_report_row_operation( ...@@ -2087,13 +2087,11 @@ 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] is_temp whether this is a temporary table
@param[in,out] heap memory heap where copied */ @param[in,out] heap memory heap where copied */
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,
bool is_temp,
mem_heap_t* heap) mem_heap_t* heap)
{ {
trx_undo_rec_t* undo_rec; trx_undo_rec_t* undo_rec;
...@@ -2109,10 +2107,7 @@ trx_undo_get_undo_rec_low( ...@@ -2109,10 +2107,7 @@ trx_undo_get_undo_rec_low(
&offset); &offset);
ut_ad(page_no > FSP_FIRST_INODE_PAGE_NO); ut_ad(page_no > FSP_FIRST_INODE_PAGE_NO);
ut_ad(offset >= TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE); ut_ad(offset >= TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE);
rseg = is_temp rseg = trx_sys->rseg_array[rseg_id];
? trx_sys->temp_rsegs[rseg_id]
: trx_sys->rseg_array[rseg_id];
ut_ad(is_temp == !rseg->is_persistent());
mtr_start(&mtr); mtr_start(&mtr);
...@@ -2128,7 +2123,6 @@ trx_undo_get_undo_rec_low( ...@@ -2128,7 +2123,6 @@ trx_undo_get_undo_rec_low(
/** Copy an undo record to heap. /** Copy an undo record to heap.
@param[in] roll_ptr roll pointer to record @param[in] roll_ptr roll pointer to record
@param[in] is_temp whether this is a temporary table
@param[in,out] heap memory heap where copied @param[in,out] heap memory heap where copied
@param[in] trx_id id of the trx that generated @param[in] trx_id id of the trx that generated
the roll pointer: it points to an the roll pointer: it points to an
...@@ -2143,7 +2137,6 @@ static MY_ATTRIBUTE((warn_unused_result)) ...@@ -2143,7 +2137,6 @@ static MY_ATTRIBUTE((warn_unused_result))
bool bool
trx_undo_get_undo_rec( trx_undo_get_undo_rec(
roll_ptr_t roll_ptr, roll_ptr_t roll_ptr,
bool is_temp,
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,
...@@ -2155,7 +2148,7 @@ trx_undo_get_undo_rec( ...@@ -2155,7 +2148,7 @@ trx_undo_get_undo_rec(
missing_history = purge_sys->view.changes_visible(trx_id, name); missing_history = purge_sys->view.changes_visible(trx_id, name);
if (!missing_history) { if (!missing_history) {
*undo_rec = trx_undo_get_undo_rec_low(roll_ptr, is_temp, heap); *undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
} }
rw_lock_s_unlock(&purge_sys->latch); rw_lock_s_unlock(&purge_sys->latch);
...@@ -2236,19 +2229,19 @@ trx_undo_prev_version_build( ...@@ -2236,19 +2229,19 @@ trx_undo_prev_version_build(
return(true); return(true);
} }
const bool is_temp = dict_table_is_temporary(index->table); ut_ad(!dict_table_is_temporary(index->table));
rec_trx_id = row_get_rec_trx_id(rec, index, offsets); rec_trx_id = row_get_rec_trx_id(rec, index, offsets);
ut_ad(!index->table->skip_alter_undo); ut_ad(!index->table->skip_alter_undo);
if (trx_undo_get_undo_rec( if (trx_undo_get_undo_rec(
roll_ptr, is_temp, heap, rec_trx_id, index->table->name, roll_ptr, heap, rec_trx_id, index->table->name,
&undo_rec)) { &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 */
ut_ad(!is_temp);
undo_rec = trx_undo_get_undo_rec_low( undo_rec = trx_undo_get_undo_rec_low(
roll_ptr, is_temp, heap); roll_ptr, heap);
} else { } else {
/* The undo record may already have been purged, /* The undo record may already have been purged,
during purge or semi-consistent read. */ during purge or semi-consistent read. */
......
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