Commit e0084b9d authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-31234 InnoDB does not free UNDO after the fix of MDEV-30671

trx_purge_truncate_history(): Only call trx_purge_truncate_rseg_history()
if the rollback segment is safe to process. This will avoid leaking undo
log pages that are not yet ready to be processed. This fixes a regression
that was introduced in
commit 0de3be8c (MDEV-30671).

trx_sys_t::any_active_transactions(): Separately count XA PREPARE
transactions.

srv_purge_should_exit(): Terminate slow shutdown if the history size
does not change and XA PREPARE transactions exist in the system.
This will avoid a hang of the test innodb.recovery_shutdown.

Tested by: Matthias Leich
parent b735ca47
...@@ -1055,7 +1055,7 @@ class trx_sys_t ...@@ -1055,7 +1055,7 @@ class trx_sys_t
void close(); void close();
/** @return total number of active (non-prepared) transactions */ /** @return total number of active (non-prepared) transactions */
ulint any_active_transactions(); size_t any_active_transactions(size_t *prepared= nullptr);
/** /**
......
...@@ -1697,7 +1697,7 @@ void srv_master_callback(void*) ...@@ -1697,7 +1697,7 @@ void srv_master_callback(void*)
} }
/** @return whether purge should exit due to shutdown */ /** @return whether purge should exit due to shutdown */
static bool srv_purge_should_exit() static bool srv_purge_should_exit(size_t old_history_size)
{ {
ut_ad(srv_shutdown_state <= SRV_SHUTDOWN_CLEANUP); ut_ad(srv_shutdown_state <= SRV_SHUTDOWN_CLEANUP);
...@@ -1708,7 +1708,12 @@ static bool srv_purge_should_exit() ...@@ -1708,7 +1708,12 @@ static bool srv_purge_should_exit()
return true; return true;
/* Slow shutdown was requested. */ /* Slow shutdown was requested. */
if (const size_t history_size= trx_sys.rseg_history_len) size_t prepared, active= trx_sys.any_active_transactions(&prepared);
const size_t history_size= trx_sys.rseg_history_len;
if (!history_size);
else if (!active && history_size == old_history_size && prepared);
else
{ {
static time_t progress_time; static time_t progress_time;
time_t now= time(NULL); time_t now= time(NULL);
...@@ -1725,7 +1730,7 @@ static bool srv_purge_should_exit() ...@@ -1725,7 +1730,7 @@ static bool srv_purge_should_exit()
return false; return false;
} }
return !trx_sys.any_active_transactions(); return !active;
} }
/*********************************************************************//** /*********************************************************************//**
...@@ -1845,7 +1850,7 @@ static size_t srv_do_purge(ulint* n_total_purged) ...@@ -1845,7 +1850,7 @@ static size_t srv_do_purge(ulint* n_total_purged)
*n_total_purged += n_pages_purged; *n_total_purged += n_pages_purged;
} while (n_pages_purged > 0 && !purge_sys.paused() } while (n_pages_purged > 0 && !purge_sys.paused()
&& !srv_purge_should_exit()); && !srv_purge_should_exit(rseg_history_len));
return(rseg_history_len); return(rseg_history_len);
} }
...@@ -1960,7 +1965,7 @@ static void purge_coordinator_callback_low() ...@@ -1960,7 +1965,7 @@ static void purge_coordinator_callback_low()
} }
} }
while ((purge_sys.enabled() && !purge_sys.paused()) || while ((purge_sys.enabled() && !purge_sys.paused()) ||
!srv_purge_should_exit()); !srv_purge_should_exit(trx_sys.rseg_history_len));
} }
static void purge_coordinator_callback(void*) static void purge_coordinator_callback(void*)
...@@ -2031,15 +2036,19 @@ ulint srv_get_task_queue_length() ...@@ -2031,15 +2036,19 @@ ulint srv_get_task_queue_length()
/** Shut down the purge threads. */ /** Shut down the purge threads. */
void srv_purge_shutdown() void srv_purge_shutdown()
{ {
if (purge_sys.enabled()) { if (purge_sys.enabled())
if (!srv_fast_shutdown && !opt_bootstrap) {
srv_update_purge_thread_count(innodb_purge_threads_MAX); if (!srv_fast_shutdown && !opt_bootstrap)
while(!srv_purge_should_exit()) { srv_update_purge_thread_count(innodb_purge_threads_MAX);
ut_a(!purge_sys.paused()); size_t history_size= trx_sys.rseg_history_len;
srv_wake_purge_thread_if_not_active(); while (!srv_purge_should_exit(history_size))
purge_coordinator_task.wait(); {
} history_size= trx_sys.rseg_history_len;
purge_sys.coordinator_shutdown(); ut_a(!purge_sys.paused());
srv_shutdown_purge_tasks(); srv_wake_purge_thread_if_not_active();
} purge_coordinator_task.wait();
}
purge_sys.coordinator_shutdown();
srv_shutdown_purge_tasks();
}
} }
...@@ -448,12 +448,7 @@ trx_purge_truncate_rseg_history( ...@@ -448,12 +448,7 @@ trx_purge_truncate_rseg_history(
prev_hdr_addr.boffset = static_cast<uint16_t>(prev_hdr_addr.boffset prev_hdr_addr.boffset = static_cast<uint16_t>(prev_hdr_addr.boffset
- TRX_UNDO_HISTORY_NODE); - TRX_UNDO_HISTORY_NODE);
if (!rseg.trx_ref_count if (mach_read_from_2(TRX_UNDO_SEG_HDR + TRX_UNDO_STATE + block->frame)
&& rseg.needs_purge <= (purge_sys.head.trx_no
? purge_sys.head.trx_no
: purge_sys.tail.trx_no)
&& mach_read_from_2(TRX_UNDO_SEG_HDR + TRX_UNDO_STATE
+ block->frame)
== TRX_UNDO_TO_PURGE == TRX_UNDO_TO_PURGE
&& !mach_read_from_2(block->frame + hdr_addr.boffset && !mach_read_from_2(block->frame + hdr_addr.boffset
+ TRX_UNDO_NEXT_LOG)) { + TRX_UNDO_NEXT_LOG)) {
...@@ -544,7 +539,8 @@ static void trx_purge_truncate_history() ...@@ -544,7 +539,8 @@ static void trx_purge_truncate_history()
ut_ad(rseg->id == i); ut_ad(rseg->id == i);
ut_ad(rseg->is_persistent()); ut_ad(rseg->is_persistent());
mutex_enter(&rseg->mutex); mutex_enter(&rseg->mutex);
trx_purge_truncate_rseg_history(*rseg, head); if (!rseg->trx_ref_count && rseg->needs_purge <= head.trx_no)
trx_purge_truncate_rseg_history(*rseg, head);
mutex_exit(&rseg->mutex); mutex_exit(&rseg->mutex);
} }
} }
......
...@@ -325,15 +325,29 @@ trx_sys_t::close() ...@@ -325,15 +325,29 @@ trx_sys_t::close()
} }
/** @return total number of active (non-prepared) transactions */ /** @return total number of active (non-prepared) transactions */
ulint trx_sys_t::any_active_transactions() size_t trx_sys_t::any_active_transactions(size_t *prepared)
{ {
uint32_t total_trx= 0; size_t total_trx= 0, prepared_trx= 0;
trx_sys.trx_list.for_each([&total_trx](const trx_t &trx) { trx_sys.trx_list.for_each([&](const trx_t &trx) {
if (trx.state == TRX_STATE_COMMITTED_IN_MEMORY || switch (trx.state) {
(trx.state == TRX_STATE_ACTIVE && trx.id)) case TRX_STATE_NOT_STARTED:
break;
case TRX_STATE_ACTIVE:
if (!trx.id)
break;
/* fall through */
case TRX_STATE_COMMITTED_IN_MEMORY:
total_trx++; total_trx++;
break;
case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED:
prepared_trx++;
}
}); });
if (prepared)
*prepared= prepared_trx;
return total_trx; return total_trx;
} }
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