2014-02-26 19:11:54 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
|
2017-05-15 17:17:16 +03:00
|
|
|
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
|
2018-02-15 09:50:03 +02:00
|
|
|
Copyright (c) 2017, 2018, MariaDB Corporation.
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
|
|
Foundation; version 2 of the License.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
|
|
this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
|
|
|
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/**************************************************//**
|
|
|
|
@file trx/trx0purge.cc
|
|
|
|
Purge old versions
|
|
|
|
|
|
|
|
Created 3/26/1996 Heikki Tuuri
|
|
|
|
*******************************************************/
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
#include "ha_prototypes.h"
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
#include "trx0purge.h"
|
|
|
|
#include "fsp0fsp.h"
|
|
|
|
#include "fut0fut.h"
|
2016-08-12 11:17:45 +03:00
|
|
|
#include "mach0data.h"
|
|
|
|
#include "mtr0log.h"
|
|
|
|
#include "os0thread.h"
|
2014-02-26 19:11:54 +01:00
|
|
|
#include "que0que.h"
|
|
|
|
#include "row0purge.h"
|
|
|
|
#include "row0upd.h"
|
2016-08-12 11:17:45 +03:00
|
|
|
#include "srv0mon.h"
|
|
|
|
#include "fsp0sysspace.h"
|
2014-02-26 19:11:54 +01:00
|
|
|
#include "srv0srv.h"
|
|
|
|
#include "srv0start.h"
|
2016-08-12 11:17:45 +03:00
|
|
|
#include "sync0sync.h"
|
|
|
|
#include "trx0rec.h"
|
|
|
|
#include "trx0roll.h"
|
|
|
|
#include "trx0rseg.h"
|
|
|
|
#include "trx0trx.h"
|
2018-02-20 15:10:03 +02:00
|
|
|
#include <mysql/service_wsrep.h>
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/** Maximum allowable purge history length. <=0 means 'infinite'. */
|
2016-08-12 11:17:45 +03:00
|
|
|
ulong srv_max_purge_lag = 0;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/** Max DML user threads delay in micro-seconds. */
|
2016-08-12 11:17:45 +03:00
|
|
|
ulong srv_max_purge_lag_delay = 0;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/** The global data structure coordinating a purge */
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys_t purge_sys;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/** A dummy undo record used as a return value when we have a whole undo log
|
|
|
|
which needs no purge */
|
2016-08-12 11:17:45 +03:00
|
|
|
trx_undo_rec_t trx_purge_dummy_rec;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
2016-08-12 11:17:45 +03:00
|
|
|
my_bool srv_purge_view_update_only_debug;
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** Sentinel value */
|
2018-02-21 18:04:25 +02:00
|
|
|
static const TrxUndoRsegs NullElement;
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2018-02-21 18:04:25 +02:00
|
|
|
/** Default constructor */
|
2018-02-22 09:30:41 +02:00
|
|
|
TrxUndoRsegsIterator::TrxUndoRsegsIterator()
|
2018-02-21 18:04:25 +02:00
|
|
|
: m_rsegs(NullElement), m_iter(m_rsegs.begin())
|
2016-08-12 11:17:45 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
/** Sets the next rseg to purge in purge_sys.
|
2018-02-21 18:04:25 +02:00
|
|
|
Executed in the purge coordinator thread.
|
2017-03-09 22:06:22 +02:00
|
|
|
@return whether anything is to be purged */
|
2018-02-21 18:04:25 +02:00
|
|
|
inline bool TrxUndoRsegsIterator::set_next()
|
2016-08-12 11:17:45 +03:00
|
|
|
{
|
2018-02-22 09:30:41 +02:00
|
|
|
mutex_enter(&purge_sys.pq_mutex);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
/* Only purge consumes events from the priority queue, user
|
|
|
|
threads only produce the events. */
|
|
|
|
|
|
|
|
/* Check if there are more rsegs to process in the
|
|
|
|
current element. */
|
2018-02-21 18:04:25 +02:00
|
|
|
if (m_iter != m_rsegs.end()) {
|
2016-08-12 11:17:45 +03:00
|
|
|
/* We are still processing rollback segment from
|
|
|
|
the same transaction and so expected transaction
|
2018-02-21 18:04:25 +02:00
|
|
|
number shouldn't increase. Undo the increment of
|
2018-02-21 19:02:32 +02:00
|
|
|
expected commit done by caller assuming rollback
|
2016-08-12 11:17:45 +03:00
|
|
|
segments from given transaction are done. */
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.tail.commit = (*m_iter)->last_commit;
|
|
|
|
} else if (!purge_sys.purge_queue.empty()) {
|
|
|
|
m_rsegs = purge_sys.purge_queue.top();
|
|
|
|
purge_sys.purge_queue.pop();
|
|
|
|
ut_ad(purge_sys.purge_queue.empty()
|
|
|
|
|| purge_sys.purge_queue.top() != m_rsegs);
|
2018-02-21 18:04:25 +02:00
|
|
|
m_iter = m_rsegs.begin();
|
2016-08-12 11:17:45 +03:00
|
|
|
} else {
|
|
|
|
/* Queue is empty, reset iterator. */
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.rseg = NULL;
|
|
|
|
mutex_exit(&purge_sys.pq_mutex);
|
2018-02-21 18:04:25 +02:00
|
|
|
m_rsegs = NullElement;
|
|
|
|
m_iter = m_rsegs.begin();
|
2017-03-09 22:06:22 +02:00
|
|
|
return false;
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.rseg = *m_iter++;
|
|
|
|
mutex_exit(&purge_sys.pq_mutex);
|
|
|
|
mutex_enter(&purge_sys.rseg->mutex);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_a(purge_sys.rseg->last_page_no != FIL_NULL);
|
|
|
|
ut_ad(purge_sys.rseg->last_trx_no() == m_rsegs.trx_no());
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2017-05-23 11:09:47 +03:00
|
|
|
/* We assume in purge of externally stored fields that space id is
|
|
|
|
in the range of UNDO tablespace space ids */
|
2018-03-26 17:23:47 +03:00
|
|
|
ut_ad(purge_sys.rseg->space->id == TRX_SYS_SPACE
|
|
|
|
|| srv_is_undo_tablespace(purge_sys.rseg->space->id));
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_a(purge_sys.tail.commit <= purge_sys.rseg->last_commit);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.tail.commit = purge_sys.rseg->last_commit;
|
|
|
|
purge_sys.hdr_offset = purge_sys.rseg->last_offset;
|
|
|
|
purge_sys.hdr_page_no = purge_sys.rseg->last_page_no;
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
mutex_exit(&purge_sys.rseg->mutex);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2017-03-09 22:06:22 +02:00
|
|
|
return(true);
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
/** Build a purge 'query' graph. The actual purge is performed by executing
|
2014-02-26 19:11:54 +01:00
|
|
|
this query graph.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return own: the query graph */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
que_t*
|
2018-02-15 09:50:03 +02:00
|
|
|
purge_graph_build()
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2017-03-09 20:40:48 +02:00
|
|
|
ut_a(srv_n_purge_threads > 0);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-15 09:50:03 +02:00
|
|
|
trx_t* trx = trx_allocate_for_background();
|
|
|
|
ut_ad(!trx->id);
|
2017-03-09 20:40:48 +02:00
|
|
|
trx->start_time = ut_time();
|
|
|
|
trx->state = TRX_STATE_ACTIVE;
|
|
|
|
trx->op_info = "purge trx";
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
mem_heap_t* heap = mem_heap_create(512);
|
|
|
|
que_fork_t* fork = que_fork_create(
|
|
|
|
NULL, NULL, QUE_FORK_PURGE, heap);
|
|
|
|
fork->trx = trx;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
for (ulint i = 0; i < srv_n_purge_threads; ++i) {
|
|
|
|
que_thr_t* thr = que_thr_create(fork, heap, NULL);
|
2014-02-26 19:11:54 +01:00
|
|
|
thr->child = row_purge_node_create(thr, heap);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(fork);
|
|
|
|
}
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
/** Initialise the purge system. */
|
|
|
|
void purge_sys_t::create()
|
2017-03-09 20:40:48 +02:00
|
|
|
{
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_ad(this == &purge_sys);
|
|
|
|
ut_ad(!is_initialised());
|
|
|
|
event= os_event_create(0);
|
|
|
|
n_stop= 0;
|
|
|
|
running= false;
|
|
|
|
state= PURGE_STATE_INIT;
|
|
|
|
query= purge_graph_build();
|
|
|
|
n_submitted= 0;
|
|
|
|
n_completed= 0;
|
|
|
|
next_stored= false;
|
|
|
|
rseg= NULL;
|
|
|
|
page_no= 0;
|
|
|
|
offset= 0;
|
|
|
|
hdr_page_no= 0;
|
|
|
|
hdr_offset= 0;
|
|
|
|
rw_lock_create(trx_purge_latch_key, &latch, SYNC_PURGE_LATCH);
|
|
|
|
mutex_create(LATCH_ID_PURGE_SYS_PQ, &pq_mutex);
|
|
|
|
undo_trunc.create();
|
|
|
|
m_initialised = true;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
/** Close the purge subsystem on shutdown. */
|
|
|
|
void purge_sys_t::close()
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_ad(this == &purge_sys);
|
|
|
|
if (!is_initialised()) return;
|
2017-03-09 20:40:48 +02:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
m_initialised = false;
|
2018-02-15 09:50:03 +02:00
|
|
|
trx_t* trx = query->trx;
|
2017-03-09 20:40:48 +02:00
|
|
|
que_graph_free(query);
|
2018-02-15 09:50:03 +02:00
|
|
|
ut_ad(!trx->id);
|
|
|
|
ut_ad(trx->state == TRX_STATE_ACTIVE);
|
|
|
|
trx->state = TRX_STATE_NOT_STARTED;
|
|
|
|
trx_free_for_background(trx);
|
2017-03-09 20:40:48 +02:00
|
|
|
rw_lock_free(&latch);
|
|
|
|
/* rw_lock_free() already called latch.~rw_lock_t(); tame the
|
|
|
|
debug assertions when the destructor will be called once more. */
|
|
|
|
ut_ad(latch.magic_n == 0);
|
|
|
|
ut_d(latch.magic_n = RW_LOCK_MAGIC_N);
|
|
|
|
mutex_free(&pq_mutex);
|
|
|
|
os_event_destroy(event);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*================ UNDO LOG HISTORY LIST =============================*/
|
|
|
|
|
2017-08-11 12:47:54 +03:00
|
|
|
/** Prepend the history list with an undo log.
|
|
|
|
Remove the 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 */
|
2014-02-26 19:11:54 +01:00
|
|
|
void
|
2017-08-11 12:47:54 +03:00
|
|
|
trx_purge_add_undo_to_history(const trx_t* trx, trx_undo_t*& undo, mtr_t* mtr)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2018-02-20 15:10:03 +02:00
|
|
|
DBUG_PRINT("trx", ("commit(" TRX_ID_FMT "," TRX_ID_FMT ")",
|
|
|
|
trx->id, trx->no));
|
2017-08-11 12:47:54 +03:00
|
|
|
ut_ad(undo == trx->rsegs.m_redo.undo
|
|
|
|
|| undo == trx->rsegs.m_redo.old_insert);
|
|
|
|
trx_rseg_t* rseg = trx->rsegs.m_redo.rseg;
|
|
|
|
ut_ad(undo->rseg == rseg);
|
MDEV-12219 Discard temporary undo logs at transaction commit
Starting with MySQL 5.7, temporary tables in InnoDB are handled
differently from persistent tables. Because temporary tables are
private to a connection, concurrency control and multi-versioning
(MVCC) are not applicable. For performance reasons, purge is
disabled as well. Rollback is supported for temporary tables;
that is why we have the temporary undo logs in the first place.
Because MVCC and purge are disabled for temporary tables, we should
discard all temporary undo logs already at transaction commit,
just like we discard the persistent insert_undo logs. Before this
change, update_undo logs were being preserved.
trx_temp_undo_t: A wrapper for temporary undo logs, comprising
a rollback segment and a single temporary undo log.
trx_rsegs_t::m_noredo: Use trx_temp_undo_t.
(Instead of insert_undo, update_undo, there will be a single undo.)
trx_is_noredo_rseg_updated(), trx_is_rseg_assigned(): Remove.
trx_undo_add_page(): Remove the parameter undo_ptr.
Acquire and release the rollback segment mutex inside the function.
trx_undo_free_last_page(): Remove the parameter trx.
trx_undo_truncate_end(): Remove the parameter trx, and add the
parameter is_temp. Clean up the code a bit.
trx_undo_assign_undo(): Split the parameter undo_ptr into rseg, undo.
trx_undo_commit_cleanup(): Renamed from trx_undo_insert_cleanup().
Replace the parameter undo_ptr with undo.
This will discard the temporary undo or insert_undo log at
commit/rollback.
trx_purge_add_update_undo_to_history(), trx_undo_update_cleanup():
Remove 3 parameters. Always operate on the persistent update_undo.
trx_serialise(): Renamed from trx_serialisation_number_get().
trx_write_serialisation_history(): Simplify the code flow.
If there are no persistent changes, do not update MONITOR_TRX_COMMIT_UNDO.
trx_commit_in_memory(): Simplify the logic, and add assertions.
trx_undo_page_report_modify(): Keep a direct reference to the
persistent update_undo log.
trx_undo_report_row_operation(): Simplify some code.
Always assign TRX_UNDO_INSERT for temporary undo logs.
trx_prepare_low(): Keep only one parameter. Prepare all 3 undo logs.
trx_roll_try_truncate(): Remove the parameter undo_ptr.
Try to truncate all 3 undo logs of the transaction.
trx_roll_pop_top_rec_of_trx_low(): Remove.
trx_roll_pop_top_rec_of_trx(): Remove the redundant parameter
trx->roll_limit. Clear roll_limit when exhausting the undo logs.
Consider all 3 undo logs at once, prioritizing the persistent
undo logs.
row_undo(): Minor cleanup. Let trx_roll_pop_top_rec_of_trx()
reset the trx->roll_limit.
2017-03-09 23:20:51 +02:00
|
|
|
trx_rsegf_t* rseg_header = trx_rsegf_get(
|
|
|
|
rseg->space, rseg->page_no, mtr);
|
2017-08-11 12:47:54 +03:00
|
|
|
page_t* undo_page = trx_undo_set_state_at_finish(
|
|
|
|
undo, mtr);
|
MDEV-12219 Discard temporary undo logs at transaction commit
Starting with MySQL 5.7, temporary tables in InnoDB are handled
differently from persistent tables. Because temporary tables are
private to a connection, concurrency control and multi-versioning
(MVCC) are not applicable. For performance reasons, purge is
disabled as well. Rollback is supported for temporary tables;
that is why we have the temporary undo logs in the first place.
Because MVCC and purge are disabled for temporary tables, we should
discard all temporary undo logs already at transaction commit,
just like we discard the persistent insert_undo logs. Before this
change, update_undo logs were being preserved.
trx_temp_undo_t: A wrapper for temporary undo logs, comprising
a rollback segment and a single temporary undo log.
trx_rsegs_t::m_noredo: Use trx_temp_undo_t.
(Instead of insert_undo, update_undo, there will be a single undo.)
trx_is_noredo_rseg_updated(), trx_is_rseg_assigned(): Remove.
trx_undo_add_page(): Remove the parameter undo_ptr.
Acquire and release the rollback segment mutex inside the function.
trx_undo_free_last_page(): Remove the parameter trx.
trx_undo_truncate_end(): Remove the parameter trx, and add the
parameter is_temp. Clean up the code a bit.
trx_undo_assign_undo(): Split the parameter undo_ptr into rseg, undo.
trx_undo_commit_cleanup(): Renamed from trx_undo_insert_cleanup().
Replace the parameter undo_ptr with undo.
This will discard the temporary undo or insert_undo log at
commit/rollback.
trx_purge_add_update_undo_to_history(), trx_undo_update_cleanup():
Remove 3 parameters. Always operate on the persistent update_undo.
trx_serialise(): Renamed from trx_serialisation_number_get().
trx_write_serialisation_history(): Simplify the code flow.
If there are no persistent changes, do not update MONITOR_TRX_COMMIT_UNDO.
trx_commit_in_memory(): Simplify the logic, and add assertions.
trx_undo_page_report_modify(): Keep a direct reference to the
persistent update_undo log.
trx_undo_report_row_operation(): Simplify some code.
Always assign TRX_UNDO_INSERT for temporary undo logs.
trx_prepare_low(): Keep only one parameter. Prepare all 3 undo logs.
trx_roll_try_truncate(): Remove the parameter undo_ptr.
Try to truncate all 3 undo logs of the transaction.
trx_roll_pop_top_rec_of_trx_low(): Remove.
trx_roll_pop_top_rec_of_trx(): Remove the redundant parameter
trx->roll_limit. Clear roll_limit when exhausting the undo logs.
Consider all 3 undo logs at once, prioritizing the persistent
undo logs.
row_undo(): Minor cleanup. Let trx_roll_pop_top_rec_of_trx()
reset the trx->roll_limit.
2017-03-09 23:20:51 +02:00
|
|
|
trx_ulogf_t* undo_header = undo_page + undo->hdr_offset;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-08-15 17:18:55 +03:00
|
|
|
ut_ad(mach_read_from_2(undo_header + TRX_UNDO_NEEDS_PURGE) <= 1);
|
|
|
|
|
2018-02-20 15:10:03 +02:00
|
|
|
if (UNIV_UNLIKELY(mach_read_from_4(TRX_RSEG_FORMAT + rseg_header))) {
|
|
|
|
/* This database must have been upgraded from
|
|
|
|
before MariaDB 10.3.5. */
|
|
|
|
trx_rseg_format_upgrade(rseg_header, mtr);
|
|
|
|
}
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
if (undo->state != TRX_UNDO_CACHED) {
|
|
|
|
ulint hist_size;
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
trx_usegf_t* seg_header = undo_page + TRX_UNDO_SEG_HDR;
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
|
|
|
/* The undo log segment will not be reused */
|
2018-02-20 15:10:03 +02:00
|
|
|
ut_a(undo->id < TRX_RSEG_N_SLOTS);
|
2014-02-26 19:11:54 +01:00
|
|
|
trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr);
|
|
|
|
|
|
|
|
MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_USED);
|
|
|
|
|
|
|
|
hist_size = mtr_read_ulint(
|
|
|
|
rseg_header + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, mtr);
|
|
|
|
|
|
|
|
ut_ad(undo->size == flst_get_len(
|
2016-08-12 11:17:45 +03:00
|
|
|
seg_header + TRX_UNDO_PAGE_LIST));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mlog_write_ulint(
|
|
|
|
rseg_header + TRX_RSEG_HISTORY_SIZE,
|
|
|
|
hist_size + undo->size, MLOG_4BYTES, mtr);
|
|
|
|
|
MDEV-15443 Properly read wsrep XID and binlog position from rollback segment headers
The problem is a regression caused by MDEV-15158.
If some transactions were committed with wsrep_on=0, a
rollback segment header having the highest trx_id assigned might
store undefined wsrep XID. When reading the wsrep checkpoint
from InnodB, the undefined wsrep XID might be returned instead
of the highest valid one.
Similarly, if the binary log is intermittently disabled or enabled
while InnoDB transactions are being committed, the latest updated
rollback segment header page might not contain the latest binlog metadata.
Therefore, the MDEV-15158 logic to rely on TRX_RSEG_MAX_TRX_ID for
determining the most recent WSREP XID or binlog position is invalid.
We must choose the maximum entries among the rollback segment header
pages.
This fix is based on code submitted by Teemu Ollakka from Codership
and by Thirunarayanan Balathandayuthapani from MariaDB Corporation.
trx_purge_add_undo_to_history(): Only write TRX_RSEG_MAX_TRX_ID
when it was used to be written before MDEV-15158.
wsrep_seqno: Renamed from trx_sys_cur_xid_seqno.
wsrep_uuid: Renamed from trx_sys_cur_xid_uuid, and enable in non-debug
builds.
read_wsrep_xid_uuid(): Make non-debug, and remove the memcpy().
trx_rseg_update_wsrep_checkpoint(): Correctly compare and copy
the entire UUID in the debug check. In case of UUID mismatch,
write the WSREP XID to all 128 rollback segment headers in
a single mini-transaction.
trx_rseg_read_wsrep_checkpoint(rseg_header, xid): Make static.
In case the information is absent, do not overwrite xid.
trx_rseg_read_wsrep_checkpoint(xid): Determine the maximum
WSREP XID.
trx_rseg_mem_restore(): Remove the parameter max_rseg_trx_id.
Determine the latest binlog file and position by comparing
file names and offsets. Declare trx_sys.recovered_binlog_offset
as an unsigned type.
2018-03-06 23:29:38 +02:00
|
|
|
mlog_write_ull(rseg_header + TRX_RSEG_MAX_TRX_ID,
|
|
|
|
trx_sys.get_max_trx_id(), mtr);
|
|
|
|
}
|
2018-02-20 15:10:03 +02:00
|
|
|
|
MDEV-13039 innodb_fast_shutdown=0 may fail to purge all undo log
When a slow shutdown is performed soon after spawning some work for
background threads that can create or commit transactions, it is possible
that new transactions are started or committed after the purge has finished.
This is violating the specification of innodb_fast_shutdown=0, namely that
the purge must be completed. (None of the history of the recent transactions
would be purged.)
Also, it is possible that the purge threads would exit in slow shutdown
while there exist active transactions, such as recovered incomplete
transactions that are being rolled back. Thus, the slow shutdown could
fail to purge some undo log that becomes purgeable after the transaction
commit or rollback.
srv_undo_sources: A flag that indicates if undo log can be generated
or the persistent, whether by background threads or by user SQL.
Even when this flag is clear, active transactions that already exist
in the system may be committed or rolled back.
innodb_shutdown(): Renamed from innobase_shutdown_for_mysql().
Do not return an error code; the operation never fails.
Clear the srv_undo_sources flag, and also ensure that the background
DROP TABLE queue is empty.
srv_purge_should_exit(): Do not allow the purge to exit if
srv_undo_sources are active or the background DROP TABLE queue is not
empty, or in slow shutdown, if any active transactions exist
(and are being rolled back).
srv_purge_coordinator_thread(): Remove some previous workarounds
for this bug.
innobase_start_or_create_for_mysql(): Set buf_page_cleaner_is_active
and srv_dict_stats_thread_active directly. Set srv_undo_sources before
starting the purge subsystem, to prevent immediate shutdown of the purge.
Create dict_stats_thread and fts_optimize_thread immediately
after setting srv_undo_sources, so that shutdown can use this flag to
determine if these subsystems were started.
dict_stats_shutdown(): Shut down dict_stats_thread. Backported from 10.2.
srv_shutdown_table_bg_threads(): Remove (unused).
2017-06-08 15:43:06 +03:00
|
|
|
/* Before any transaction-generating background threads or the
|
|
|
|
purge have been started, recv_recovery_rollback_active() can
|
|
|
|
start transactions in row_merge_drop_temp_indexes() and
|
|
|
|
fts_drop_orphaned_tables(), and roll back recovered transactions.
|
2017-06-23 09:46:51 +03:00
|
|
|
|
|
|
|
Arbitrary user transactions may be executed when all the undo log
|
|
|
|
related background processes (including purge) are disabled due to
|
|
|
|
innodb_force_recovery=2 or innodb_force_recovery=3.
|
|
|
|
DROP TABLE may be executed at any innodb_force_recovery level.
|
|
|
|
|
MDEV-13039 innodb_fast_shutdown=0 may fail to purge all undo log
When a slow shutdown is performed soon after spawning some work for
background threads that can create or commit transactions, it is possible
that new transactions are started or committed after the purge has finished.
This is violating the specification of innodb_fast_shutdown=0, namely that
the purge must be completed. (None of the history of the recent transactions
would be purged.)
Also, it is possible that the purge threads would exit in slow shutdown
while there exist active transactions, such as recovered incomplete
transactions that are being rolled back. Thus, the slow shutdown could
fail to purge some undo log that becomes purgeable after the transaction
commit or rollback.
srv_undo_sources: A flag that indicates if undo log can be generated
or the persistent, whether by background threads or by user SQL.
Even when this flag is clear, active transactions that already exist
in the system may be committed or rolled back.
innodb_shutdown(): Renamed from innobase_shutdown_for_mysql().
Do not return an error code; the operation never fails.
Clear the srv_undo_sources flag, and also ensure that the background
DROP TABLE queue is empty.
srv_purge_should_exit(): Do not allow the purge to exit if
srv_undo_sources are active or the background DROP TABLE queue is not
empty, or in slow shutdown, if any active transactions exist
(and are being rolled back).
srv_purge_coordinator_thread(): Remove some previous workarounds
for this bug.
innobase_start_or_create_for_mysql(): Set buf_page_cleaner_is_active
and srv_dict_stats_thread_active directly. Set srv_undo_sources before
starting the purge subsystem, to prevent immediate shutdown of the purge.
Create dict_stats_thread and fts_optimize_thread immediately
after setting srv_undo_sources, so that shutdown can use this flag to
determine if these subsystems were started.
dict_stats_shutdown(): Shut down dict_stats_thread. Backported from 10.2.
srv_shutdown_table_bg_threads(): Remove (unused).
2017-06-08 15:43:06 +03:00
|
|
|
After the purge thread has been given permission to exit,
|
|
|
|
in fast shutdown, we may roll back transactions (trx->undo_no==0)
|
2017-08-08 19:54:12 +03:00
|
|
|
in THD::cleanup() invoked from unlink_thd(), and we may also
|
|
|
|
continue to execute user transactions. */
|
MDEV-13039 innodb_fast_shutdown=0 may fail to purge all undo log
When a slow shutdown is performed soon after spawning some work for
background threads that can create or commit transactions, it is possible
that new transactions are started or committed after the purge has finished.
This is violating the specification of innodb_fast_shutdown=0, namely that
the purge must be completed. (None of the history of the recent transactions
would be purged.)
Also, it is possible that the purge threads would exit in slow shutdown
while there exist active transactions, such as recovered incomplete
transactions that are being rolled back. Thus, the slow shutdown could
fail to purge some undo log that becomes purgeable after the transaction
commit or rollback.
srv_undo_sources: A flag that indicates if undo log can be generated
or the persistent, whether by background threads or by user SQL.
Even when this flag is clear, active transactions that already exist
in the system may be committed or rolled back.
innodb_shutdown(): Renamed from innobase_shutdown_for_mysql().
Do not return an error code; the operation never fails.
Clear the srv_undo_sources flag, and also ensure that the background
DROP TABLE queue is empty.
srv_purge_should_exit(): Do not allow the purge to exit if
srv_undo_sources are active or the background DROP TABLE queue is not
empty, or in slow shutdown, if any active transactions exist
(and are being rolled back).
srv_purge_coordinator_thread(): Remove some previous workarounds
for this bug.
innobase_start_or_create_for_mysql(): Set buf_page_cleaner_is_active
and srv_dict_stats_thread_active directly. Set srv_undo_sources before
starting the purge subsystem, to prevent immediate shutdown of the purge.
Create dict_stats_thread and fts_optimize_thread immediately
after setting srv_undo_sources, so that shutdown can use this flag to
determine if these subsystems were started.
dict_stats_shutdown(): Shut down dict_stats_thread. Backported from 10.2.
srv_shutdown_table_bg_threads(): Remove (unused).
2017-06-08 15:43:06 +03:00
|
|
|
ut_ad(srv_undo_sources
|
|
|
|
|| ((srv_startup_is_before_trx_rollback_phase
|
2017-12-21 15:45:40 +04:00
|
|
|
|| trx_rollback_is_active)
|
2018-02-22 09:30:41 +02:00
|
|
|
&& purge_sys.state == PURGE_STATE_INIT)
|
2017-06-21 12:22:02 +03:00
|
|
|
|| (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND
|
2018-02-22 09:30:41 +02:00
|
|
|
&& purge_sys.state == PURGE_STATE_DISABLED)
|
2017-12-04 16:26:14 +02:00
|
|
|
|| ((trx->undo_no == 0 || trx->in_mysql_trx_list
|
MDEV-14941 Timeouts on persistent statistics tables caused by MDEV-14511
MDEV-14511 tried to avoid some consistency problems related to InnoDB
persistent statistics. The persistent statistics are being written by
an InnoDB internal SQL interpreter that requires the InnoDB data dictionary
cache to be locked.
Before MDEV-14511, the statistics were written during DDL in separate
transactions, which could unnecessarily reduce performance (each commit
would require a redo log flush) and break atomicity, because the statistics
would be updated separately from the dictionary transaction.
However, because it is unacceptable to hold the InnoDB data dictionary
cache locked while suspending the execution for waiting for a
transactional lock (in the mysql.innodb_index_stats or
mysql.innodb_table_stats tables) to be released, any lock conflict
was immediately be reported as "lock wait timeout".
To fix MDEV-14941, an attempt to reduce these lock conflicts by acquiring
transactional locks on the user tables in both the statistics and DDL
operations was made, but it would still not entirely prevent lock conflicts
on the mysql.innodb_index_stats and mysql.innodb_table_stats tables.
Fixing the remaining problems would require a change that is too intrusive
for a GA release series, such as MariaDB 10.2.
Thefefore, we revert the change MDEV-14511. To silence the
MDEV-13201 assertion, we use the pre-existing flag trx_t::internal.
2018-01-21 18:23:28 +02:00
|
|
|
|| trx->internal)
|
2017-08-08 19:54:12 +03:00
|
|
|
&& srv_fast_shutdown));
|
2016-09-06 09:43:16 +03:00
|
|
|
|
2018-02-20 15:10:03 +02:00
|
|
|
#ifdef WITH_WSREP
|
|
|
|
if (wsrep_is_wsrep_xid(trx->xid)) {
|
|
|
|
trx_rseg_update_wsrep_checkpoint(rseg_header, trx->xid, mtr);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (trx->mysql_log_file_name && *trx->mysql_log_file_name) {
|
|
|
|
/* Update the latest MySQL binlog name and offset info
|
|
|
|
in rollback segment header if MySQL binlogging is on
|
|
|
|
or the database server is a MySQL replication save. */
|
|
|
|
trx_rseg_update_binlog_offset(rseg_header, trx, mtr);
|
|
|
|
}
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/* Add the log as the first in the history list */
|
|
|
|
flst_add_first(rseg_header + TRX_RSEG_HISTORY,
|
|
|
|
undo_header + TRX_UNDO_HISTORY_NODE, mtr);
|
|
|
|
|
|
|
|
mlog_write_ull(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr);
|
2017-08-15 17:18:55 +03:00
|
|
|
/* This is needed for upgrading old undo log pages from
|
|
|
|
before MariaDB 10.3.1. */
|
|
|
|
if (UNIV_UNLIKELY(!mach_read_from_2(undo_header
|
|
|
|
+ TRX_UNDO_NEEDS_PURGE))) {
|
|
|
|
mlog_write_ulint(undo_header + TRX_UNDO_NEEDS_PURGE, 1,
|
2014-02-26 19:11:54 +01:00
|
|
|
MLOG_2BYTES, mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rseg->last_page_no == FIL_NULL) {
|
|
|
|
rseg->last_page_no = undo->hdr_page_no;
|
|
|
|
rseg->last_offset = undo->hdr_offset;
|
2018-02-21 19:02:32 +02:00
|
|
|
rseg->set_last_trx_no(trx->no, undo == trx->rsegs.m_redo.undo);
|
2017-08-15 17:18:55 +03:00
|
|
|
rseg->needs_purge = true;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
2017-08-11 12:47:54 +03:00
|
|
|
|
2018-02-20 10:02:42 +02:00
|
|
|
trx_sys.history_insert();
|
|
|
|
|
2017-08-11 12:47:54 +03:00
|
|
|
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);
|
2018-02-20 10:16:52 +02:00
|
|
|
ut_free(undo);
|
2017-08-11 12:47:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
undo = NULL;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2017-01-04 18:16:37 +02:00
|
|
|
/** Remove undo log header from the history list.
|
|
|
|
@param[in,out] rseg_hdr rollback segment header
|
|
|
|
@param[in] log_hdr undo log segment header
|
|
|
|
@param[in,out] mtr mini transaction. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
trx_purge_remove_log_hdr(
|
|
|
|
trx_rsegf_t* rseg_hdr,
|
|
|
|
trx_ulogf_t* log_hdr,
|
|
|
|
mtr_t* mtr)
|
|
|
|
{
|
|
|
|
flst_remove(rseg_hdr + TRX_RSEG_HISTORY,
|
|
|
|
log_hdr + TRX_UNDO_HISTORY_NODE, mtr);
|
2018-02-20 10:02:42 +02:00
|
|
|
trx_sys.history_remove();
|
2017-01-04 18:16:37 +02:00
|
|
|
}
|
|
|
|
|
MDEV-12289 Keep 128 persistent rollback segments for compatibility and performance
InnoDB divides the allocation of undo logs into rollback segments.
The DB_ROLL_PTR system column of clustered indexes can address up to
128 rollback segments (TRX_SYS_N_RSEGS). Originally, InnoDB only
created one rollback segment. In MySQL 5.5 or in the InnoDB Plugin
for MySQL 5.1, all 128 rollback segments were created.
MySQL 5.7 hard-codes the rollback segment IDs 1..32 for temporary undo logs.
On upgrade, unless a slow shutdown (innodb_fast_shutdown=0)
was performed on the old server instance, these rollback segments
could be in use by transactions that are in XA PREPARE state or
transactions that were left behind by a server kill followed by a
normal shutdown immediately after restart.
Persistent tables cannot refer to temporary undo logs or vice versa.
Therefore, we should keep two distinct sets of rollback segments:
one for persistent tables and another for temporary tables. In this way,
all 128 rollback segments will be available for both types of tables,
which could improve performance. Also, MariaDB 10.2 will remain more
compatible than MySQL 5.7 with data files from earlier versions of
MySQL or MariaDB.
trx_sys_t::temp_rsegs[TRX_SYS_N_RSEGS]: A new array of temporary
rollback segments. The trx_sys_t::rseg_array[TRX_SYS_N_RSEGS] will
be solely for persistent undo logs.
srv_tmp_undo_logs. Remove. Use the constant TRX_SYS_N_RSEGS.
srv_available_undo_logs: Change the type to ulong.
trx_rseg_get_on_id(): Remove. Instead, let the callers refer to
trx_sys directly.
trx_rseg_create(), trx_sysf_rseg_find_free(): Remove unneeded parameters.
These functions only deal with persistent undo logs.
trx_temp_rseg_create(): New function, to create all temporary rollback
segments at server startup.
trx_rseg_t::is_persistent(): Determine if the rollback segment is for
persistent tables.
trx_sys_is_noredo_rseg_slot(): Remove. The callers must know based on
context (such as table handle) whether the DB_ROLL_PTR is referring to
a persistent undo log.
trx_sys_create_rsegs(): Remove all parameters, which were always passed
as global variables. Instead, modify the global variables directly.
enum trx_rseg_type_t: Remove.
trx_t::get_temp_rseg(): A method to ensure that a temporary
rollback segment has been assigned for the transaction.
trx_t::assign_temp_rseg(): Replaces trx_assign_rseg().
trx_purge_free_segment(), trx_purge_truncate_rseg_history():
Remove the redundant variable noredo=false.
Temporary undo logs are discarded immediately at transaction commit
or rollback, not lazily by purge.
trx_purge_mark_undo_for_truncate(): Remove references to the
temporary rollback segments.
trx_purge_mark_undo_for_truncate(): Remove a check for temporary
rollback segments. Only the dedicated persistent undo log tablespaces
can be truncated.
trx_undo_get_undo_rec_low(), trx_undo_get_undo_rec(): Add the
parameter is_temp.
trx_rseg_mem_restore(): Split from trx_rseg_mem_create().
Initialize the undo log and the rollback segment from the file
data structures.
trx_sysf_get_n_rseg_slots(): Renamed from
trx_sysf_used_slots_for_redo_rseg(). Count the persistent
rollback segment headers that have been initialized.
trx_sys_close(): Also free trx_sys->temp_rsegs[].
get_next_redo_rseg(): Merged to trx_assign_rseg_low().
trx_assign_rseg_low(): Remove the parameters and access the
global variables directly. Revert to simple round-robin, now that
the whole trx_sys->rseg_array[] is for persistent undo log again.
get_next_noredo_rseg(): Moved to trx_t::assign_temp_rseg().
srv_undo_tablespaces_init(): Remove some parameters and use the
global variables directly. Clarify some error messages.
Adjust the test innodb.log_file. Apparently, before these changes,
InnoDB somehow ignored missing dedicated undo tablespace files that
are pointed by the TRX_SYS header page, possibly losing part of
essential transaction system state.
2017-03-30 13:11:34 +03:00
|
|
|
/** Free an undo log segment, and remove the header from the history list.
|
2017-01-04 18:16:37 +02:00
|
|
|
@param[in,out] rseg rollback segment
|
MDEV-12289 Keep 128 persistent rollback segments for compatibility and performance
InnoDB divides the allocation of undo logs into rollback segments.
The DB_ROLL_PTR system column of clustered indexes can address up to
128 rollback segments (TRX_SYS_N_RSEGS). Originally, InnoDB only
created one rollback segment. In MySQL 5.5 or in the InnoDB Plugin
for MySQL 5.1, all 128 rollback segments were created.
MySQL 5.7 hard-codes the rollback segment IDs 1..32 for temporary undo logs.
On upgrade, unless a slow shutdown (innodb_fast_shutdown=0)
was performed on the old server instance, these rollback segments
could be in use by transactions that are in XA PREPARE state or
transactions that were left behind by a server kill followed by a
normal shutdown immediately after restart.
Persistent tables cannot refer to temporary undo logs or vice versa.
Therefore, we should keep two distinct sets of rollback segments:
one for persistent tables and another for temporary tables. In this way,
all 128 rollback segments will be available for both types of tables,
which could improve performance. Also, MariaDB 10.2 will remain more
compatible than MySQL 5.7 with data files from earlier versions of
MySQL or MariaDB.
trx_sys_t::temp_rsegs[TRX_SYS_N_RSEGS]: A new array of temporary
rollback segments. The trx_sys_t::rseg_array[TRX_SYS_N_RSEGS] will
be solely for persistent undo logs.
srv_tmp_undo_logs. Remove. Use the constant TRX_SYS_N_RSEGS.
srv_available_undo_logs: Change the type to ulong.
trx_rseg_get_on_id(): Remove. Instead, let the callers refer to
trx_sys directly.
trx_rseg_create(), trx_sysf_rseg_find_free(): Remove unneeded parameters.
These functions only deal with persistent undo logs.
trx_temp_rseg_create(): New function, to create all temporary rollback
segments at server startup.
trx_rseg_t::is_persistent(): Determine if the rollback segment is for
persistent tables.
trx_sys_is_noredo_rseg_slot(): Remove. The callers must know based on
context (such as table handle) whether the DB_ROLL_PTR is referring to
a persistent undo log.
trx_sys_create_rsegs(): Remove all parameters, which were always passed
as global variables. Instead, modify the global variables directly.
enum trx_rseg_type_t: Remove.
trx_t::get_temp_rseg(): A method to ensure that a temporary
rollback segment has been assigned for the transaction.
trx_t::assign_temp_rseg(): Replaces trx_assign_rseg().
trx_purge_free_segment(), trx_purge_truncate_rseg_history():
Remove the redundant variable noredo=false.
Temporary undo logs are discarded immediately at transaction commit
or rollback, not lazily by purge.
trx_purge_mark_undo_for_truncate(): Remove references to the
temporary rollback segments.
trx_purge_mark_undo_for_truncate(): Remove a check for temporary
rollback segments. Only the dedicated persistent undo log tablespaces
can be truncated.
trx_undo_get_undo_rec_low(), trx_undo_get_undo_rec(): Add the
parameter is_temp.
trx_rseg_mem_restore(): Split from trx_rseg_mem_create().
Initialize the undo log and the rollback segment from the file
data structures.
trx_sysf_get_n_rseg_slots(): Renamed from
trx_sysf_used_slots_for_redo_rseg(). Count the persistent
rollback segment headers that have been initialized.
trx_sys_close(): Also free trx_sys->temp_rsegs[].
get_next_redo_rseg(): Merged to trx_assign_rseg_low().
trx_assign_rseg_low(): Remove the parameters and access the
global variables directly. Revert to simple round-robin, now that
the whole trx_sys->rseg_array[] is for persistent undo log again.
get_next_noredo_rseg(): Moved to trx_t::assign_temp_rseg().
srv_undo_tablespaces_init(): Remove some parameters and use the
global variables directly. Clarify some error messages.
Adjust the test innodb.log_file. Apparently, before these changes,
InnoDB somehow ignored missing dedicated undo tablespace files that
are pointed by the TRX_SYS header page, possibly losing part of
essential transaction system state.
2017-03-30 13:11:34 +03:00
|
|
|
@param[in] hdr_addr file address of log_hdr */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
void
|
MDEV-12289 Keep 128 persistent rollback segments for compatibility and performance
InnoDB divides the allocation of undo logs into rollback segments.
The DB_ROLL_PTR system column of clustered indexes can address up to
128 rollback segments (TRX_SYS_N_RSEGS). Originally, InnoDB only
created one rollback segment. In MySQL 5.5 or in the InnoDB Plugin
for MySQL 5.1, all 128 rollback segments were created.
MySQL 5.7 hard-codes the rollback segment IDs 1..32 for temporary undo logs.
On upgrade, unless a slow shutdown (innodb_fast_shutdown=0)
was performed on the old server instance, these rollback segments
could be in use by transactions that are in XA PREPARE state or
transactions that were left behind by a server kill followed by a
normal shutdown immediately after restart.
Persistent tables cannot refer to temporary undo logs or vice versa.
Therefore, we should keep two distinct sets of rollback segments:
one for persistent tables and another for temporary tables. In this way,
all 128 rollback segments will be available for both types of tables,
which could improve performance. Also, MariaDB 10.2 will remain more
compatible than MySQL 5.7 with data files from earlier versions of
MySQL or MariaDB.
trx_sys_t::temp_rsegs[TRX_SYS_N_RSEGS]: A new array of temporary
rollback segments. The trx_sys_t::rseg_array[TRX_SYS_N_RSEGS] will
be solely for persistent undo logs.
srv_tmp_undo_logs. Remove. Use the constant TRX_SYS_N_RSEGS.
srv_available_undo_logs: Change the type to ulong.
trx_rseg_get_on_id(): Remove. Instead, let the callers refer to
trx_sys directly.
trx_rseg_create(), trx_sysf_rseg_find_free(): Remove unneeded parameters.
These functions only deal with persistent undo logs.
trx_temp_rseg_create(): New function, to create all temporary rollback
segments at server startup.
trx_rseg_t::is_persistent(): Determine if the rollback segment is for
persistent tables.
trx_sys_is_noredo_rseg_slot(): Remove. The callers must know based on
context (such as table handle) whether the DB_ROLL_PTR is referring to
a persistent undo log.
trx_sys_create_rsegs(): Remove all parameters, which were always passed
as global variables. Instead, modify the global variables directly.
enum trx_rseg_type_t: Remove.
trx_t::get_temp_rseg(): A method to ensure that a temporary
rollback segment has been assigned for the transaction.
trx_t::assign_temp_rseg(): Replaces trx_assign_rseg().
trx_purge_free_segment(), trx_purge_truncate_rseg_history():
Remove the redundant variable noredo=false.
Temporary undo logs are discarded immediately at transaction commit
or rollback, not lazily by purge.
trx_purge_mark_undo_for_truncate(): Remove references to the
temporary rollback segments.
trx_purge_mark_undo_for_truncate(): Remove a check for temporary
rollback segments. Only the dedicated persistent undo log tablespaces
can be truncated.
trx_undo_get_undo_rec_low(), trx_undo_get_undo_rec(): Add the
parameter is_temp.
trx_rseg_mem_restore(): Split from trx_rseg_mem_create().
Initialize the undo log and the rollback segment from the file
data structures.
trx_sysf_get_n_rseg_slots(): Renamed from
trx_sysf_used_slots_for_redo_rseg(). Count the persistent
rollback segment headers that have been initialized.
trx_sys_close(): Also free trx_sys->temp_rsegs[].
get_next_redo_rseg(): Merged to trx_assign_rseg_low().
trx_assign_rseg_low(): Remove the parameters and access the
global variables directly. Revert to simple round-robin, now that
the whole trx_sys->rseg_array[] is for persistent undo log again.
get_next_noredo_rseg(): Moved to trx_t::assign_temp_rseg().
srv_undo_tablespaces_init(): Remove some parameters and use the
global variables directly. Clarify some error messages.
Adjust the test innodb.log_file. Apparently, before these changes,
InnoDB somehow ignored missing dedicated undo tablespace files that
are pointed by the TRX_SYS header page, possibly losing part of
essential transaction system state.
2017-03-30 13:11:34 +03:00
|
|
|
trx_purge_free_segment(trx_rseg_t* rseg, fil_addr_t hdr_addr)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
mtr_t mtr;
|
|
|
|
trx_rsegf_t* rseg_hdr;
|
2017-08-15 17:18:55 +03:00
|
|
|
page_t* undo_page;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-08-15 17:18:55 +03:00
|
|
|
mtr.start();
|
|
|
|
mutex_enter(&rseg->mutex);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-08-15 17:18:55 +03:00
|
|
|
rseg_hdr = trx_rsegf_get(rseg->space, rseg->page_no, &mtr);
|
|
|
|
undo_page = trx_undo_page_get(
|
2018-03-26 17:23:47 +03:00
|
|
|
page_id_t(rseg->space->id, hdr_addr.page), &mtr);
|
2017-08-15 17:18:55 +03:00
|
|
|
|
|
|
|
/* Mark the last undo log totally purged, so that if the
|
|
|
|
system crashes, the tail of the undo log will not get accessed
|
|
|
|
again. The list of pages in the undo log tail gets
|
|
|
|
inconsistent during the freeing of the segment, and therefore
|
|
|
|
purge should not try to access them again. */
|
|
|
|
mlog_write_ulint(undo_page + hdr_addr.boffset + TRX_UNDO_NEEDS_PURGE,
|
|
|
|
0, MLOG_2BYTES, &mtr);
|
|
|
|
|
|
|
|
while (!fseg_free_step_not_header(
|
|
|
|
TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER
|
|
|
|
+ undo_page, false, &mtr)) {
|
|
|
|
mutex_exit(&rseg->mutex);
|
|
|
|
|
|
|
|
mtr.commit();
|
|
|
|
mtr.start();
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mutex_enter(&rseg->mutex);
|
|
|
|
|
2017-03-09 22:06:22 +02:00
|
|
|
rseg_hdr = trx_rsegf_get(rseg->space, rseg->page_no, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
undo_page = trx_undo_page_get(
|
2018-03-26 17:23:47 +03:00
|
|
|
page_id_t(rseg->space->id, hdr_addr.page), &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The page list may now be inconsistent, but the length field
|
|
|
|
stored in the list base node tells us how big it was before we
|
|
|
|
started the freeing. */
|
|
|
|
|
2017-08-15 17:18:55 +03:00
|
|
|
const ulint seg_size = flst_get_len(
|
|
|
|
TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST + undo_page);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* We may free the undo log segment header page; it must be freed
|
|
|
|
within the same mtr as the undo log header is removed from the
|
|
|
|
history list: otherwise, in case of a database crash, the segment
|
|
|
|
could become inaccessible garbage in the file space. */
|
|
|
|
|
2017-08-15 17:18:55 +03:00
|
|
|
trx_purge_remove_log_hdr(rseg_hdr, undo_page + hdr_addr.boffset, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
do {
|
|
|
|
|
|
|
|
/* Here we assume that a file segment with just the header
|
|
|
|
page can be freed in a few steps, so that the buffer pool
|
|
|
|
is not flooded with bufferfixed pages: see the note in
|
|
|
|
fsp0fsp.cc. */
|
|
|
|
|
2017-08-15 17:18:55 +03:00
|
|
|
} while (!fseg_free_step(TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER
|
|
|
|
+ undo_page, false, &mtr));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-08-15 17:18:55 +03:00
|
|
|
const ulint hist_size = mach_read_from_4(rseg_hdr
|
|
|
|
+ TRX_RSEG_HISTORY_SIZE);
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(hist_size >= seg_size);
|
|
|
|
|
|
|
|
mlog_write_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
|
|
|
|
hist_size - seg_size, MLOG_4BYTES, &mtr);
|
|
|
|
|
|
|
|
ut_ad(rseg->curr_size >= seg_size);
|
|
|
|
|
|
|
|
rseg->curr_size -= seg_size;
|
|
|
|
|
|
|
|
mutex_exit(&(rseg->mutex));
|
|
|
|
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
}
|
|
|
|
|
MDEV-12289 Keep 128 persistent rollback segments for compatibility and performance
InnoDB divides the allocation of undo logs into rollback segments.
The DB_ROLL_PTR system column of clustered indexes can address up to
128 rollback segments (TRX_SYS_N_RSEGS). Originally, InnoDB only
created one rollback segment. In MySQL 5.5 or in the InnoDB Plugin
for MySQL 5.1, all 128 rollback segments were created.
MySQL 5.7 hard-codes the rollback segment IDs 1..32 for temporary undo logs.
On upgrade, unless a slow shutdown (innodb_fast_shutdown=0)
was performed on the old server instance, these rollback segments
could be in use by transactions that are in XA PREPARE state or
transactions that were left behind by a server kill followed by a
normal shutdown immediately after restart.
Persistent tables cannot refer to temporary undo logs or vice versa.
Therefore, we should keep two distinct sets of rollback segments:
one for persistent tables and another for temporary tables. In this way,
all 128 rollback segments will be available for both types of tables,
which could improve performance. Also, MariaDB 10.2 will remain more
compatible than MySQL 5.7 with data files from earlier versions of
MySQL or MariaDB.
trx_sys_t::temp_rsegs[TRX_SYS_N_RSEGS]: A new array of temporary
rollback segments. The trx_sys_t::rseg_array[TRX_SYS_N_RSEGS] will
be solely for persistent undo logs.
srv_tmp_undo_logs. Remove. Use the constant TRX_SYS_N_RSEGS.
srv_available_undo_logs: Change the type to ulong.
trx_rseg_get_on_id(): Remove. Instead, let the callers refer to
trx_sys directly.
trx_rseg_create(), trx_sysf_rseg_find_free(): Remove unneeded parameters.
These functions only deal with persistent undo logs.
trx_temp_rseg_create(): New function, to create all temporary rollback
segments at server startup.
trx_rseg_t::is_persistent(): Determine if the rollback segment is for
persistent tables.
trx_sys_is_noredo_rseg_slot(): Remove. The callers must know based on
context (such as table handle) whether the DB_ROLL_PTR is referring to
a persistent undo log.
trx_sys_create_rsegs(): Remove all parameters, which were always passed
as global variables. Instead, modify the global variables directly.
enum trx_rseg_type_t: Remove.
trx_t::get_temp_rseg(): A method to ensure that a temporary
rollback segment has been assigned for the transaction.
trx_t::assign_temp_rseg(): Replaces trx_assign_rseg().
trx_purge_free_segment(), trx_purge_truncate_rseg_history():
Remove the redundant variable noredo=false.
Temporary undo logs are discarded immediately at transaction commit
or rollback, not lazily by purge.
trx_purge_mark_undo_for_truncate(): Remove references to the
temporary rollback segments.
trx_purge_mark_undo_for_truncate(): Remove a check for temporary
rollback segments. Only the dedicated persistent undo log tablespaces
can be truncated.
trx_undo_get_undo_rec_low(), trx_undo_get_undo_rec(): Add the
parameter is_temp.
trx_rseg_mem_restore(): Split from trx_rseg_mem_create().
Initialize the undo log and the rollback segment from the file
data structures.
trx_sysf_get_n_rseg_slots(): Renamed from
trx_sysf_used_slots_for_redo_rseg(). Count the persistent
rollback segment headers that have been initialized.
trx_sys_close(): Also free trx_sys->temp_rsegs[].
get_next_redo_rseg(): Merged to trx_assign_rseg_low().
trx_assign_rseg_low(): Remove the parameters and access the
global variables directly. Revert to simple round-robin, now that
the whole trx_sys->rseg_array[] is for persistent undo log again.
get_next_noredo_rseg(): Moved to trx_t::assign_temp_rseg().
srv_undo_tablespaces_init(): Remove some parameters and use the
global variables directly. Clarify some error messages.
Adjust the test innodb.log_file. Apparently, before these changes,
InnoDB somehow ignored missing dedicated undo tablespace files that
are pointed by the TRX_SYS header page, possibly losing part of
essential transaction system state.
2017-03-30 13:11:34 +03:00
|
|
|
/** Remove unnecessary history data from a rollback segment.
|
|
|
|
@param[in,out] rseg rollback segment
|
2018-02-21 12:54:33 +02:00
|
|
|
@param[in] limit truncate anything before this */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
void
|
2018-02-21 12:54:33 +02:00
|
|
|
trx_purge_truncate_rseg_history(
|
|
|
|
trx_rseg_t& rseg,
|
|
|
|
const purge_sys_t::iterator& limit)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
fil_addr_t hdr_addr;
|
|
|
|
fil_addr_t prev_hdr_addr;
|
|
|
|
trx_rsegf_t* rseg_hdr;
|
|
|
|
page_t* undo_page;
|
|
|
|
trx_ulogf_t* log_hdr;
|
|
|
|
trx_usegf_t* seg_hdr;
|
|
|
|
mtr_t mtr;
|
|
|
|
trx_id_t undo_trx_no;
|
|
|
|
|
2018-02-21 12:54:33 +02:00
|
|
|
mtr.start();
|
|
|
|
ut_ad(rseg.is_persistent());
|
|
|
|
mutex_enter(&rseg.mutex);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-21 12:54:33 +02:00
|
|
|
rseg_hdr = trx_rsegf_get(rseg.space, rseg.page_no, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
hdr_addr = trx_purge_get_log_from_hist(
|
|
|
|
flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
|
|
|
|
loop:
|
|
|
|
if (hdr_addr.page == FIL_NULL) {
|
2018-02-21 12:54:33 +02:00
|
|
|
func_exit:
|
|
|
|
mutex_exit(&rseg.mutex);
|
|
|
|
mtr.commit();
|
2014-02-26 19:11:54 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-26 17:23:47 +03:00
|
|
|
undo_page = trx_undo_page_get(page_id_t(rseg.space->id, hdr_addr.page),
|
2017-03-09 22:06:22 +02:00
|
|
|
&mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
log_hdr = undo_page + hdr_addr.boffset;
|
|
|
|
|
|
|
|
undo_trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
|
|
|
|
|
2018-02-21 19:02:32 +02:00
|
|
|
if (undo_trx_no >= limit.trx_no()) {
|
|
|
|
if (undo_trx_no == limit.trx_no()) {
|
2014-02-26 19:11:54 +01:00
|
|
|
trx_undo_truncate_start(
|
2018-02-21 12:54:33 +02:00
|
|
|
&rseg, hdr_addr.page,
|
|
|
|
hdr_addr.boffset, limit.undo_no);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2018-02-21 12:54:33 +02:00
|
|
|
goto func_exit;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
prev_hdr_addr = trx_purge_get_log_from_hist(
|
|
|
|
flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
|
|
|
|
|
|
|
|
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
|
|
|
|
|
|
|
|
if ((mach_read_from_2(seg_hdr + TRX_UNDO_STATE) == TRX_UNDO_TO_PURGE)
|
|
|
|
&& (mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG) == 0)) {
|
|
|
|
|
|
|
|
/* We can free the whole log segment */
|
|
|
|
|
2018-02-21 12:54:33 +02:00
|
|
|
mutex_exit(&rseg.mutex);
|
|
|
|
mtr.commit();
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-01-04 18:16:37 +02:00
|
|
|
/* calls the trx_purge_remove_log_hdr()
|
|
|
|
inside trx_purge_free_segment(). */
|
2018-02-21 12:54:33 +02:00
|
|
|
trx_purge_free_segment(&rseg, hdr_addr);
|
2014-02-26 19:11:54 +01:00
|
|
|
} else {
|
2017-01-04 18:16:37 +02:00
|
|
|
/* Remove the log hdr from the rseg history. */
|
|
|
|
trx_purge_remove_log_hdr(rseg_hdr, log_hdr, &mtr);
|
|
|
|
|
2018-02-21 12:54:33 +02:00
|
|
|
mutex_exit(&rseg.mutex);
|
|
|
|
mtr.commit();
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2018-02-21 12:54:33 +02:00
|
|
|
mtr.start();
|
|
|
|
mutex_enter(&rseg.mutex);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-21 12:54:33 +02:00
|
|
|
rseg_hdr = trx_rsegf_get(rseg.space, rseg.page_no, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
hdr_addr = prev_hdr_addr;
|
|
|
|
|
|
|
|
goto loop;
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/** UNDO log truncate logger. Needed to track state of truncate during crash.
|
|
|
|
An auxiliary redo log file undo_<space_id>_trunc.log will created while the
|
|
|
|
truncate of the UNDO is in progress. This file is required during recovery
|
|
|
|
to complete the truncate. */
|
|
|
|
|
|
|
|
namespace undo {
|
|
|
|
|
|
|
|
/** Populate log file name based on space_id
|
|
|
|
@param[in] space_id id of the undo tablespace.
|
|
|
|
@return DB_SUCCESS or error code */
|
|
|
|
dberr_t populate_log_file_name(
|
|
|
|
ulint space_id,
|
|
|
|
char*& log_file_name)
|
|
|
|
{
|
|
|
|
ulint log_file_name_sz =
|
|
|
|
strlen(srv_log_group_home_dir) + 22 + 1 /* NUL */
|
|
|
|
+ strlen(undo::s_log_prefix)
|
|
|
|
+ strlen(undo::s_log_ext);
|
|
|
|
|
|
|
|
log_file_name = new (std::nothrow) char[log_file_name_sz];
|
|
|
|
if (log_file_name == 0) {
|
|
|
|
return(DB_OUT_OF_MEMORY);
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(log_file_name, 0, log_file_name_sz);
|
|
|
|
|
|
|
|
strcpy(log_file_name, srv_log_group_home_dir);
|
|
|
|
ulint log_file_name_len = strlen(log_file_name);
|
|
|
|
|
|
|
|
if (log_file_name[log_file_name_len - 1]
|
|
|
|
!= OS_PATH_SEPARATOR) {
|
|
|
|
|
|
|
|
log_file_name[log_file_name_len]
|
|
|
|
= OS_PATH_SEPARATOR;
|
|
|
|
log_file_name_len = strlen(log_file_name);
|
|
|
|
}
|
|
|
|
|
2017-11-11 23:07:24 +02:00
|
|
|
snprintf(log_file_name + log_file_name_len,
|
|
|
|
log_file_name_sz - log_file_name_len,
|
|
|
|
"%s%lu_%s", undo::s_log_prefix,
|
|
|
|
(ulong) space_id, s_log_ext);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
return(DB_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Create the truncate log file.
|
|
|
|
@param[in] space_id id of the undo tablespace to truncate.
|
|
|
|
@return DB_SUCCESS or error code. */
|
|
|
|
dberr_t init(ulint space_id)
|
|
|
|
{
|
|
|
|
dberr_t err;
|
|
|
|
char* log_file_name;
|
|
|
|
|
|
|
|
/* Step-1: Create the log file name using the pre-decided
|
|
|
|
prefix/suffix and table id of undo tablepsace to truncate. */
|
|
|
|
err = populate_log_file_name(space_id, log_file_name);
|
|
|
|
if (err != DB_SUCCESS) {
|
|
|
|
return(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Step-2: Create the log file, open it and write 0 to
|
|
|
|
indicate init phase. */
|
|
|
|
bool ret;
|
|
|
|
os_file_t handle = os_file_create(
|
|
|
|
innodb_log_file_key, log_file_name, OS_FILE_CREATE,
|
|
|
|
OS_FILE_NORMAL, OS_LOG_FILE, srv_read_only_mode, &ret);
|
|
|
|
if (!ret) {
|
|
|
|
delete[] log_file_name;
|
|
|
|
return(DB_IO_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
ulint sz = UNIV_PAGE_SIZE;
|
|
|
|
void* buf = ut_zalloc_nokey(sz + UNIV_PAGE_SIZE);
|
|
|
|
if (buf == NULL) {
|
|
|
|
os_file_close(handle);
|
|
|
|
delete[] log_file_name;
|
|
|
|
return(DB_OUT_OF_MEMORY);
|
|
|
|
}
|
|
|
|
|
|
|
|
byte* log_buf = static_cast<byte*>(
|
|
|
|
ut_align(buf, UNIV_PAGE_SIZE));
|
|
|
|
|
|
|
|
IORequest request(IORequest::WRITE);
|
|
|
|
|
|
|
|
err = os_file_write(
|
|
|
|
request, log_file_name, handle, log_buf, 0, sz);
|
|
|
|
|
|
|
|
os_file_flush(handle);
|
|
|
|
os_file_close(handle);
|
|
|
|
|
|
|
|
ut_free(buf);
|
|
|
|
delete[] log_file_name;
|
|
|
|
|
|
|
|
return(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Mark completion of undo truncate action by writing magic number to
|
|
|
|
the log file and then removing it from the disk.
|
|
|
|
If we are going to remove it from disk then why write magic number ?
|
|
|
|
This is to safeguard from unlink (file-system) anomalies that will keep
|
|
|
|
the link to the file even after unlink action is successfull and
|
|
|
|
ref-count = 0.
|
|
|
|
@param[in] space_id id of the undo tablespace to truncate.*/
|
|
|
|
void done(
|
|
|
|
ulint space_id)
|
|
|
|
{
|
|
|
|
dberr_t err;
|
|
|
|
char* log_file_name;
|
|
|
|
|
|
|
|
/* Step-1: Create the log file name using the pre-decided
|
|
|
|
prefix/suffix and table id of undo tablepsace to truncate. */
|
|
|
|
err = populate_log_file_name(space_id, log_file_name);
|
|
|
|
if (err != DB_SUCCESS) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Step-2: Open log file and write magic number to
|
|
|
|
indicate done phase. */
|
|
|
|
bool ret;
|
|
|
|
os_file_t handle =
|
|
|
|
os_file_create_simple_no_error_handling(
|
|
|
|
innodb_log_file_key, log_file_name,
|
|
|
|
OS_FILE_OPEN, OS_FILE_READ_WRITE,
|
|
|
|
srv_read_only_mode, &ret);
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
os_file_delete(innodb_log_file_key, log_file_name);
|
|
|
|
delete[] log_file_name;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ulint sz = UNIV_PAGE_SIZE;
|
|
|
|
void* buf = ut_zalloc_nokey(sz + UNIV_PAGE_SIZE);
|
|
|
|
if (buf == NULL) {
|
|
|
|
os_file_close(handle);
|
|
|
|
os_file_delete(innodb_log_file_key, log_file_name);
|
|
|
|
delete[] log_file_name;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte* log_buf = static_cast<byte*>(
|
|
|
|
ut_align(buf, UNIV_PAGE_SIZE));
|
|
|
|
|
|
|
|
mach_write_to_4(log_buf, undo::s_magic);
|
|
|
|
|
|
|
|
IORequest request(IORequest::WRITE);
|
|
|
|
|
|
|
|
err = os_file_write(
|
|
|
|
request, log_file_name, handle, log_buf, 0, sz);
|
|
|
|
|
|
|
|
ut_ad(err == DB_SUCCESS);
|
|
|
|
|
|
|
|
os_file_flush(handle);
|
|
|
|
os_file_close(handle);
|
|
|
|
|
|
|
|
ut_free(buf);
|
|
|
|
os_file_delete(innodb_log_file_key, log_file_name);
|
|
|
|
delete[] log_file_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Check if TRUNCATE_DDL_LOG file exist.
|
|
|
|
@param[in] space_id id of the undo tablespace.
|
|
|
|
@return true if exist else false. */
|
|
|
|
bool is_log_present(
|
|
|
|
ulint space_id)
|
|
|
|
{
|
|
|
|
dberr_t err;
|
|
|
|
char* log_file_name;
|
|
|
|
|
|
|
|
/* Step-1: Populate log file name. */
|
|
|
|
err = populate_log_file_name(space_id, log_file_name);
|
|
|
|
if (err != DB_SUCCESS) {
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Step-2: Check for existence of the file. */
|
|
|
|
bool exist;
|
|
|
|
os_file_type_t type;
|
|
|
|
os_file_status(log_file_name, &exist, &type);
|
|
|
|
|
|
|
|
/* Step-3: If file exists, check it for presence of magic
|
|
|
|
number. If found, then delete the file and report file
|
|
|
|
doesn't exist as presence of magic number suggest that
|
|
|
|
truncate action was complete. */
|
|
|
|
|
|
|
|
if (exist) {
|
|
|
|
bool ret;
|
|
|
|
os_file_t handle =
|
|
|
|
os_file_create_simple_no_error_handling(
|
|
|
|
innodb_log_file_key, log_file_name,
|
|
|
|
OS_FILE_OPEN, OS_FILE_READ_WRITE,
|
|
|
|
srv_read_only_mode, &ret);
|
|
|
|
if (!ret) {
|
|
|
|
os_file_delete(innodb_log_file_key,
|
|
|
|
log_file_name);
|
|
|
|
delete[] log_file_name;
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
ulint sz = UNIV_PAGE_SIZE;
|
|
|
|
void* buf = ut_zalloc_nokey(sz + UNIV_PAGE_SIZE);
|
|
|
|
if (buf == NULL) {
|
|
|
|
os_file_close(handle);
|
|
|
|
os_file_delete(innodb_log_file_key,
|
|
|
|
log_file_name);
|
|
|
|
delete[] log_file_name;
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
byte* log_buf = static_cast<byte*>(
|
|
|
|
ut_align(buf, UNIV_PAGE_SIZE));
|
|
|
|
|
|
|
|
IORequest request(IORequest::READ);
|
|
|
|
|
|
|
|
dberr_t err;
|
|
|
|
|
|
|
|
err = os_file_read(request, handle, log_buf, 0, sz);
|
|
|
|
|
|
|
|
os_file_close(handle);
|
|
|
|
|
|
|
|
if (err != DB_SUCCESS) {
|
|
|
|
|
|
|
|
ib::info()
|
|
|
|
<< "Unable to read '"
|
|
|
|
<< log_file_name << "' : "
|
|
|
|
<< ut_strerr(err);
|
|
|
|
|
|
|
|
os_file_delete(
|
|
|
|
innodb_log_file_key, log_file_name);
|
|
|
|
|
|
|
|
ut_free(buf);
|
|
|
|
|
|
|
|
delete[] log_file_name;
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
ulint magic_no = mach_read_from_4(log_buf);
|
|
|
|
|
|
|
|
ut_free(buf);
|
|
|
|
|
|
|
|
if (magic_no == undo::s_magic) {
|
|
|
|
/* Found magic number. */
|
|
|
|
os_file_delete(innodb_log_file_key,
|
|
|
|
log_file_name);
|
|
|
|
delete[] log_file_name;
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete[] log_file_name;
|
|
|
|
|
|
|
|
return(exist);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Iterate over all the UNDO tablespaces and check if any of the UNDO
|
|
|
|
tablespace qualifies for TRUNCATE (size > threshold).
|
|
|
|
@param[in,out] undo_trunc undo truncate tracker */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
trx_purge_mark_undo_for_truncate(
|
|
|
|
undo::Truncate* undo_trunc)
|
|
|
|
{
|
|
|
|
/* Step-1: If UNDO Tablespace
|
|
|
|
- already marked for truncate (OR)
|
|
|
|
- truncate disabled
|
|
|
|
return immediately else search for qualifying tablespace. */
|
|
|
|
if (undo_trunc->is_marked() || !srv_undo_log_truncate) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Step-2: Validation/Qualification checks
|
|
|
|
a. At-least 2 UNDO tablespaces so even if one UNDO tablespace
|
|
|
|
is being truncated server can continue to operate.
|
MDEV-12289 Keep 128 persistent rollback segments for compatibility and performance
InnoDB divides the allocation of undo logs into rollback segments.
The DB_ROLL_PTR system column of clustered indexes can address up to
128 rollback segments (TRX_SYS_N_RSEGS). Originally, InnoDB only
created one rollback segment. In MySQL 5.5 or in the InnoDB Plugin
for MySQL 5.1, all 128 rollback segments were created.
MySQL 5.7 hard-codes the rollback segment IDs 1..32 for temporary undo logs.
On upgrade, unless a slow shutdown (innodb_fast_shutdown=0)
was performed on the old server instance, these rollback segments
could be in use by transactions that are in XA PREPARE state or
transactions that were left behind by a server kill followed by a
normal shutdown immediately after restart.
Persistent tables cannot refer to temporary undo logs or vice versa.
Therefore, we should keep two distinct sets of rollback segments:
one for persistent tables and another for temporary tables. In this way,
all 128 rollback segments will be available for both types of tables,
which could improve performance. Also, MariaDB 10.2 will remain more
compatible than MySQL 5.7 with data files from earlier versions of
MySQL or MariaDB.
trx_sys_t::temp_rsegs[TRX_SYS_N_RSEGS]: A new array of temporary
rollback segments. The trx_sys_t::rseg_array[TRX_SYS_N_RSEGS] will
be solely for persistent undo logs.
srv_tmp_undo_logs. Remove. Use the constant TRX_SYS_N_RSEGS.
srv_available_undo_logs: Change the type to ulong.
trx_rseg_get_on_id(): Remove. Instead, let the callers refer to
trx_sys directly.
trx_rseg_create(), trx_sysf_rseg_find_free(): Remove unneeded parameters.
These functions only deal with persistent undo logs.
trx_temp_rseg_create(): New function, to create all temporary rollback
segments at server startup.
trx_rseg_t::is_persistent(): Determine if the rollback segment is for
persistent tables.
trx_sys_is_noredo_rseg_slot(): Remove. The callers must know based on
context (such as table handle) whether the DB_ROLL_PTR is referring to
a persistent undo log.
trx_sys_create_rsegs(): Remove all parameters, which were always passed
as global variables. Instead, modify the global variables directly.
enum trx_rseg_type_t: Remove.
trx_t::get_temp_rseg(): A method to ensure that a temporary
rollback segment has been assigned for the transaction.
trx_t::assign_temp_rseg(): Replaces trx_assign_rseg().
trx_purge_free_segment(), trx_purge_truncate_rseg_history():
Remove the redundant variable noredo=false.
Temporary undo logs are discarded immediately at transaction commit
or rollback, not lazily by purge.
trx_purge_mark_undo_for_truncate(): Remove references to the
temporary rollback segments.
trx_purge_mark_undo_for_truncate(): Remove a check for temporary
rollback segments. Only the dedicated persistent undo log tablespaces
can be truncated.
trx_undo_get_undo_rec_low(), trx_undo_get_undo_rec(): Add the
parameter is_temp.
trx_rseg_mem_restore(): Split from trx_rseg_mem_create().
Initialize the undo log and the rollback segment from the file
data structures.
trx_sysf_get_n_rseg_slots(): Renamed from
trx_sysf_used_slots_for_redo_rseg(). Count the persistent
rollback segment headers that have been initialized.
trx_sys_close(): Also free trx_sys->temp_rsegs[].
get_next_redo_rseg(): Merged to trx_assign_rseg_low().
trx_assign_rseg_low(): Remove the parameters and access the
global variables directly. Revert to simple round-robin, now that
the whole trx_sys->rseg_array[] is for persistent undo log again.
get_next_noredo_rseg(): Moved to trx_t::assign_temp_rseg().
srv_undo_tablespaces_init(): Remove some parameters and use the
global variables directly. Clarify some error messages.
Adjust the test innodb.log_file. Apparently, before these changes,
InnoDB somehow ignored missing dedicated undo tablespace files that
are pointed by the TRX_SYS header page, possibly losing part of
essential transaction system state.
2017-03-30 13:11:34 +03:00
|
|
|
b. At-least 2 persistent UNDO logs (besides the default rseg-0)
|
2016-08-12 11:17:45 +03:00
|
|
|
b. At-least 1 UNDO tablespace size > threshold. */
|
MDEV-12289 Keep 128 persistent rollback segments for compatibility and performance
InnoDB divides the allocation of undo logs into rollback segments.
The DB_ROLL_PTR system column of clustered indexes can address up to
128 rollback segments (TRX_SYS_N_RSEGS). Originally, InnoDB only
created one rollback segment. In MySQL 5.5 or in the InnoDB Plugin
for MySQL 5.1, all 128 rollback segments were created.
MySQL 5.7 hard-codes the rollback segment IDs 1..32 for temporary undo logs.
On upgrade, unless a slow shutdown (innodb_fast_shutdown=0)
was performed on the old server instance, these rollback segments
could be in use by transactions that are in XA PREPARE state or
transactions that were left behind by a server kill followed by a
normal shutdown immediately after restart.
Persistent tables cannot refer to temporary undo logs or vice versa.
Therefore, we should keep two distinct sets of rollback segments:
one for persistent tables and another for temporary tables. In this way,
all 128 rollback segments will be available for both types of tables,
which could improve performance. Also, MariaDB 10.2 will remain more
compatible than MySQL 5.7 with data files from earlier versions of
MySQL or MariaDB.
trx_sys_t::temp_rsegs[TRX_SYS_N_RSEGS]: A new array of temporary
rollback segments. The trx_sys_t::rseg_array[TRX_SYS_N_RSEGS] will
be solely for persistent undo logs.
srv_tmp_undo_logs. Remove. Use the constant TRX_SYS_N_RSEGS.
srv_available_undo_logs: Change the type to ulong.
trx_rseg_get_on_id(): Remove. Instead, let the callers refer to
trx_sys directly.
trx_rseg_create(), trx_sysf_rseg_find_free(): Remove unneeded parameters.
These functions only deal with persistent undo logs.
trx_temp_rseg_create(): New function, to create all temporary rollback
segments at server startup.
trx_rseg_t::is_persistent(): Determine if the rollback segment is for
persistent tables.
trx_sys_is_noredo_rseg_slot(): Remove. The callers must know based on
context (such as table handle) whether the DB_ROLL_PTR is referring to
a persistent undo log.
trx_sys_create_rsegs(): Remove all parameters, which were always passed
as global variables. Instead, modify the global variables directly.
enum trx_rseg_type_t: Remove.
trx_t::get_temp_rseg(): A method to ensure that a temporary
rollback segment has been assigned for the transaction.
trx_t::assign_temp_rseg(): Replaces trx_assign_rseg().
trx_purge_free_segment(), trx_purge_truncate_rseg_history():
Remove the redundant variable noredo=false.
Temporary undo logs are discarded immediately at transaction commit
or rollback, not lazily by purge.
trx_purge_mark_undo_for_truncate(): Remove references to the
temporary rollback segments.
trx_purge_mark_undo_for_truncate(): Remove a check for temporary
rollback segments. Only the dedicated persistent undo log tablespaces
can be truncated.
trx_undo_get_undo_rec_low(), trx_undo_get_undo_rec(): Add the
parameter is_temp.
trx_rseg_mem_restore(): Split from trx_rseg_mem_create().
Initialize the undo log and the rollback segment from the file
data structures.
trx_sysf_get_n_rseg_slots(): Renamed from
trx_sysf_used_slots_for_redo_rseg(). Count the persistent
rollback segment headers that have been initialized.
trx_sys_close(): Also free trx_sys->temp_rsegs[].
get_next_redo_rseg(): Merged to trx_assign_rseg_low().
trx_assign_rseg_low(): Remove the parameters and access the
global variables directly. Revert to simple round-robin, now that
the whole trx_sys->rseg_array[] is for persistent undo log again.
get_next_noredo_rseg(): Moved to trx_t::assign_temp_rseg().
srv_undo_tablespaces_init(): Remove some parameters and use the
global variables directly. Clarify some error messages.
Adjust the test innodb.log_file. Apparently, before these changes,
InnoDB somehow ignored missing dedicated undo tablespace files that
are pointed by the TRX_SYS header page, possibly losing part of
essential transaction system state.
2017-03-30 13:11:34 +03:00
|
|
|
if (srv_undo_tablespaces_active < 2 || srv_undo_logs < 3) {
|
2016-08-12 11:17:45 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Avoid bias selection and so start the scan from immediate next
|
|
|
|
of last selected UNDO tablespace for truncate. */
|
|
|
|
ulint space_id = undo_trunc->get_scan_start();
|
|
|
|
|
|
|
|
for (ulint i = 1; i <= srv_undo_tablespaces_active; i++) {
|
|
|
|
|
|
|
|
if (fil_space_get_size(space_id)
|
|
|
|
> (srv_max_undo_log_size / srv_page_size)) {
|
|
|
|
/* Tablespace qualifies for truncate. */
|
|
|
|
undo_trunc->mark(space_id);
|
|
|
|
undo::Truncate::add_space_to_trunc_list(space_id);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
space_id = ((space_id + 1) % (srv_undo_tablespaces_active + 1));
|
|
|
|
if (space_id == 0) {
|
|
|
|
/* Note: UNDO tablespace ids starts from 1. */
|
|
|
|
++space_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Couldn't make any selection. */
|
|
|
|
if (!undo_trunc->is_marked()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-12 13:30:10 +02:00
|
|
|
DBUG_LOG("undo",
|
|
|
|
"marking for truncate UNDO tablespace "
|
|
|
|
<< undo_trunc->get_marked_space_id());
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
/* Step-3: Iterate over all the rsegs of selected UNDO tablespace
|
|
|
|
and mark them temporarily unavailable for allocation.*/
|
|
|
|
for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) {
|
2017-12-22 16:15:41 +02:00
|
|
|
if (trx_rseg_t* rseg = trx_sys.rseg_array[i]) {
|
MDEV-12289 Keep 128 persistent rollback segments for compatibility and performance
InnoDB divides the allocation of undo logs into rollback segments.
The DB_ROLL_PTR system column of clustered indexes can address up to
128 rollback segments (TRX_SYS_N_RSEGS). Originally, InnoDB only
created one rollback segment. In MySQL 5.5 or in the InnoDB Plugin
for MySQL 5.1, all 128 rollback segments were created.
MySQL 5.7 hard-codes the rollback segment IDs 1..32 for temporary undo logs.
On upgrade, unless a slow shutdown (innodb_fast_shutdown=0)
was performed on the old server instance, these rollback segments
could be in use by transactions that are in XA PREPARE state or
transactions that were left behind by a server kill followed by a
normal shutdown immediately after restart.
Persistent tables cannot refer to temporary undo logs or vice versa.
Therefore, we should keep two distinct sets of rollback segments:
one for persistent tables and another for temporary tables. In this way,
all 128 rollback segments will be available for both types of tables,
which could improve performance. Also, MariaDB 10.2 will remain more
compatible than MySQL 5.7 with data files from earlier versions of
MySQL or MariaDB.
trx_sys_t::temp_rsegs[TRX_SYS_N_RSEGS]: A new array of temporary
rollback segments. The trx_sys_t::rseg_array[TRX_SYS_N_RSEGS] will
be solely for persistent undo logs.
srv_tmp_undo_logs. Remove. Use the constant TRX_SYS_N_RSEGS.
srv_available_undo_logs: Change the type to ulong.
trx_rseg_get_on_id(): Remove. Instead, let the callers refer to
trx_sys directly.
trx_rseg_create(), trx_sysf_rseg_find_free(): Remove unneeded parameters.
These functions only deal with persistent undo logs.
trx_temp_rseg_create(): New function, to create all temporary rollback
segments at server startup.
trx_rseg_t::is_persistent(): Determine if the rollback segment is for
persistent tables.
trx_sys_is_noredo_rseg_slot(): Remove. The callers must know based on
context (such as table handle) whether the DB_ROLL_PTR is referring to
a persistent undo log.
trx_sys_create_rsegs(): Remove all parameters, which were always passed
as global variables. Instead, modify the global variables directly.
enum trx_rseg_type_t: Remove.
trx_t::get_temp_rseg(): A method to ensure that a temporary
rollback segment has been assigned for the transaction.
trx_t::assign_temp_rseg(): Replaces trx_assign_rseg().
trx_purge_free_segment(), trx_purge_truncate_rseg_history():
Remove the redundant variable noredo=false.
Temporary undo logs are discarded immediately at transaction commit
or rollback, not lazily by purge.
trx_purge_mark_undo_for_truncate(): Remove references to the
temporary rollback segments.
trx_purge_mark_undo_for_truncate(): Remove a check for temporary
rollback segments. Only the dedicated persistent undo log tablespaces
can be truncated.
trx_undo_get_undo_rec_low(), trx_undo_get_undo_rec(): Add the
parameter is_temp.
trx_rseg_mem_restore(): Split from trx_rseg_mem_create().
Initialize the undo log and the rollback segment from the file
data structures.
trx_sysf_get_n_rseg_slots(): Renamed from
trx_sysf_used_slots_for_redo_rseg(). Count the persistent
rollback segment headers that have been initialized.
trx_sys_close(): Also free trx_sys->temp_rsegs[].
get_next_redo_rseg(): Merged to trx_assign_rseg_low().
trx_assign_rseg_low(): Remove the parameters and access the
global variables directly. Revert to simple round-robin, now that
the whole trx_sys->rseg_array[] is for persistent undo log again.
get_next_noredo_rseg(): Moved to trx_t::assign_temp_rseg().
srv_undo_tablespaces_init(): Remove some parameters and use the
global variables directly. Clarify some error messages.
Adjust the test innodb.log_file. Apparently, before these changes,
InnoDB somehow ignored missing dedicated undo tablespace files that
are pointed by the TRX_SYS header page, possibly losing part of
essential transaction system state.
2017-03-30 13:11:34 +03:00
|
|
|
ut_ad(rseg->is_persistent());
|
2018-03-26 17:23:47 +03:00
|
|
|
if (rseg->space->id
|
|
|
|
== undo_trunc->get_marked_space_id()) {
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
/* Once set this rseg will not be allocated
|
|
|
|
to new booting transaction but we will wait
|
|
|
|
for existing active transaction to finish. */
|
|
|
|
rseg->skip_allocation = true;
|
|
|
|
undo_trunc->add_rseg_to_trunc(rseg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
undo::undo_spaces_t undo::Truncate::s_spaces_to_truncate;
|
|
|
|
|
|
|
|
/** Cleanse purge queue to remove the rseg that reside in undo-tablespace
|
|
|
|
marked for truncate.
|
|
|
|
@param[in,out] undo_trunc undo truncate tracker */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
trx_purge_cleanse_purge_queue(
|
|
|
|
undo::Truncate* undo_trunc)
|
|
|
|
{
|
2018-02-22 09:30:41 +02:00
|
|
|
mutex_enter(&purge_sys.pq_mutex);
|
2016-08-12 11:17:45 +03:00
|
|
|
typedef std::vector<TrxUndoRsegs> purge_elem_list_t;
|
|
|
|
purge_elem_list_t purge_elem_list;
|
|
|
|
|
|
|
|
/* Remove rseg instances that are in the purge queue before we start
|
|
|
|
truncate of corresponding UNDO truncate. */
|
2018-02-22 09:30:41 +02:00
|
|
|
while (!purge_sys.purge_queue.empty()) {
|
|
|
|
purge_elem_list.push_back(purge_sys.purge_queue.top());
|
|
|
|
purge_sys.purge_queue.pop();
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_ad(purge_sys.purge_queue.empty());
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
for (purge_elem_list_t::iterator it = purge_elem_list.begin();
|
|
|
|
it != purge_elem_list.end();
|
|
|
|
++it) {
|
|
|
|
|
|
|
|
for (TrxUndoRsegs::iterator it2 = it->begin();
|
|
|
|
it2 != it->end();
|
|
|
|
++it2) {
|
|
|
|
|
2018-03-26 17:23:47 +03:00
|
|
|
if ((*it2)->space->id
|
2016-08-12 11:17:45 +03:00
|
|
|
== undo_trunc->get_marked_space_id()) {
|
|
|
|
it->erase(it2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-21 16:15:20 +02:00
|
|
|
if (!it->empty()) {
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.purge_queue.push(*it);
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
|
|
|
}
|
2018-02-22 09:30:41 +02:00
|
|
|
mutex_exit(&purge_sys.pq_mutex);
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Iterate over selected UNDO tablespace and check if all the rsegs
|
|
|
|
that resides in the tablespace are free.
|
|
|
|
@param[in] limit truncate_limit
|
|
|
|
@param[in,out] undo_trunc undo truncate tracker */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
trx_purge_initiate_truncate(
|
2018-02-21 12:54:33 +02:00
|
|
|
const purge_sys_t::iterator& limit,
|
2016-08-12 11:17:45 +03:00
|
|
|
undo::Truncate* undo_trunc)
|
|
|
|
{
|
|
|
|
/* Step-1: Early check to findout if any of the the UNDO tablespace
|
|
|
|
is marked for truncate. */
|
|
|
|
if (!undo_trunc->is_marked()) {
|
|
|
|
/* No tablespace marked for truncate yet. */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Step-2: Scan over each rseg and ensure that it doesn't hold any
|
|
|
|
active undo records. */
|
|
|
|
bool all_free = true;
|
|
|
|
|
|
|
|
for (ulint i = 0; i < undo_trunc->rsegs_size() && all_free; ++i) {
|
|
|
|
|
|
|
|
trx_rseg_t* rseg = undo_trunc->get_ith_rseg(i);
|
|
|
|
|
|
|
|
mutex_enter(&rseg->mutex);
|
|
|
|
|
|
|
|
if (rseg->trx_ref_count > 0) {
|
|
|
|
/* This rseg is still being held by an active
|
|
|
|
transaction. */
|
|
|
|
all_free = false;
|
|
|
|
mutex_exit(&rseg->mutex);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_ad(rseg->trx_ref_count == 0);
|
|
|
|
ut_ad(rseg->skip_allocation);
|
|
|
|
|
|
|
|
ulint size_of_rsegs = rseg->curr_size;
|
|
|
|
|
|
|
|
if (size_of_rsegs == 1) {
|
|
|
|
mutex_exit(&rseg->mutex);
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* There could be cached undo segment. Check if records
|
|
|
|
in these segments can be purged. Normal purge history
|
|
|
|
will not touch these cached segment. */
|
|
|
|
ulint cached_undo_size = 0;
|
|
|
|
|
|
|
|
for (trx_undo_t* undo =
|
MDEV-12288 Reset DB_TRX_ID when the history is removed, to speed up MVCC
Let InnoDB purge reset DB_TRX_ID,DB_ROLL_PTR when the history is removed.
[TODO: It appears that the resetting is not taking place as often as
it could be. We should test that a simple INSERT should eventually
cause row_purge_reset_trx_id() to be invoked unless DROP TABLE is
invoked soon enough.]
The InnoDB clustered index record system columns DB_TRX_ID,DB_ROLL_PTR
are used by multi-versioning. After the history is no longer needed, these
columns can safely be reset to 0 and 1<<55 (to indicate a fresh insert).
When a reader sees 0 in the DB_TRX_ID column, it can instantly determine
that the record is present the read view. There is no need to acquire
the transaction system mutex to check if the transaction exists, because
writes can never be conducted by a transaction whose ID is 0.
The persistent InnoDB undo log used to be split into two parts:
insert_undo and update_undo. The insert_undo log was discarded at
transaction commit or rollback, and the update_undo log was processed
by the purge subsystem. As part of this change, we will only generate
a single undo log for new transactions, and the purge subsystem will
reset the DB_TRX_ID whenever a clustered index record is touched.
That is, all persistent undo log will be preserved at transaction commit
or rollback, to be removed by purge.
The InnoDB redo log format is changed in two ways:
We remove the redo log record type MLOG_UNDO_HDR_REUSE, and
we introduce the MLOG_ZIP_WRITE_TRX_ID record for updating the
DB_TRX_ID,DB_ROLL_PTR in a ROW_FORMAT=COMPRESSED table.
This is also changing the format of persistent InnoDB data files:
undo log and clustered index leaf page records. It will still be
possible via import and export to exchange data files with earlier
versions of MariaDB. The change to clustered index leaf page records
is simple: we allow DB_TRX_ID to be 0.
When it comes to the undo log, we must be able to upgrade from earlier
MariaDB versions after a clean shutdown (no redo log to apply).
While it would be nice to perform a slow shutdown (innodb_fast_shutdown=0)
before an upgrade, to empty the undo logs, we cannot assume that this
has been done. So, separate insert_undo log may exist for recovered
uncommitted transactions. These transactions may be automatically
rolled back, or they may be in XA PREPARE state, in which case InnoDB
will preserve the transaction until an explicit XA COMMIT or XA ROLLBACK.
Upgrade has been tested by starting up MariaDB 10.2 with
./mysql-test-run --manual-gdb innodb.read_only_recovery
and then starting up this patched server with
and without --innodb-read-only.
trx_undo_ptr_t::undo: Renamed from update_undo.
trx_undo_ptr_t::old_insert: Renamed from insert_undo.
trx_rseg_t::undo_list: Renamed from update_undo_list.
trx_rseg_t::undo_cached: Merged from update_undo_cached
and insert_undo_cached.
trx_rseg_t::old_insert_list: Renamed from insert_undo_list.
row_purge_reset_trx_id(): New function to reset the columns.
This will be called for all undo processing in purge
that does not remove the clustered index record.
trx_undo_update_rec_get_update(): Allow trx_id=0 when copying the
old DB_TRX_ID of the record to the undo log.
ReadView::changes_visible(): Allow id==0. (Return true for it.
This is what speeds up the MVCC.)
row_vers_impl_x_locked_low(), row_vers_build_for_semi_consistent_read():
Implement a fast path for DB_TRX_ID=0.
Always initialize the TRX_UNDO_PAGE_TYPE to 0. Remove undo->type.
MLOG_UNDO_HDR_REUSE: Remove. This changes the redo log format!
innobase_start_or_create_for_mysql(): Set srv_undo_sources before
starting any transactions.
The parsing of the MLOG_ZIP_WRITE_TRX_ID record was successfully
tested by running the following:
./mtr --parallel=auto --mysqld=--debug=d,ib_log innodb_zip.bug56680
grep MLOG_ZIP_WRITE_TRX_ID var/*/log/mysqld.1.err
2017-07-07 13:08:16 +03:00
|
|
|
UT_LIST_GET_FIRST(rseg->undo_cached);
|
2016-08-12 11:17:45 +03:00
|
|
|
undo != NULL && all_free;
|
|
|
|
undo = UT_LIST_GET_NEXT(undo_list, undo)) {
|
|
|
|
|
2018-02-21 19:02:32 +02:00
|
|
|
if (limit.trx_no() < undo->trx_id) {
|
2016-08-12 11:17:45 +03:00
|
|
|
all_free = false;
|
|
|
|
} else {
|
|
|
|
cached_undo_size += undo->size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_ad(size_of_rsegs >= (cached_undo_size + 1));
|
|
|
|
|
|
|
|
if (size_of_rsegs > (cached_undo_size + 1)) {
|
|
|
|
/* There are pages besides cached pages that
|
|
|
|
still hold active data. */
|
|
|
|
all_free = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_exit(&rseg->mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!all_free) {
|
|
|
|
/* rseg still holds active data.*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Step-3: Start the actual truncate.
|
|
|
|
a. log-checkpoint
|
|
|
|
b. Write the DDL log to protect truncate action from CRASH
|
|
|
|
c. Remove rseg instance if added to purge queue before we
|
|
|
|
initiate truncate.
|
|
|
|
d. Execute actual truncate
|
|
|
|
e. Remove the DDL log. */
|
|
|
|
|
|
|
|
/* After truncate if server crashes then redo logging done for this
|
|
|
|
undo tablespace might not stand valid as tablespace has been
|
|
|
|
truncated. */
|
|
|
|
log_make_checkpoint_at(LSN_MAX, TRUE);
|
|
|
|
|
2017-04-25 09:26:01 +03:00
|
|
|
const ulint space_id = undo_trunc->get_marked_space_id();
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2017-04-25 09:26:01 +03:00
|
|
|
ib::info() << "Truncating UNDO tablespace " << space_id;
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
dberr_t err =
|
|
|
|
#endif /* UNIV_DEBUG */
|
2017-04-25 09:26:01 +03:00
|
|
|
undo_trunc->start_logging(space_id);
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(err == DB_SUCCESS);
|
|
|
|
|
|
|
|
DBUG_EXECUTE_IF("ib_undo_trunc_before_truncate",
|
|
|
|
ib::info() << "ib_undo_trunc_before_truncate";
|
|
|
|
DBUG_SUICIDE(););
|
|
|
|
|
|
|
|
trx_purge_cleanse_purge_queue(undo_trunc);
|
|
|
|
|
2017-04-25 09:26:01 +03:00
|
|
|
if (!trx_undo_truncate_tablespace(undo_trunc)) {
|
2016-08-12 11:17:45 +03:00
|
|
|
/* Note: In case of error we don't enable the rsegs
|
|
|
|
and neither unmark the tablespace so the tablespace
|
|
|
|
continue to remain inactive. */
|
2017-04-25 09:26:01 +03:00
|
|
|
ib::error() << "Failed to truncate UNDO tablespace "
|
|
|
|
<< space_id;
|
2016-08-12 11:17:45 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
if (purge_sys.rseg != NULL
|
|
|
|
&& purge_sys.rseg->last_page_no == FIL_NULL) {
|
|
|
|
/* If purge_sys.rseg is pointing to rseg that was recently
|
2016-08-12 11:17:45 +03:00
|
|
|
truncated then move to next rseg element.
|
2018-02-22 09:30:41 +02:00
|
|
|
Note: Ideally purge_sys.rseg should be NULL because purge
|
2016-08-12 11:17:45 +03:00
|
|
|
should complete processing of all the records but there is
|
|
|
|
purge_batch_size that can force the purge loop to exit before
|
2018-02-22 09:30:41 +02:00
|
|
|
all the records are purged and in this case purge_sys.rseg
|
2016-08-12 11:17:45 +03:00
|
|
|
could point to a valid rseg waiting for next purge cycle. */
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.next_stored = false;
|
|
|
|
purge_sys.rseg = NULL;
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
DBUG_EXECUTE_IF("ib_undo_trunc_before_ddl_log_end",
|
|
|
|
ib::info() << "ib_undo_trunc_before_ddl_log_end";
|
|
|
|
DBUG_SUICIDE(););
|
|
|
|
|
|
|
|
log_make_checkpoint_at(LSN_MAX, TRUE);
|
|
|
|
|
2017-04-25 09:26:01 +03:00
|
|
|
undo_trunc->done_logging(space_id);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
/* Completed truncate. Now it is safe to re-use the tablespace. */
|
|
|
|
for (ulint i = 0; i < undo_trunc->rsegs_size(); ++i) {
|
|
|
|
trx_rseg_t* rseg = undo_trunc->get_ith_rseg(i);
|
|
|
|
rseg->skip_allocation = false;
|
|
|
|
}
|
|
|
|
|
2017-04-25 09:26:01 +03:00
|
|
|
ib::info() << "Truncated UNDO tablespace " << space_id;
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
undo_trunc->reset();
|
|
|
|
undo::Truncate::clear_trunc_list();
|
|
|
|
|
|
|
|
DBUG_EXECUTE_IF("ib_undo_trunc_trunc_done",
|
|
|
|
ib::info() << "ib_undo_trunc_trunc_done";
|
|
|
|
DBUG_SUICIDE(););
|
|
|
|
}
|
|
|
|
|
2018-01-24 22:10:16 +04:00
|
|
|
/**
|
2014-02-26 19:11:54 +01:00
|
|
|
Removes unnecessary history data from rollback segments. NOTE that when this
|
2018-01-24 22:10:16 +04:00
|
|
|
function is called, the caller must not have any latches on undo log pages!
|
|
|
|
*/
|
2018-02-21 12:54:33 +02:00
|
|
|
static void trx_purge_truncate_history()
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_ad(purge_sys.head <= purge_sys.tail);
|
|
|
|
purge_sys_t::iterator& head = purge_sys.head.commit
|
|
|
|
? purge_sys.head : purge_sys.tail;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
if (head.trx_no() >= purge_sys.view.low_limit_no()) {
|
2018-02-21 12:54:33 +02:00
|
|
|
/* This is sometimes necessary. TODO: find out why. */
|
2018-02-22 09:30:41 +02:00
|
|
|
head.reset_trx_no(purge_sys.view.low_limit_no());
|
2018-02-21 12:54:33 +02:00
|
|
|
head.undo_no = 0;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2017-04-25 09:26:01 +03:00
|
|
|
for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) {
|
2018-02-21 12:54:33 +02:00
|
|
|
if (trx_rseg_t* rseg = trx_sys.rseg_array[i]) {
|
|
|
|
ut_ad(rseg->id == i);
|
|
|
|
trx_purge_truncate_rseg_history(*rseg, head);
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* UNDO tablespace truncate. We will try to truncate as much as we
|
|
|
|
can (greedy approach). This will ensure when the server is idle we
|
|
|
|
try and truncate all the UNDO tablespaces. */
|
2017-04-25 09:26:01 +03:00
|
|
|
for (ulint i = srv_undo_tablespaces_active; i--; ) {
|
2018-02-22 09:30:41 +02:00
|
|
|
trx_purge_mark_undo_for_truncate(&purge_sys.undo_trunc);
|
|
|
|
trx_purge_initiate_truncate(head, &purge_sys.undo_trunc);
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************//**
|
|
|
|
Updates the last not yet purged history log info in rseg when we have purged
|
2018-02-22 09:30:41 +02:00
|
|
|
a whole undo log. Advances also purge_sys.purge_trx_no past the purged log. */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
void
|
|
|
|
trx_purge_rseg_get_next_history_log(
|
|
|
|
/*================================*/
|
|
|
|
trx_rseg_t* rseg, /*!< in: rollback segment */
|
|
|
|
ulint* n_pages_handled)/*!< in/out: number of UNDO pages
|
|
|
|
handled */
|
|
|
|
{
|
|
|
|
page_t* undo_page;
|
|
|
|
trx_ulogf_t* log_hdr;
|
|
|
|
fil_addr_t prev_log_addr;
|
|
|
|
trx_id_t trx_no;
|
|
|
|
mtr_t mtr;
|
|
|
|
|
|
|
|
mutex_enter(&(rseg->mutex));
|
|
|
|
|
|
|
|
ut_a(rseg->last_page_no != FIL_NULL);
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.tail.commit = rseg->last_commit + 1;
|
|
|
|
purge_sys.tail.undo_no = 0;
|
|
|
|
purge_sys.next_stored = false;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mtr_start(&mtr);
|
|
|
|
|
|
|
|
undo_page = trx_undo_page_get_s_latched(
|
2018-03-26 17:23:47 +03:00
|
|
|
page_id_t(rseg->space->id, rseg->last_page_no), &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
log_hdr = undo_page + rseg->last_offset;
|
|
|
|
|
|
|
|
/* Increase the purge page count by one for every handled log */
|
|
|
|
|
|
|
|
(*n_pages_handled)++;
|
|
|
|
|
|
|
|
prev_log_addr = trx_purge_get_log_from_hist(
|
|
|
|
flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
|
|
|
|
|
|
|
|
if (prev_log_addr.page == FIL_NULL) {
|
|
|
|
/* No logs left in the history list */
|
|
|
|
|
|
|
|
rseg->last_page_no = FIL_NULL;
|
|
|
|
|
|
|
|
mutex_exit(&(rseg->mutex));
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_exit(&rseg->mutex);
|
|
|
|
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
2017-08-15 17:18:55 +03:00
|
|
|
/* Read the previous log header. */
|
2014-02-26 19:11:54 +01:00
|
|
|
mtr_start(&mtr);
|
|
|
|
|
2018-03-26 17:23:47 +03:00
|
|
|
log_hdr = trx_undo_page_get_s_latched(page_id_t(rseg->space->id,
|
2016-08-12 11:17:45 +03:00
|
|
|
prev_log_addr.page),
|
2017-03-09 22:06:22 +02:00
|
|
|
&mtr)
|
2014-02-26 19:11:54 +01:00
|
|
|
+ prev_log_addr.boffset;
|
|
|
|
|
|
|
|
trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
|
2017-08-15 17:18:55 +03:00
|
|
|
unsigned purge = mach_read_from_2(log_hdr + TRX_UNDO_NEEDS_PURGE);
|
|
|
|
ut_ad(purge <= 1);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
|
|
|
mutex_enter(&(rseg->mutex));
|
|
|
|
|
|
|
|
rseg->last_page_no = prev_log_addr.page;
|
|
|
|
rseg->last_offset = prev_log_addr.boffset;
|
2018-02-21 19:02:32 +02:00
|
|
|
rseg->set_last_trx_no(trx_no, purge != 0);
|
2017-08-15 17:18:55 +03:00
|
|
|
rseg->needs_purge = purge != 0;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Purge can also produce events, however these are already ordered
|
|
|
|
in the rollback segment and any user generated event will be greater
|
|
|
|
than the events that Purge produces. ie. Purge can never produce
|
|
|
|
events from an empty rollback segment. */
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
mutex_enter(&purge_sys.pq_mutex);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.purge_queue.push(*rseg);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
mutex_exit(&purge_sys.pq_mutex);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mutex_exit(&rseg->mutex);
|
|
|
|
}
|
|
|
|
|
2017-03-09 22:06:22 +02:00
|
|
|
/** Position the purge sys "iterator" on the undo record to use for purging. */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
void
|
2017-03-09 22:06:22 +02:00
|
|
|
trx_purge_read_undo_rec()
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
ulint offset;
|
|
|
|
ulint page_no;
|
|
|
|
ib_uint64_t undo_no;
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.hdr_offset = purge_sys.rseg->last_offset;
|
|
|
|
page_no = purge_sys.hdr_page_no = purge_sys.rseg->last_page_no;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
if (purge_sys.rseg->needs_purge) {
|
2014-02-26 19:11:54 +01:00
|
|
|
mtr_t mtr;
|
2017-08-15 17:18:55 +03:00
|
|
|
mtr.start();
|
|
|
|
if (trx_undo_rec_t* undo_rec = trx_undo_get_first_rec(
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.rseg->space, purge_sys.hdr_page_no,
|
|
|
|
purge_sys.hdr_offset, RW_S_LATCH, &mtr)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
offset = page_offset(undo_rec);
|
|
|
|
undo_no = trx_undo_rec_get_undo_no(undo_rec);
|
|
|
|
page_no = page_get_page_no(page_align(undo_rec));
|
|
|
|
} else {
|
|
|
|
offset = 0;
|
|
|
|
undo_no = 0;
|
|
|
|
}
|
|
|
|
|
2017-08-15 17:18:55 +03:00
|
|
|
mtr.commit();
|
2014-02-26 19:11:54 +01:00
|
|
|
} else {
|
|
|
|
offset = 0;
|
|
|
|
undo_no = 0;
|
|
|
|
}
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.offset = offset;
|
|
|
|
purge_sys.page_no = page_no;
|
|
|
|
purge_sys.tail.undo_no = undo_no;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.next_stored = true;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************//**
|
|
|
|
Chooses the next undo log to purge and updates the info in purge_sys. This
|
|
|
|
function is used to initialize purge_sys when the next record to purge is
|
|
|
|
not known, and also to update the purge system info on the next record when
|
|
|
|
purge has handled the whole undo log for a transaction. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
trx_purge_choose_next_log(void)
|
|
|
|
/*===========================*/
|
|
|
|
{
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_ad(!purge_sys.next_stored);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
if (purge_sys.rseg_iter.set_next()) {
|
2017-03-09 22:06:22 +02:00
|
|
|
trx_purge_read_undo_rec();
|
2014-02-26 19:11:54 +01:00
|
|
|
} else {
|
|
|
|
/* There is nothing to do yet. */
|
|
|
|
os_thread_yield();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************//**
|
|
|
|
Gets the next record to purge and updates the info in the purge system.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return copy of an undo log record or pointer to the dummy undo log record */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
trx_undo_rec_t*
|
|
|
|
trx_purge_get_next_rec(
|
|
|
|
/*===================*/
|
|
|
|
ulint* n_pages_handled,/*!< in/out: number of UNDO pages
|
|
|
|
handled */
|
|
|
|
mem_heap_t* heap) /*!< in: memory heap where copied */
|
|
|
|
{
|
|
|
|
trx_undo_rec_t* rec;
|
|
|
|
trx_undo_rec_t* rec_copy;
|
|
|
|
trx_undo_rec_t* rec2;
|
|
|
|
page_t* undo_page;
|
|
|
|
page_t* page;
|
|
|
|
ulint offset;
|
|
|
|
ulint page_no;
|
|
|
|
ulint space;
|
|
|
|
mtr_t mtr;
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_ad(purge_sys.next_stored);
|
|
|
|
ut_ad(purge_sys.tail.trx_no() < purge_sys.view.low_limit_no());
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-03-26 17:23:47 +03:00
|
|
|
space = purge_sys.rseg->space->id;
|
2018-02-22 09:30:41 +02:00
|
|
|
page_no = purge_sys.page_no;
|
|
|
|
offset = purge_sys.offset;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (offset == 0) {
|
|
|
|
/* It is the dummy undo log record, which means that there is
|
|
|
|
no need to purge this undo log */
|
|
|
|
|
|
|
|
trx_purge_rseg_get_next_history_log(
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.rseg, n_pages_handled);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Look for the next undo log and record to purge */
|
|
|
|
|
|
|
|
trx_purge_choose_next_log();
|
|
|
|
|
|
|
|
return(&trx_purge_dummy_rec);
|
|
|
|
}
|
|
|
|
|
|
|
|
mtr_start(&mtr);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
undo_page = trx_undo_page_get_s_latched(page_id_t(space, page_no),
|
2017-03-09 22:06:22 +02:00
|
|
|
&mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
rec = undo_page + offset;
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
rec2 = trx_undo_page_get_next_rec(rec, purge_sys.hdr_page_no,
|
|
|
|
purge_sys.hdr_offset);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-08-15 17:18:55 +03:00
|
|
|
if (rec2 == NULL) {
|
2018-02-22 09:30:41 +02:00
|
|
|
rec2 = trx_undo_get_next_rec(rec, purge_sys.hdr_page_no,
|
|
|
|
purge_sys.hdr_offset, &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rec2 == NULL) {
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
|
|
|
trx_purge_rseg_get_next_history_log(
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.rseg, n_pages_handled);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Look for the next undo log and record to purge */
|
|
|
|
|
|
|
|
trx_purge_choose_next_log();
|
|
|
|
|
|
|
|
mtr_start(&mtr);
|
|
|
|
|
|
|
|
undo_page = trx_undo_page_get_s_latched(
|
2017-03-09 22:06:22 +02:00
|
|
|
page_id_t(space, page_no), &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
rec = undo_page + offset;
|
|
|
|
} else {
|
|
|
|
page = page_align(rec2);
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.offset = rec2 - page;
|
|
|
|
purge_sys.page_no = page_get_page_no(page);
|
|
|
|
purge_sys.tail.undo_no = trx_undo_rec_get_undo_no(rec2);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (undo_page != page) {
|
|
|
|
/* We advance to a new page of the undo log: */
|
|
|
|
(*n_pages_handled)++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rec_copy = trx_undo_rec_copy(rec, heap);
|
|
|
|
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
|
|
|
return(rec_copy);
|
|
|
|
}
|
|
|
|
|
|
|
|
/********************************************************************//**
|
|
|
|
Fetches the next undo log record from the history list to purge. It must be
|
|
|
|
released with the corresponding release function.
|
|
|
|
@return copy of an undo log record or pointer to trx_purge_dummy_rec,
|
|
|
|
if the whole undo log can skipped in purge; NULL if none left */
|
2016-09-06 09:43:16 +03:00
|
|
|
static MY_ATTRIBUTE((warn_unused_result))
|
2014-02-26 19:11:54 +01:00
|
|
|
trx_undo_rec_t*
|
|
|
|
trx_purge_fetch_next_rec(
|
|
|
|
/*=====================*/
|
|
|
|
roll_ptr_t* roll_ptr, /*!< out: roll pointer to undo record */
|
|
|
|
ulint* n_pages_handled,/*!< in/out: number of UNDO log pages
|
|
|
|
handled */
|
|
|
|
mem_heap_t* heap) /*!< in: memory heap where copied */
|
|
|
|
{
|
2018-02-22 09:30:41 +02:00
|
|
|
if (!purge_sys.next_stored) {
|
2014-02-26 19:11:54 +01:00
|
|
|
trx_purge_choose_next_log();
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
if (!purge_sys.next_stored) {
|
2016-08-12 11:17:45 +03:00
|
|
|
DBUG_PRINT("ib_purge",
|
|
|
|
("no logs left in the history list"));
|
2014-02-26 19:11:54 +01:00
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
if (purge_sys.tail.trx_no() >= purge_sys.view.low_limit_no()) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fprintf(stderr, "Thread %lu purging trx %llu undo record %llu\n",
|
|
|
|
os_thread_get_curr_id(), iter->trx_no, iter->undo_no); */
|
|
|
|
|
|
|
|
*roll_ptr = trx_undo_build_roll_ptr(
|
2017-08-15 17:18:55 +03:00
|
|
|
/* row_purge_record_func() will later set
|
|
|
|
ROLL_PTR_INSERT_FLAG for TRX_UNDO_INSERT_REC */
|
|
|
|
false,
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.rseg->id,
|
|
|
|
purge_sys.page_no, purge_sys.offset);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* The following call will advance the stored values of the
|
|
|
|
purge iterator. */
|
|
|
|
|
|
|
|
return(trx_purge_get_next_rec(n_pages_handled, heap));
|
|
|
|
}
|
|
|
|
|
2018-02-22 09:18:53 +02:00
|
|
|
/** Run a purge batch.
|
|
|
|
@param n_purge_threads number of purge threads
|
2016-08-12 11:17:45 +03:00
|
|
|
@return number of undo log pages handled in the batch */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
ulint
|
2018-02-22 09:18:53 +02:00
|
|
|
trx_purge_attach_undo_recs(ulint n_purge_threads)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
que_thr_t* thr;
|
|
|
|
ulint i = 0;
|
|
|
|
ulint n_pages_handled = 0;
|
2018-02-22 09:30:41 +02:00
|
|
|
ulint n_thrs = UT_LIST_GET_LEN(purge_sys.query->thrs);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ut_a(n_purge_threads > 0);
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.head = purge_sys.tail;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Debug code to validate some pre-requisites and reset done flag. */
|
2018-02-22 09:30:41 +02:00
|
|
|
for (thr = UT_LIST_GET_FIRST(purge_sys.query->thrs);
|
2014-02-26 19:11:54 +01:00
|
|
|
thr != NULL && i < n_purge_threads;
|
|
|
|
thr = UT_LIST_GET_NEXT(thrs, thr), ++i) {
|
|
|
|
|
|
|
|
purge_node_t* node;
|
|
|
|
|
|
|
|
/* Get the purge node. */
|
|
|
|
node = (purge_node_t*) thr->child;
|
|
|
|
|
|
|
|
ut_a(que_node_get_type(node) == QUE_NODE_PURGE);
|
|
|
|
ut_a(node->undo_recs == NULL);
|
|
|
|
ut_a(node->done);
|
|
|
|
|
|
|
|
node->done = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* There should never be fewer nodes than threads, the inverse
|
|
|
|
however is allowed because we only use purge threads as needed. */
|
|
|
|
ut_a(i == n_purge_threads);
|
|
|
|
|
|
|
|
/* Fetch and parse the UNDO records. The UNDO records are added
|
|
|
|
to a per purge node vector. */
|
2018-02-22 09:30:41 +02:00
|
|
|
thr = UT_LIST_GET_FIRST(purge_sys.query->thrs);
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_a(n_thrs > 0 && thr != NULL);
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_ad(purge_sys.head <= purge_sys.tail);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
2018-02-22 09:18:53 +02:00
|
|
|
const ulint batch_size = srv_purge_batch_size;
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
for (;;) {
|
|
|
|
purge_node_t* node;
|
|
|
|
trx_purge_rec_t* purge_rec;
|
|
|
|
|
|
|
|
ut_a(!thr->is_active);
|
|
|
|
|
|
|
|
/* Get the purge node. */
|
|
|
|
node = (purge_node_t*) thr->child;
|
|
|
|
ut_a(que_node_get_type(node) == QUE_NODE_PURGE);
|
|
|
|
|
|
|
|
purge_rec = static_cast<trx_purge_rec_t*>(
|
|
|
|
mem_heap_zalloc(node->heap, sizeof(*purge_rec)));
|
|
|
|
|
|
|
|
/* Track the max {trx_id, undo_no} for truncating the
|
|
|
|
UNDO logs once we have purged the records. */
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
if (purge_sys.head <= purge_sys.tail) {
|
|
|
|
purge_sys.head = purge_sys.tail;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
/* Fetch the next record, and advance the purge_sys.tail. */
|
2014-02-26 19:11:54 +01:00
|
|
|
purge_rec->undo_rec = trx_purge_fetch_next_rec(
|
|
|
|
&purge_rec->roll_ptr, &n_pages_handled, node->heap);
|
|
|
|
|
|
|
|
if (purge_rec->undo_rec != NULL) {
|
|
|
|
|
|
|
|
if (node->undo_recs == NULL) {
|
|
|
|
node->undo_recs = ib_vector_create(
|
|
|
|
ib_heap_allocator_create(node->heap),
|
|
|
|
sizeof(trx_purge_rec_t),
|
|
|
|
batch_size);
|
|
|
|
} else {
|
|
|
|
ut_a(!ib_vector_is_empty(node->undo_recs));
|
|
|
|
}
|
|
|
|
|
|
|
|
ib_vector_push(node->undo_recs, purge_rec);
|
|
|
|
|
|
|
|
if (n_pages_handled >= batch_size) {
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
thr = UT_LIST_GET_NEXT(thrs, thr);
|
|
|
|
|
|
|
|
if (!(++i % n_purge_threads)) {
|
2018-02-22 09:30:41 +02:00
|
|
|
thr = UT_LIST_GET_FIRST(purge_sys.query->thrs);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ut_a(thr != NULL);
|
|
|
|
}
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_ad(purge_sys.head <= purge_sys.tail);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
return(n_pages_handled);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
Calculate the DML delay required.
|
|
|
|
@return delay in microseconds or ULINT_MAX */
|
|
|
|
static
|
|
|
|
ulint
|
|
|
|
trx_purge_dml_delay(void)
|
|
|
|
/*=====================*/
|
|
|
|
{
|
|
|
|
/* Determine how much data manipulation language (DML) statements
|
|
|
|
need to be delayed in order to reduce the lagging of the purge
|
|
|
|
thread. */
|
|
|
|
ulint delay = 0; /* in microseconds; default: no delay */
|
|
|
|
|
|
|
|
/* If purge lag is set (ie. > 0) then calculate the new DML delay.
|
|
|
|
Note: we do a dirty read of the trx_sys_t data structure here,
|
2017-12-22 16:15:41 +02:00
|
|
|
without holding trx_sys.mutex. */
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (srv_max_purge_lag > 0) {
|
|
|
|
float ratio;
|
|
|
|
|
2018-02-20 10:02:42 +02:00
|
|
|
ratio = float(trx_sys.history_size()) / srv_max_purge_lag;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (ratio > 1.0) {
|
|
|
|
/* If the history list length exceeds the
|
|
|
|
srv_max_purge_lag, the data manipulation
|
|
|
|
statements are delayed by at least 5000
|
|
|
|
microseconds. */
|
|
|
|
delay = (ulint) ((ratio - .5) * 10000);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (delay > srv_max_purge_lag_delay) {
|
|
|
|
delay = srv_max_purge_lag_delay;
|
|
|
|
}
|
|
|
|
|
|
|
|
MONITOR_SET(MONITOR_DML_PURGE_DELAY, delay);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(delay);
|
|
|
|
}
|
|
|
|
|
2018-02-22 09:18:53 +02:00
|
|
|
/** Wait for pending purge jobs to complete. */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
void
|
2018-02-22 09:18:53 +02:00
|
|
|
trx_purge_wait_for_workers_to_complete()
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2018-02-22 09:30:41 +02:00
|
|
|
ulint n_submitted = purge_sys.n_submitted;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Ensure that the work queue empties out. */
|
2018-02-22 09:30:41 +02:00
|
|
|
while ((ulint) my_atomic_loadlint(&purge_sys.n_completed) != n_submitted) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (srv_get_task_queue_length() > 0) {
|
|
|
|
srv_release_threads(SRV_WORKER, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
os_thread_yield();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* None of the worker threads should be doing any work. */
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_a(purge_sys.n_submitted == purge_sys.n_completed);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* There should be no outstanding tasks as long
|
|
|
|
as the worker threads are active. */
|
|
|
|
ut_a(srv_get_task_queue_length() == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
This function runs a purge batch.
|
2016-08-12 11:17:45 +03:00
|
|
|
@return number of undo log pages handled in the batch */
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint
|
|
|
|
trx_purge(
|
|
|
|
/*======*/
|
|
|
|
ulint n_purge_threads, /*!< in: number of purge tasks
|
|
|
|
to submit to the work queue */
|
|
|
|
bool truncate) /*!< in: truncate history if true */
|
|
|
|
{
|
|
|
|
que_thr_t* thr = NULL;
|
|
|
|
ulint n_pages_handled;
|
|
|
|
|
|
|
|
ut_a(n_purge_threads > 0);
|
|
|
|
|
|
|
|
srv_dml_needed_delay = trx_purge_dml_delay();
|
|
|
|
|
|
|
|
/* The number of tasks submitted should be completed. */
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_a(purge_sys.n_submitted == purge_sys.n_completed);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
rw_lock_x_lock(&purge_sys.latch);
|
2018-01-30 20:59:42 +04:00
|
|
|
trx_sys.clone_oldest_view();
|
2018-02-22 09:30:41 +02:00
|
|
|
rw_lock_x_unlock(&purge_sys.latch);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
if (srv_purge_view_update_only_debug) {
|
|
|
|
return(0);
|
|
|
|
}
|
2016-08-12 11:17:45 +03:00
|
|
|
#endif /* UNIV_DEBUG */
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Fetch the UNDO recs that need to be purged. */
|
2018-02-22 09:18:53 +02:00
|
|
|
n_pages_handled = trx_purge_attach_undo_recs(n_purge_threads);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Do we do an asynchronous purge or not ? */
|
|
|
|
if (n_purge_threads > 1) {
|
|
|
|
ulint i = 0;
|
|
|
|
|
|
|
|
/* Submit the tasks to the work queue. */
|
|
|
|
for (i = 0; i < n_purge_threads - 1; ++i) {
|
|
|
|
thr = que_fork_scheduler_round_robin(
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.query, thr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
ut_a(thr != NULL);
|
|
|
|
|
|
|
|
srv_que_task_enqueue_low(thr);
|
|
|
|
}
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
thr = que_fork_scheduler_round_robin(purge_sys.query, thr);
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_a(thr != NULL);
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.n_submitted += n_purge_threads - 1;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
goto run_synchronously;
|
|
|
|
|
|
|
|
/* Do it synchronously. */
|
|
|
|
} else {
|
2018-02-22 09:30:41 +02:00
|
|
|
thr = que_fork_scheduler_round_robin(purge_sys.query, NULL);
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(thr);
|
|
|
|
|
|
|
|
run_synchronously:
|
2018-02-22 09:30:41 +02:00
|
|
|
++purge_sys.n_submitted;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
que_run_threads(thr);
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
my_atomic_addlint(&purge_sys.n_completed, 1);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (n_purge_threads > 1) {
|
2018-02-22 09:18:53 +02:00
|
|
|
trx_purge_wait_for_workers_to_complete();
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_a(purge_sys.n_submitted == purge_sys.n_completed);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (truncate) {
|
2018-02-21 12:54:33 +02:00
|
|
|
trx_purge_truncate_history();
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
MONITOR_INC_VALUE(MONITOR_PURGE_INVOKED, 1);
|
|
|
|
MONITOR_INC_VALUE(MONITOR_PURGE_N_PAGE_HANDLED, n_pages_handled);
|
|
|
|
|
|
|
|
return(n_pages_handled);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
Get the purge state.
|
|
|
|
@return purge state. */
|
|
|
|
purge_state_t
|
|
|
|
trx_purge_state(void)
|
|
|
|
/*=================*/
|
|
|
|
{
|
|
|
|
purge_state_t state;
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
rw_lock_x_lock(&purge_sys.latch);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
state = purge_sys.state;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
rw_lock_x_unlock(&purge_sys.latch);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
return(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
Stop purge and wait for it to stop, move to PURGE_STATE_STOP. */
|
|
|
|
void
|
|
|
|
trx_purge_stop(void)
|
|
|
|
/*================*/
|
|
|
|
{
|
2018-02-22 09:30:41 +02:00
|
|
|
rw_lock_x_lock(&purge_sys.latch);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
switch (purge_sys.state) {
|
2017-12-13 14:16:15 +02:00
|
|
|
case PURGE_STATE_INIT:
|
|
|
|
case PURGE_STATE_DISABLED:
|
|
|
|
ut_error;
|
|
|
|
case PURGE_STATE_EXIT:
|
|
|
|
/* Shutdown must have been initiated during
|
|
|
|
FLUSH TABLES FOR EXPORT. */
|
|
|
|
ut_ad(!srv_undo_sources);
|
|
|
|
unlock:
|
2018-02-22 09:30:41 +02:00
|
|
|
rw_lock_x_unlock(&purge_sys.latch);
|
2017-12-13 14:16:15 +02:00
|
|
|
break;
|
|
|
|
case PURGE_STATE_STOP:
|
|
|
|
ut_ad(srv_n_purge_threads > 0);
|
2018-02-22 09:30:41 +02:00
|
|
|
++purge_sys.n_stop;
|
|
|
|
purge_sys.state = PURGE_STATE_STOP;
|
|
|
|
if (!purge_sys.running) {
|
2017-12-13 14:16:15 +02:00
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
ib::info() << "Waiting for purge to stop";
|
|
|
|
do {
|
2018-02-22 09:30:41 +02:00
|
|
|
rw_lock_x_unlock(&purge_sys.latch);
|
2017-12-13 14:16:15 +02:00
|
|
|
os_thread_sleep(10000);
|
2018-02-22 09:30:41 +02:00
|
|
|
rw_lock_x_lock(&purge_sys.latch);
|
|
|
|
} while (purge_sys.running);
|
2017-12-13 14:16:15 +02:00
|
|
|
goto unlock;
|
|
|
|
case PURGE_STATE_RUN:
|
|
|
|
ut_ad(srv_n_purge_threads > 0);
|
2018-02-22 09:30:41 +02:00
|
|
|
++purge_sys.n_stop;
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::info() << "Stopping purge";
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* We need to wakeup the purge thread in case it is suspended,
|
|
|
|
so that it can acknowledge the state change. */
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
const int64_t sig_count = os_event_reset(purge_sys.event);
|
|
|
|
purge_sys.state = PURGE_STATE_STOP;
|
2014-02-26 19:11:54 +01:00
|
|
|
srv_purge_wakeup();
|
2018-02-22 09:30:41 +02:00
|
|
|
rw_lock_x_unlock(&purge_sys.latch);
|
2014-02-26 19:11:54 +01:00
|
|
|
/* Wait for purge coordinator to signal that it
|
|
|
|
is suspended. */
|
2018-02-22 09:30:41 +02:00
|
|
|
os_event_wait_low(purge_sys.event, sig_count);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
MONITOR_INC_VALUE(MONITOR_PURGE_STOP_COUNT, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
Resume purge, move to PURGE_STATE_RUN. */
|
|
|
|
void
|
|
|
|
trx_purge_run(void)
|
|
|
|
/*===============*/
|
|
|
|
{
|
2018-02-22 09:30:41 +02:00
|
|
|
rw_lock_x_lock(&purge_sys.latch);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
switch (purge_sys.state) {
|
2014-02-26 19:11:54 +01:00
|
|
|
case PURGE_STATE_EXIT:
|
2017-12-13 14:16:15 +02:00
|
|
|
/* Shutdown must have been initiated during
|
|
|
|
FLUSH TABLES FOR EXPORT. */
|
|
|
|
ut_ad(!srv_undo_sources);
|
|
|
|
break;
|
|
|
|
case PURGE_STATE_INIT:
|
2014-02-26 19:11:54 +01:00
|
|
|
case PURGE_STATE_DISABLED:
|
|
|
|
ut_error;
|
|
|
|
|
|
|
|
case PURGE_STATE_RUN:
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_a(!purge_sys.n_stop);
|
2014-02-26 19:11:54 +01:00
|
|
|
break;
|
2017-02-17 10:32:21 +02:00
|
|
|
case PURGE_STATE_STOP:
|
2018-02-22 09:30:41 +02:00
|
|
|
ut_a(purge_sys.n_stop);
|
|
|
|
if (--purge_sys.n_stop == 0) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::info() << "Resuming purge";
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
purge_sys.state = PURGE_STATE_RUN;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
MONITOR_INC_VALUE(MONITOR_PURGE_RESUME_COUNT, 1);
|
|
|
|
}
|
|
|
|
|
2018-02-22 09:30:41 +02:00
|
|
|
rw_lock_x_unlock(&purge_sys.latch);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
srv_purge_wakeup();
|
|
|
|
}
|