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.
|
2017-04-25 09:26:01 +03:00
|
|
|
Copyright (c) 2017, 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"
|
2016-08-12 11:17:45 +03:00
|
|
|
#include "read0read.h"
|
2014-02-26 19:11:54 +01:00
|
|
|
#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"
|
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 */
|
2017-03-09 20:40:48 +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 */
|
|
|
|
const TrxUndoRsegs TrxUndoRsegsIterator::NullElement(UINT64_UNDEFINED);
|
|
|
|
|
|
|
|
/** Constructor */
|
2017-03-09 20:40:48 +02:00
|
|
|
TrxUndoRsegsIterator::TrxUndoRsegsIterator()
|
2016-08-12 11:17:45 +03:00
|
|
|
:
|
|
|
|
m_trx_undo_rsegs(NullElement),
|
|
|
|
m_iter(m_trx_undo_rsegs.end())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
/** Sets the next rseg to purge in purge_sys.
|
2017-03-09 22:06:22 +02:00
|
|
|
@return whether anything is to be purged */
|
|
|
|
inline
|
|
|
|
bool
|
2016-08-12 11:17:45 +03:00
|
|
|
TrxUndoRsegsIterator::set_next()
|
|
|
|
{
|
2017-03-09 20:40:48 +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. */
|
|
|
|
if (m_iter != m_trx_undo_rsegs.end()) {
|
|
|
|
|
|
|
|
/* We are still processing rollback segment from
|
|
|
|
the same transaction and so expected transaction
|
|
|
|
number shouldn't increase. Undo increment of
|
|
|
|
expected trx_no done by caller assuming rollback
|
|
|
|
segments from given transaction are done. */
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_sys->iter.trx_no = (*m_iter)->last_trx_no;
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
} else if (!purge_sys->purge_queue.empty()) {
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
/* Read the next element from the queue.
|
|
|
|
Combine elements if they have same transaction number.
|
|
|
|
This can happen if a transaction shares redo rollback segment
|
|
|
|
with another transaction that has already added it to purge
|
|
|
|
queue and former transaction also needs to schedule non-redo
|
|
|
|
rollback segment for purge. */
|
|
|
|
m_trx_undo_rsegs = NullElement;
|
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_pq_t& purge_queue = purge_sys->purge_queue;
|
|
|
|
|
|
|
|
while (!purge_queue.empty()) {
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
if (m_trx_undo_rsegs.get_trx_no() == UINT64_UNDEFINED) {
|
2017-03-09 20:40:48 +02:00
|
|
|
m_trx_undo_rsegs = purge_queue.top();
|
|
|
|
} else if (purge_queue.top().get_trx_no() ==
|
2016-08-12 11:17:45 +03:00
|
|
|
m_trx_undo_rsegs.get_trx_no()) {
|
|
|
|
m_trx_undo_rsegs.append(
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_queue.top());
|
2016-08-12 11:17:45 +03:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_queue.pop();
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
m_iter = m_trx_undo_rsegs.begin();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* Queue is empty, reset iterator. */
|
|
|
|
m_trx_undo_rsegs = NullElement;
|
|
|
|
m_iter = m_trx_undo_rsegs.end();
|
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
mutex_exit(&purge_sys->pq_mutex);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_sys->rseg = NULL;
|
2017-03-09 22:06:22 +02:00
|
|
|
return false;
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_sys->rseg = *m_iter++;
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
mutex_exit(&purge_sys->pq_mutex);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
ut_a(purge_sys->rseg != NULL);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
mutex_enter(&purge_sys->rseg->mutex);
|
2016-08-12 11:17:45 +03:00
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
ut_a(purge_sys->rseg->last_page_no != FIL_NULL);
|
|
|
|
ut_ad(purge_sys->rseg->last_trx_no == m_trx_undo_rsegs.get_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 */
|
|
|
|
ut_a(purge_sys->rseg->space == TRX_SYS_SPACE
|
|
|
|
|| srv_is_undo_tablespace(purge_sys->rseg->space));
|
2016-08-12 11:17:45 +03:00
|
|
|
|
|
|
|
ut_a(purge_sys->iter.trx_no <= purge_sys->rseg->last_trx_no);
|
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_sys->iter.trx_no = purge_sys->rseg->last_trx_no;
|
|
|
|
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
|
|
|
|
2017-03-09 20:40:48 +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.
|
2017-03-09 20:40:48 +02:00
|
|
|
@param[in,out] sess the purge session
|
2016-08-12 11:17:45 +03:00
|
|
|
@return own: the query graph */
|
2014-02-26 19:11:54 +01:00
|
|
|
static
|
|
|
|
que_t*
|
2017-03-09 20:40:48 +02:00
|
|
|
trx_purge_graph_build(sess_t* sess)
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2017-03-09 20:40:48 +02:00
|
|
|
ut_a(srv_n_purge_threads > 0);
|
|
|
|
/* A purge transaction is not a real transaction, we use a transaction
|
|
|
|
here only because the query threads code requires it. It is otherwise
|
|
|
|
quite unnecessary. We should get rid of it eventually. */
|
|
|
|
trx_t* trx = sess->trx;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
ut_ad(trx->sess == sess);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
trx->id = 0;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
/** Construct the purge system. */
|
|
|
|
purge_sys_t::purge_sys_t()
|
|
|
|
: sess(sess_open()), latch(), event(os_event_create(0)),
|
|
|
|
n_stop(0), running(false), state(PURGE_STATE_INIT),
|
|
|
|
query(trx_purge_graph_build(sess)),
|
|
|
|
view(), n_submitted(0), n_completed(0),
|
|
|
|
iter(), limit(),
|
2016-08-12 11:17:45 +03:00
|
|
|
#ifdef UNIV_DEBUG
|
2017-03-09 20:40:48 +02:00
|
|
|
done(),
|
2016-08-12 11:17:45 +03:00
|
|
|
#endif /* UNIV_DEBUG */
|
2017-03-09 20:40:48 +02:00
|
|
|
next_stored(false), rseg(NULL),
|
|
|
|
page_no(0), offset(0), hdr_page_no(0), hdr_offset(0),
|
|
|
|
rseg_iter(), purge_queue(), pq_mutex(), undo_trunc()
|
|
|
|
{
|
|
|
|
ut_ad(!purge_sys);
|
|
|
|
rw_lock_create(trx_purge_latch_key, &latch, SYNC_PURGE_LATCH);
|
|
|
|
mutex_create(LATCH_ID_PURGE_SYS_PQ, &pq_mutex);
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
/** Destruct the purge system. */
|
|
|
|
purge_sys_t::~purge_sys_t()
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2017-03-09 20:40:48 +02:00
|
|
|
ut_ad(this == purge_sys);
|
|
|
|
|
|
|
|
que_graph_free(query);
|
|
|
|
ut_a(sess->trx->id == 0);
|
|
|
|
sess->trx->state = TRX_STATE_NOT_STARTED;
|
|
|
|
sess_close(sess);
|
|
|
|
view.close();
|
|
|
|
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
|
|
|
{
|
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
|
|
|
|
|
|
|
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 */
|
|
|
|
|
|
|
|
if (UNIV_UNLIKELY(undo->id >= TRX_RSEG_N_SLOTS)) {
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::fatal() << "undo->id is " << undo->id;
|
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-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
|
|
|
|
|| trx_rollback_or_clean_is_active)
|
|
|
|
&& purge_sys->state == PURGE_STATE_INIT)
|
2017-06-21 12:22:02 +03:00
|
|
|
|| (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND
|
|
|
|
&& purge_sys->state == PURGE_STATE_DISABLED)
|
2017-08-08 19:54:12 +03:00
|
|
|
|| ((trx->undo_no == 0 || trx->in_mysql_trx_list)
|
|
|
|
&& srv_fast_shutdown));
|
2016-09-06 09:43:16 +03:00
|
|
|
|
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);
|
|
|
|
|
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
|
|
|
my_atomic_addlint(&trx_sys->rseg_history_len, 1);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Write the trx number to the undo log header */
|
|
|
|
mlog_write_ull(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr);
|
|
|
|
|
|
|
|
/* Write information about delete markings to the undo log header */
|
|
|
|
|
|
|
|
if (!undo->del_marks) {
|
|
|
|
mlog_write_ulint(undo_header + TRX_UNDO_DEL_MARKS, FALSE,
|
|
|
|
MLOG_2BYTES, mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rseg->last_page_no == FIL_NULL) {
|
|
|
|
rseg->last_page_no = undo->hdr_page_no;
|
|
|
|
rseg->last_offset = undo->hdr_offset;
|
|
|
|
rseg->last_trx_no = trx->no;
|
|
|
|
rseg->last_del_marks = undo->del_marks;
|
|
|
|
}
|
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);
|
|
|
|
trx_undo_mem_free(undo);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2017-01-05 10:48:03 +02:00
|
|
|
my_atomic_addlint(&trx_sys->rseg_history_len, -1);
|
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;
|
|
|
|
trx_ulogf_t* log_hdr;
|
|
|
|
trx_usegf_t* seg_hdr;
|
|
|
|
ulint seg_size;
|
|
|
|
ulint hist_size;
|
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
|
|
|
bool marked = false;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
page_t* undo_page;
|
|
|
|
|
|
|
|
mtr_start(&mtr);
|
|
|
|
|
|
|
|
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(
|
2017-03-09 22:06:22 +02:00
|
|
|
page_id_t(rseg->space, hdr_addr.page), &mtr);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
|
|
|
|
log_hdr = undo_page + hdr_addr.boffset;
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|
|
|
|
if (!marked) {
|
2016-08-12 11:17:45 +03:00
|
|
|
marked = true;
|
2014-02-26 19:11:54 +01:00
|
|
|
mlog_write_ulint(
|
|
|
|
log_hdr + TRX_UNDO_DEL_MARKS, FALSE,
|
|
|
|
MLOG_2BYTES, &mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fseg_free_step_not_header(
|
2016-08-12 11:17:45 +03:00
|
|
|
seg_hdr + TRX_UNDO_FSEG_HEADER, false, &mtr)) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_exit(&rseg->mutex);
|
|
|
|
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
seg_size = flst_get_len(seg_hdr + TRX_UNDO_PAGE_LIST);
|
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-01-04 18:16:37 +02:00
|
|
|
trx_purge_remove_log_hdr(rseg_hdr, log_hdr, &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. */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
} while (!fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER, false, &mtr));
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
|
|
|
|
MLOG_4BYTES, &mtr);
|
|
|
|
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
|
|
|
|
@param[in] limit truncate offset */
|
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_truncate_rseg_history(trx_rseg_t* rseg, const purge_iter_t* 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;
|
|
|
|
|
|
|
|
mtr_start(&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
|
|
|
ut_ad(rseg->is_persistent());
|
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
|
|
|
|
|
|
|
hdr_addr = trx_purge_get_log_from_hist(
|
|
|
|
flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
|
|
|
|
loop:
|
|
|
|
if (hdr_addr.page == FIL_NULL) {
|
|
|
|
|
|
|
|
mutex_exit(&(rseg->mutex));
|
|
|
|
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
undo_page = trx_undo_page_get(page_id_t(rseg->space, 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);
|
|
|
|
|
|
|
|
if (undo_trx_no >= limit->trx_no) {
|
|
|
|
|
2016-09-06 09:43:16 +03:00
|
|
|
/* limit space_id should match the rollback segment
|
|
|
|
space id to avoid freeing of the page belongs to
|
|
|
|
different rollback segment for the same trx_no. */
|
|
|
|
if (undo_trx_no == limit->trx_no
|
|
|
|
&& rseg->space == limit->undo_rseg_space) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
trx_undo_truncate_start(
|
2016-08-12 11:17:45 +03:00
|
|
|
rseg, hdr_addr.page,
|
2014-02-26 19:11:54 +01:00
|
|
|
hdr_addr.boffset, limit->undo_no);
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_exit(&(rseg->mutex));
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 */
|
|
|
|
|
|
|
|
mutex_exit(&(rseg->mutex));
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
2017-01-04 18:16:37 +02:00
|
|
|
/* calls the trx_purge_remove_log_hdr()
|
|
|
|
inside trx_purge_free_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
|
|
|
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);
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
mutex_exit(&(rseg->mutex));
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
mtr_start(&mtr);
|
|
|
|
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
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_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);
|
|
|
|
|
|
|
|
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) {
|
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 (trx_rseg_t* rseg = trx_sys->rseg_array[i]) {
|
|
|
|
ut_ad(rseg->is_persistent());
|
|
|
|
if (rseg->space == 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)
|
|
|
|
{
|
|
|
|
mutex_enter(&purge_sys->pq_mutex);
|
|
|
|
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. */
|
2017-03-09 20:40:48 +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
|
|
|
}
|
2017-03-09 20:40:48 +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) {
|
|
|
|
|
|
|
|
if ((*it2)->space
|
|
|
|
== undo_trunc->get_marked_space_id()) {
|
|
|
|
it->erase(it2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
if (it->size()) {
|
2016-08-12 11:17:45 +03:00
|
|
|
/* size != 0 suggest that there exist other rsegs that
|
|
|
|
needs processing so add this element to purge queue.
|
|
|
|
Note: Other rseg could be non-redo rsegs. */
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_sys->purge_queue.push(*it);
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
mutex_exit(&purge_sys->pq_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 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(
|
|
|
|
purge_iter_t* limit,
|
|
|
|
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)) {
|
|
|
|
|
|
|
|
if (limit->trx_no < undo->trx_id) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (purge_sys->rseg != NULL
|
|
|
|
&& purge_sys->rseg->last_page_no == FIL_NULL) {
|
|
|
|
/* If purge_sys->rseg is pointing to rseg that was recently
|
|
|
|
truncated then move to next rseg element.
|
|
|
|
Note: Ideally purge_sys->rseg should be NULL because purge
|
|
|
|
should complete processing of all the records but there is
|
|
|
|
purge_batch_size that can force the purge loop to exit before
|
|
|
|
all the records are purged and in this case purge_sys->rseg
|
|
|
|
could point to a valid rseg waiting for next purge cycle. */
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_sys->next_stored = false;
|
2016-08-12 11:17:45 +03:00
|
|
|
purge_sys->rseg = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
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(););
|
|
|
|
}
|
|
|
|
|
2014-02-26 19:11:54 +01:00
|
|
|
/********************************************************************//**
|
|
|
|
Removes unnecessary history data from rollback segments. NOTE that when this
|
|
|
|
function is called, the caller must not have any latches on undo log pages! */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
trx_purge_truncate_history(
|
|
|
|
/*========================*/
|
|
|
|
purge_iter_t* limit, /*!< in: truncate limit */
|
2016-08-12 11:17:45 +03:00
|
|
|
const ReadView* view) /*!< in: purge view */
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
2017-04-25 09:26:01 +03:00
|
|
|
ut_ad(trx_purge_check_limit());
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* We play safe and set the truncate limit at most to the purge view
|
|
|
|
low_limit number, though this is not necessary */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (limit->trx_no >= view->low_limit_no()) {
|
|
|
|
limit->trx_no = view->low_limit_no();
|
2014-02-26 19:11:54 +01:00
|
|
|
limit->undo_no = 0;
|
2016-08-12 11:17:45 +03:00
|
|
|
limit->undo_rseg_space = ULINT_UNDEFINED;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(limit->trx_no <= purge_sys->view.low_limit_no());
|
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) {
|
2014-02-26 19:11:54 +01:00
|
|
|
trx_rseg_t* rseg = trx_sys->rseg_array[i];
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (rseg != NULL) {
|
|
|
|
ut_a(rseg->id == i);
|
|
|
|
trx_purge_truncate_rseg_history(rseg, limit);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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--; ) {
|
2016-08-12 11:17:45 +03:00
|
|
|
trx_purge_mark_undo_for_truncate(&purge_sys->undo_trunc);
|
|
|
|
trx_purge_initiate_truncate(limit, &purge_sys->undo_trunc);
|
|
|
|
}
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************//**
|
|
|
|
Updates the last not yet purged history log info in rseg when we have purged
|
|
|
|
a whole undo log. Advances also purge_sys->purge_trx_no past the purged log. */
|
|
|
|
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;
|
|
|
|
ibool del_marks;
|
|
|
|
mtr_t mtr;
|
|
|
|
|
|
|
|
mutex_enter(&(rseg->mutex));
|
|
|
|
|
|
|
|
ut_a(rseg->last_page_no != FIL_NULL);
|
|
|
|
|
|
|
|
purge_sys->iter.trx_no = rseg->last_trx_no + 1;
|
|
|
|
purge_sys->iter.undo_no = 0;
|
2016-08-12 11:17:45 +03:00
|
|
|
purge_sys->iter.undo_rseg_space = ULINT_UNDEFINED;
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_sys->next_stored = false;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
mtr_start(&mtr);
|
|
|
|
|
|
|
|
undo_page = trx_undo_page_get_s_latched(
|
2017-03-09 22:06:22 +02:00
|
|
|
page_id_t(rseg->space, 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);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
trx_sys_mutex_enter();
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Add debug code to track history list corruption reported
|
|
|
|
on the MySQL mailing list on Nov 9, 2004. The fut0lst.cc
|
|
|
|
file-based list was corrupt. The prev node pointer was
|
|
|
|
FIL_NULL, even though the list length was over 8 million nodes!
|
|
|
|
We assume that purge truncates the history list in large
|
|
|
|
size pieces, and if we here reach the head of the list, the
|
|
|
|
list cannot be longer than 2000 000 undo logs now. */
|
|
|
|
|
|
|
|
if (trx_sys->rseg_history_len > 2000000) {
|
2016-08-12 11:17:45 +03:00
|
|
|
ib::warn() << "Purge reached the head of the history"
|
|
|
|
" list, but its length is still reported as "
|
|
|
|
<< trx_sys->rseg_history_len << "! Make"
|
|
|
|
" a detailed bug report, and submit it to"
|
|
|
|
" http://bugs.mysql.com";
|
2014-02-26 19:11:54 +01:00
|
|
|
ut_ad(0);
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
trx_sys_mutex_exit();
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_exit(&rseg->mutex);
|
|
|
|
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
|
|
|
/* Read the trx number and del marks from the previous log header */
|
|
|
|
mtr_start(&mtr);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
log_hdr = trx_undo_page_get_s_latched(page_id_t(rseg->space,
|
|
|
|
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);
|
|
|
|
|
|
|
|
del_marks = mach_read_from_2(log_hdr + TRX_UNDO_DEL_MARKS);
|
|
|
|
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
|
|
|
mutex_enter(&(rseg->mutex));
|
|
|
|
|
|
|
|
rseg->last_page_no = prev_log_addr.page;
|
|
|
|
rseg->last_offset = prev_log_addr.boffset;
|
|
|
|
rseg->last_trx_no = trx_no;
|
|
|
|
rseg->last_del_marks = del_marks;
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
TrxUndoRsegs elem(rseg->last_trx_no);
|
|
|
|
elem.push_back(rseg);
|
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. */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
mutex_enter(&purge_sys->pq_mutex);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_sys->purge_queue.push(elem);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03: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;
|
2016-08-12 11:17:45 +03:00
|
|
|
ulint undo_rseg_space;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
purge_sys->hdr_offset = purge_sys->rseg->last_offset;
|
|
|
|
page_no = purge_sys->hdr_page_no = purge_sys->rseg->last_page_no;
|
|
|
|
|
|
|
|
if (purge_sys->rseg->last_del_marks) {
|
|
|
|
mtr_t mtr;
|
|
|
|
trx_undo_rec_t* undo_rec = NULL;
|
|
|
|
|
|
|
|
mtr_start(&mtr);
|
|
|
|
|
|
|
|
undo_rec = trx_undo_get_first_rec(
|
|
|
|
purge_sys->rseg->space,
|
|
|
|
purge_sys->hdr_page_no,
|
|
|
|
purge_sys->hdr_offset, RW_S_LATCH, &mtr);
|
|
|
|
|
|
|
|
if (undo_rec != NULL) {
|
|
|
|
offset = page_offset(undo_rec);
|
|
|
|
undo_no = trx_undo_rec_get_undo_no(undo_rec);
|
2016-08-12 11:17:45 +03:00
|
|
|
undo_rseg_space = purge_sys->rseg->space;
|
2014-02-26 19:11:54 +01:00
|
|
|
page_no = page_get_page_no(page_align(undo_rec));
|
|
|
|
} else {
|
|
|
|
offset = 0;
|
|
|
|
undo_no = 0;
|
2016-08-12 11:17:45 +03:00
|
|
|
undo_rseg_space = ULINT_UNDEFINED;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
} else {
|
|
|
|
offset = 0;
|
|
|
|
undo_no = 0;
|
2016-08-12 11:17:45 +03:00
|
|
|
undo_rseg_space = ULINT_UNDEFINED;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
purge_sys->offset = offset;
|
|
|
|
purge_sys->page_no = page_no;
|
|
|
|
purge_sys->iter.undo_no = undo_no;
|
2016-08-12 11:17:45 +03:00
|
|
|
purge_sys->iter.undo_rseg_space = undo_rseg_space;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-03-09 20:40:48 +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)
|
|
|
|
/*===========================*/
|
|
|
|
{
|
2017-03-09 20:40:48 +02:00
|
|
|
ut_ad(!purge_sys->next_stored);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-03-09 22:06:22 +02:00
|
|
|
if (purge_sys->rseg_iter.set_next()) {
|
|
|
|
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;
|
|
|
|
|
|
|
|
ut_ad(purge_sys->next_stored);
|
2016-08-12 11:17:45 +03:00
|
|
|
ut_ad(purge_sys->iter.trx_no < purge_sys->view.low_limit_no());
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
space = purge_sys->rseg->space;
|
|
|
|
page_no = purge_sys->page_no;
|
|
|
|
offset = purge_sys->offset;
|
|
|
|
|
|
|
|
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(
|
|
|
|
purge_sys->rseg, n_pages_handled);
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
rec2 = rec;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
ulint type;
|
|
|
|
trx_undo_rec_t* next_rec;
|
|
|
|
ulint cmpl_info;
|
|
|
|
|
|
|
|
/* Try first to find the next record which requires a purge
|
|
|
|
operation from the same page of the same undo log */
|
|
|
|
|
|
|
|
next_rec = trx_undo_page_get_next_rec(
|
|
|
|
rec2, purge_sys->hdr_page_no, purge_sys->hdr_offset);
|
|
|
|
|
|
|
|
if (next_rec == NULL) {
|
|
|
|
rec2 = trx_undo_get_next_rec(
|
|
|
|
rec2, purge_sys->hdr_page_no,
|
|
|
|
purge_sys->hdr_offset, &mtr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
rec2 = next_rec;
|
|
|
|
|
|
|
|
type = trx_undo_rec_get_type(rec2);
|
|
|
|
|
|
|
|
if (type == TRX_UNDO_DEL_MARK_REC) {
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmpl_info = trx_undo_rec_get_cmpl_info(rec2);
|
|
|
|
|
|
|
|
if (trx_undo_rec_get_extern_storage(rec2)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((type == TRX_UNDO_UPD_EXIST_REC)
|
|
|
|
&& !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rec2 == NULL) {
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
|
|
|
trx_purge_rseg_get_next_history_log(
|
|
|
|
purge_sys->rseg, n_pages_handled);
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
purge_sys->offset = rec2 - page;
|
|
|
|
purge_sys->page_no = page_get_page_no(page);
|
|
|
|
purge_sys->iter.undo_no = trx_undo_rec_get_undo_no(rec2);
|
2016-08-12 11:17:45 +03:00
|
|
|
purge_sys->iter.undo_rseg_space = space;
|
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 */
|
|
|
|
{
|
|
|
|
if (!purge_sys->next_stored) {
|
|
|
|
trx_purge_choose_next_log();
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (purge_sys->iter.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(
|
|
|
|
FALSE, purge_sys->rseg->id,
|
|
|
|
purge_sys->page_no, purge_sys->offset);
|
|
|
|
|
|
|
|
/* The following call will advance the stored values of the
|
|
|
|
purge iterator. */
|
|
|
|
|
|
|
|
return(trx_purge_get_next_rec(n_pages_handled, heap));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
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
|
|
|
static
|
|
|
|
ulint
|
|
|
|
trx_purge_attach_undo_recs(
|
|
|
|
/*=======================*/
|
|
|
|
ulint n_purge_threads,/*!< in: number of purge threads */
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_sys_t* purge_sys, /*!< in/out: purge instance */
|
2014-02-26 19:11:54 +01:00
|
|
|
ulint batch_size) /*!< in: no. of pages to purge */
|
|
|
|
{
|
|
|
|
que_thr_t* thr;
|
|
|
|
ulint i = 0;
|
|
|
|
ulint n_pages_handled = 0;
|
|
|
|
ulint n_thrs = UT_LIST_GET_LEN(purge_sys->query->thrs);
|
|
|
|
|
|
|
|
ut_a(n_purge_threads > 0);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
purge_sys->limit = purge_sys->iter;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
/* Debug code to validate some pre-requisites and reset done flag. */
|
|
|
|
for (thr = UT_LIST_GET_FIRST(purge_sys->query->thrs);
|
|
|
|
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. */
|
|
|
|
thr = UT_LIST_GET_FIRST(purge_sys->query->thrs);
|
|
|
|
ut_a(n_thrs > 0 && thr != NULL);
|
|
|
|
|
|
|
|
ut_ad(trx_purge_check_limit());
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
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. */
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (trx_purge_check_limit()) {
|
|
|
|
purge_sys->limit = purge_sys->iter;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Fetch the next record, and advance the purge_sys->iter. */
|
|
|
|
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)) {
|
|
|
|
thr = UT_LIST_GET_FIRST(purge_sys->query->thrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_a(thr != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_ad(trx_purge_check_limit());
|
|
|
|
|
|
|
|
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,
|
|
|
|
without holding trx_sys->mutex. */
|
|
|
|
|
|
|
|
if (srv_max_purge_lag > 0) {
|
|
|
|
float ratio;
|
|
|
|
|
|
|
|
ratio = float(trx_sys->rseg_history_len) / srv_max_purge_lag;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
Wait for pending purge jobs to complete. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
trx_purge_wait_for_workers_to_complete(
|
|
|
|
/*===================================*/
|
2017-03-09 20:40:48 +02:00
|
|
|
purge_sys_t* purge_sys) /*!< in: purge instance */
|
2014-02-26 19:11:54 +01:00
|
|
|
{
|
|
|
|
ulint n_submitted = purge_sys->n_submitted;
|
|
|
|
|
|
|
|
/* Ensure that the work queue empties out. */
|
2016-09-09 15:05:59 +04: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. */
|
|
|
|
ut_a(purge_sys->n_submitted == purge_sys->n_completed);
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
ulint batch_size, /*!< in: the maximum number of records
|
|
|
|
to purge in one batch */
|
|
|
|
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. */
|
|
|
|
ut_a(purge_sys->n_submitted == purge_sys->n_completed);
|
|
|
|
|
|
|
|
rw_lock_x_lock(&purge_sys->latch);
|
2016-08-12 11:17:45 +03:00
|
|
|
trx_sys->mvcc->clone_oldest_view(&purge_sys->view);
|
2014-02-26 19:11:54 +01:00
|
|
|
rw_lock_x_unlock(&purge_sys->latch);
|
|
|
|
|
|
|
|
#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. */
|
|
|
|
n_pages_handled = trx_purge_attach_undo_recs(
|
2016-08-12 11:17:45 +03:00
|
|
|
n_purge_threads, purge_sys, batch_size);
|
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(
|
|
|
|
purge_sys->query, thr);
|
|
|
|
|
|
|
|
ut_a(thr != NULL);
|
|
|
|
|
|
|
|
srv_que_task_enqueue_low(thr);
|
|
|
|
}
|
|
|
|
|
|
|
|
thr = que_fork_scheduler_round_robin(purge_sys->query, thr);
|
|
|
|
ut_a(thr != NULL);
|
|
|
|
|
|
|
|
purge_sys->n_submitted += n_purge_threads - 1;
|
|
|
|
|
|
|
|
goto run_synchronously;
|
|
|
|
|
|
|
|
/* Do it synchronously. */
|
|
|
|
} else {
|
|
|
|
thr = que_fork_scheduler_round_robin(purge_sys->query, NULL);
|
|
|
|
ut_ad(thr);
|
|
|
|
|
|
|
|
run_synchronously:
|
|
|
|
++purge_sys->n_submitted;
|
|
|
|
|
|
|
|
que_run_threads(thr);
|
|
|
|
|
2016-09-09 15:05:59 +04:00
|
|
|
my_atomic_addlint(
|
|
|
|
&purge_sys->n_completed, 1);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (n_purge_threads > 1) {
|
|
|
|
trx_purge_wait_for_workers_to_complete(purge_sys);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_a(purge_sys->n_submitted == purge_sys->n_completed);
|
|
|
|
|
|
|
|
#ifdef UNIV_DEBUG
|
2014-02-26 19:23:04 +01:00
|
|
|
rw_lock_x_lock(&purge_sys->latch);
|
2014-02-26 19:11:54 +01:00
|
|
|
if (purge_sys->limit.trx_no == 0) {
|
|
|
|
purge_sys->done = purge_sys->iter;
|
|
|
|
} else {
|
|
|
|
purge_sys->done = purge_sys->limit;
|
|
|
|
}
|
2014-02-26 19:23:04 +01:00
|
|
|
rw_lock_x_unlock(&purge_sys->latch);
|
2014-02-26 19:11:54 +01:00
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
|
|
|
if (truncate) {
|
2017-04-25 09:26:01 +03:00
|
|
|
trx_purge_truncate_history(
|
|
|
|
purge_sys->limit.trx_no
|
|
|
|
? &purge_sys->limit
|
|
|
|
: &purge_sys->iter,
|
|
|
|
&purge_sys->view);
|
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;
|
|
|
|
|
|
|
|
rw_lock_x_lock(&purge_sys->latch);
|
|
|
|
|
|
|
|
state = purge_sys->state;
|
|
|
|
|
|
|
|
rw_lock_x_unlock(&purge_sys->latch);
|
|
|
|
|
|
|
|
return(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
Stop purge and wait for it to stop, move to PURGE_STATE_STOP. */
|
|
|
|
void
|
|
|
|
trx_purge_stop(void)
|
|
|
|
/*================*/
|
|
|
|
{
|
|
|
|
ut_a(srv_n_purge_threads > 0);
|
|
|
|
|
|
|
|
rw_lock_x_lock(&purge_sys->latch);
|
|
|
|
|
2017-02-17 10:32:21 +02:00
|
|
|
const int64_t sig_count = os_event_reset(purge_sys->event);
|
|
|
|
const purge_state_t state = purge_sys->state;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-02-17 10:32:21 +02:00
|
|
|
ut_a(state == PURGE_STATE_RUN || state == PURGE_STATE_STOP);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2017-02-17 10:32:21 +02:00
|
|
|
++purge_sys->n_stop;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
if (state == PURGE_STATE_RUN) {
|
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. */
|
|
|
|
|
|
|
|
srv_purge_wakeup();
|
|
|
|
}
|
|
|
|
|
|
|
|
purge_sys->state = PURGE_STATE_STOP;
|
|
|
|
|
|
|
|
if (state != PURGE_STATE_STOP) {
|
2017-02-17 10:32:21 +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. */
|
|
|
|
os_event_wait_low(purge_sys->event, sig_count);
|
2016-08-12 11:17:45 +03:00
|
|
|
} else {
|
|
|
|
bool once = true;
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
/* Wait for purge to signal that it has actually stopped. */
|
|
|
|
while (purge_sys->running) {
|
2014-02-26 19:11:54 +01:00
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
if (once) {
|
|
|
|
ib::info() << "Waiting for purge to stop";
|
|
|
|
once = false;
|
2014-02-26 19:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
rw_lock_x_unlock(&purge_sys->latch);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
os_thread_sleep(10000);
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
rw_lock_x_lock(&purge_sys->latch);
|
2016-08-12 11:17:45 +03:00
|
|
|
}
|
2014-02-26 19:11:54 +01:00
|
|
|
|
|
|
|
rw_lock_x_unlock(&purge_sys->latch);
|
|
|
|
}
|
|
|
|
|
|
|
|
MONITOR_INC_VALUE(MONITOR_PURGE_STOP_COUNT, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************//**
|
|
|
|
Resume purge, move to PURGE_STATE_RUN. */
|
|
|
|
void
|
|
|
|
trx_purge_run(void)
|
|
|
|
/*===============*/
|
|
|
|
{
|
|
|
|
rw_lock_x_lock(&purge_sys->latch);
|
|
|
|
|
2016-08-12 11:17:45 +03:00
|
|
|
switch (purge_sys->state) {
|
2014-02-26 19:11:54 +01:00
|
|
|
case PURGE_STATE_INIT:
|
|
|
|
case PURGE_STATE_EXIT:
|
|
|
|
case PURGE_STATE_DISABLED:
|
|
|
|
ut_error;
|
|
|
|
|
|
|
|
case PURGE_STATE_RUN:
|
2017-02-17 10:32:21 +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:
|
|
|
|
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
|
|
|
|
|
|
|
purge_sys->state = PURGE_STATE_RUN;
|
|
|
|
}
|
|
|
|
|
|
|
|
MONITOR_INC_VALUE(MONITOR_PURGE_RESUME_COUNT, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
rw_lock_x_unlock(&purge_sys->latch);
|
|
|
|
|
|
|
|
srv_purge_wakeup();
|
|
|
|
}
|