Commit 347d9456 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-13495 Crash in rollback of a recovered INSERT transaction after upgrade

If the server is upgraded from a database that was created
before MDEV-12288, and if the undo logs in the database contain
an incomplete transaction that performed an INSERT operation,
the server would crash when rolling back that transaction.

trx_commit_low(): Relax a too strict transaction. This function
will also be called after completing the rollback of a recovered
transaction.

trx_purge_add_undo_to_history(): Merged from the functions
trx_purge_add_update_undo_to_history() and trx_undo_update_cleanup(),
which are removed. Remove the parameter undo_page, and instead call
trx_undo_set_state_at_finish() to obtain it.

trx_write_serialisation_history(): Treat undo and old_insert equally.
That is, after the rollback (or XA COMMIT) of a recovered transaction
before upgrade, move all logs (both insert_undo and update_undo) to
the purge queue.
parent 73297f53
...@@ -51,16 +51,13 @@ trx_purge_get_log_from_hist( ...@@ -51,16 +51,13 @@ trx_purge_get_log_from_hist(
/*========================*/ /*========================*/
fil_addr_t node_addr); /*!< in: file address of the history fil_addr_t node_addr); /*!< in: file address of the history
list node of the log */ list node of the log */
/************************************************************************ /** Prepend the history list with an undo log.
Adds the update undo log as the first log in the history list. Removes the Remove the undo log segment from the rseg slot if it is too big for reuse.
update undo log segment from the rseg slot if it is too big for reuse. */ @param[in] trx transaction
@param[in,out] undo undo log
@param[in,out] mtr mini-transaction */
void void
trx_purge_add_update_undo_to_history( trx_purge_add_undo_to_history(const trx_t* trx, trx_undo_t*& undo, mtr_t* mtr);
/*=================================*/
trx_t* trx, /*!< in: transaction */
page_t* undo_page, /*!< in: update undo log header page,
x-latched */
mtr_t* mtr); /*!< in: mtr */
/*******************************************************************//** /*******************************************************************//**
This function runs a purge batch. This function runs a purge batch.
@return number of undo log pages handled in the batch */ @return number of undo log pages handled in the batch */
......
...@@ -287,19 +287,6 @@ trx_undo_set_state_at_prepare( ...@@ -287,19 +287,6 @@ trx_undo_set_state_at_prepare(
bool rollback, bool rollback,
mtr_t* mtr); mtr_t* mtr);
/**********************************************************************//**
Adds the update undo log header as the first in the history list, and
frees the memory object, or puts it to the list of cached update undo log
segments. */
void
trx_undo_update_cleanup(
/*====================*/
trx_t* trx, /*!< in: trx owning the update
undo log */
page_t* undo_page, /*!< in: update undo log header page,
x-latched */
mtr_t* mtr); /*!< in: mtr */
/** Free an old insert or temporary undo log after commit or rollback. /** Free an old insert or temporary undo log after commit or rollback.
The information is not needed after a commit or rollback, therefore The information is not needed after a commit or rollback, therefore
the data can be discarded. the data can be discarded.
......
...@@ -237,21 +237,22 @@ purge_sys_t::~purge_sys_t() ...@@ -237,21 +237,22 @@ purge_sys_t::~purge_sys_t()
/*================ UNDO LOG HISTORY LIST =============================*/ /*================ UNDO LOG HISTORY LIST =============================*/
/********************************************************************//** /** Prepend the history list with an undo log.
Adds the update undo log as the first log in the history list. Removes the Remove the undo log segment from the rseg slot if it is too big for reuse.
update undo log segment from the rseg slot if it is too big for reuse. */ @param[in] trx transaction
@param[in,out] undo undo log
@param[in,out] mtr mini-transaction */
void void
trx_purge_add_update_undo_to_history( trx_purge_add_undo_to_history(const trx_t* trx, trx_undo_t*& undo, mtr_t* mtr)
/*=================================*/
trx_t* trx, /*!< in: transaction */
page_t* undo_page, /*!< in: update undo log header page,
x-latched */
mtr_t* mtr) /*!< in: mtr */
{ {
trx_undo_t* undo = trx->rsegs.m_redo.undo; ut_ad(undo == trx->rsegs.m_redo.undo
trx_rseg_t* rseg = undo->rseg; || undo == trx->rsegs.m_redo.old_insert);
trx_rseg_t* rseg = trx->rsegs.m_redo.rseg;
ut_ad(undo->rseg == rseg);
trx_rsegf_t* rseg_header = trx_rsegf_get( trx_rsegf_t* rseg_header = trx_rsegf_get(
rseg->space, rseg->page_no, mtr); rseg->space, rseg->page_no, mtr);
page_t* undo_page = trx_undo_set_state_at_finish(
undo, mtr);
trx_ulogf_t* undo_header = undo_page + undo->hdr_offset; trx_ulogf_t* undo_header = undo_page + undo->hdr_offset;
if (undo->state != TRX_UNDO_CACHED) { if (undo->state != TRX_UNDO_CACHED) {
...@@ -326,6 +327,16 @@ trx_purge_add_update_undo_to_history( ...@@ -326,6 +327,16 @@ trx_purge_add_update_undo_to_history(
rseg->last_trx_no = trx->no; rseg->last_trx_no = trx->no;
rseg->last_del_marks = undo->del_marks; rseg->last_del_marks = undo->del_marks;
} }
if (undo->state == TRX_UNDO_CACHED) {
UT_LIST_ADD_FIRST(rseg->undo_cached, undo);
MONITOR_INC(MONITOR_NUM_UNDO_SLOT_CACHED);
} else {
ut_ad(undo->state == TRX_UNDO_TO_PURGE);
trx_undo_mem_free(undo);
}
undo = NULL;
} }
/** Remove undo log header from the history list. /** Remove undo log header from the history list.
......
...@@ -1420,13 +1420,14 @@ trx_write_serialisation_history( ...@@ -1420,13 +1420,14 @@ trx_write_serialisation_history(
temp_mtr.commit(); temp_mtr.commit();
} }
if (!trx->rsegs.m_redo.rseg) { trx_rseg_t* rseg = trx->rsegs.m_redo.rseg;
if (!rseg) {
ut_ad(!trx->rsegs.m_redo.undo); ut_ad(!trx->rsegs.m_redo.undo);
ut_ad(!trx->rsegs.m_redo.old_insert); ut_ad(!trx->rsegs.m_redo.old_insert);
return false; return false;
} }
trx_undo_t* undo = trx->rsegs.m_redo.undo; trx_undo_t*& undo = trx->rsegs.m_redo.undo;
trx_undo_t*& old_insert = trx->rsegs.m_redo.old_insert; trx_undo_t*& old_insert = trx->rsegs.m_redo.old_insert;
if (!undo && !old_insert) { if (!undo && !old_insert) {
...@@ -1434,40 +1435,29 @@ trx_write_serialisation_history( ...@@ -1434,40 +1435,29 @@ trx_write_serialisation_history(
} }
ut_ad(!trx->read_only); ut_ad(!trx->read_only);
trx_rseg_t* undo_rseg = undo ? undo->rseg : NULL; trx_rseg_t* undo_rseg
ut_ad(!undo || undo->rseg == trx->rsegs.m_redo.rseg); = undo ? undo->rseg : old_insert ? old_insert->rseg : NULL;
mutex_enter(&trx->rsegs.m_redo.rseg->mutex); ut_ad(!undo || undo->rseg == rseg);
ut_ad(!old_insert || old_insert->rseg == rseg);
mutex_enter(&rseg->mutex);
/* Assign the transaction serialisation number and add any /* Assign the transaction serialisation number and add any
undo log to the purge queue. */ undo log to the purge queue. */
trx_serialise(trx, undo_rseg); trx_serialise(trx, undo_rseg);
/* It is not necessary to acquire trx->undo_mutex here because /* It is not necessary to acquire trx->undo_mutex here because
only a single OS thread is allowed to commit this transaction. */ only a single OS thread is allowed to commit this transaction.
The undo logs will be processed and purged later. */
if (UNIV_LIKELY_NULL(old_insert)) { if (UNIV_LIKELY_NULL(old_insert)) {
page_t* undo_hdr_page = trx_undo_set_state_at_finish(
old_insert, mtr);
trx_rseg_t* rseg = trx->rsegs.m_redo.rseg;
trx_purge_add_update_undo_to_history(trx, undo_hdr_page, mtr);
UT_LIST_REMOVE(rseg->old_insert_list, old_insert); UT_LIST_REMOVE(rseg->old_insert_list, old_insert);
trx_purge_add_undo_to_history(trx, old_insert, mtr);
if (old_insert->state == TRX_UNDO_CACHED) {
UT_LIST_ADD_FIRST(rseg->undo_cached, old_insert);
MONITOR_INC(MONITOR_NUM_UNDO_SLOT_CACHED);
} else {
ut_ad(old_insert->state == TRX_UNDO_TO_PURGE);
trx_undo_mem_free(old_insert);
}
old_insert = NULL;
} }
if (undo) { if (undo) {
/* The undo logs will be processed and purged later. */ UT_LIST_REMOVE(rseg->undo_list, undo);
page_t* undo_hdr_page = trx_undo_set_state_at_finish( trx_purge_add_undo_to_history(trx, undo, mtr);
undo, mtr);
trx_undo_update_cleanup(trx, undo_hdr_page, mtr);
} }
mutex_exit(&trx->rsegs.m_redo.rseg->mutex); mutex_exit(&rseg->mutex);
MONITOR_INC(MONITOR_TRX_COMMIT_UNDO); MONITOR_INC(MONITOR_TRX_COMMIT_UNDO);
...@@ -1915,7 +1905,7 @@ trx_commit_low( ...@@ -1915,7 +1905,7 @@ trx_commit_low(
assert_trx_nonlocking_or_in_list(trx); assert_trx_nonlocking_or_in_list(trx);
ut_ad(!trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)); ut_ad(!trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY));
ut_ad(!mtr || mtr->is_active()); ut_ad(!mtr || mtr->is_active());
ut_ad(!mtr == !trx->has_logged()); ut_ad(!mtr == !trx->has_logged_or_recovered());
/* undo_no is non-zero if we're doing the final commit. */ /* undo_no is non-zero if we're doing the final commit. */
if (trx->fts_trx != NULL && trx->undo_no != 0) { if (trx->fts_trx != NULL && trx->undo_no != 0) {
......
...@@ -1623,42 +1623,6 @@ trx_undo_set_state_at_prepare( ...@@ -1623,42 +1623,6 @@ trx_undo_set_state_at_prepare(
return(undo_page); return(undo_page);
} }
/**********************************************************************//**
Adds the update undo log header as the first in the history list, and
frees the memory object, or puts it to the list of cached update undo log
segments. */
void
trx_undo_update_cleanup(
/*====================*/
trx_t* trx, /*!< in: trx owning the update
undo log */
page_t* undo_page, /*!< in: update undo log header page,
x-latched */
mtr_t* mtr) /*!< in: mtr */
{
trx_undo_t* undo = trx->rsegs.m_redo.undo;
trx_rseg_t* rseg = undo->rseg;
ut_ad(mutex_own(&rseg->mutex));
trx_purge_add_update_undo_to_history(trx, undo_page, mtr);
UT_LIST_REMOVE(rseg->undo_list, undo);
trx->rsegs.m_redo.undo = NULL;
if (undo->state == TRX_UNDO_CACHED) {
UT_LIST_ADD_FIRST(rseg->undo_cached, undo);
MONITOR_INC(MONITOR_NUM_UNDO_SLOT_CACHED);
} else {
ut_ad(undo->state == TRX_UNDO_TO_PURGE);
trx_undo_mem_free(undo);
}
}
/** Free an old insert or temporary undo log after commit or rollback. /** Free an old insert or temporary undo log after commit or rollback.
The information is not needed after a commit or rollback, therefore The information is not needed after a commit or rollback, therefore
the data can be discarded. the data can be discarded.
......
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