polardbxengine/storage/innobase/include/trx0purge.h

1078 lines
38 KiB
C++

/*****************************************************************************
Copyright (c) 1996, 2019, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2.0, as published by the
Free Software Foundation.
This program is also distributed with certain software (including but not
limited to OpenSSL) that is licensed under separate terms, as designated in a
particular file or component or in included license documentation. The authors
of MySQL hereby grant you an additional permission to link the program and
your derivative works with the separately licensed software that they have
included with MySQL.
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, version 2.0,
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 St, Fifth Floor, Boston, MA 02110-1301 USA
*****************************************************************************/
/** @file include/trx0purge.h
Purge old versions
Created 3/26/1996 Heikki Tuuri
*******************************************************/
#ifndef trx0purge_h
#define trx0purge_h
#include "fil0fil.h"
#include "mtr0mtr.h"
#include "page0page.h"
#include "que0types.h"
#include "read0types.h"
#include "trx0sys.h"
#include "trx0types.h"
#include "univ.i"
#include "usr0sess.h"
#ifdef UNIV_HOTBACKUP
#include "trx0sys.h"
#endif /* UNIV_HOTBACKUP */
#include "lizard0purge.h"
#include "lizard0read0types.h"
#include "lizard0scn.h"
#include "lizard0undo0types.h"
/** The global data structure coordinating a purge */
extern trx_purge_t *purge_sys;
/** Calculates the file address of an undo log header when we have the file
address of its history list node.
@return file address of the log */
UNIV_INLINE
fil_addr_t trx_purge_get_log_from_hist(
fil_addr_t node_addr); /*!< in: file address of the history
list node of the log */
/** Creates the global purge system control structure and inits the history
mutex.
@param[in] n_purge_threads number of purge threads
@param[in,out] purge_queue UNDO log min binary heap */
void trx_purge_sys_create(ulint n_purge_threads,
lizard::purge_heap_t *purge_heap);
/** Frees the global purge system control structure. */
void trx_purge_sys_close(void);
/** Get current purged GCN number */
gcn_t lizard_sys_get_purged_gcn();
/************************************************************************
Adds the update undo log as the first log in the history list. Removes the
update undo log segment from the rseg slot if it is too big for reuse. */
void trx_purge_add_update_undo_to_history(
trx_t *trx, /*!< in: transaction */
trx_undo_ptr_t *undo_ptr, /*!< in: update undo log. */
page_t *undo_page, /*!< in: update undo log header page,
x-latched */
bool update_rseg_history_len,
/*!< in: if true: update rseg history
len else skip updating it. */
ulint n_added_logs, /*!< in: number of logs added */
mtr_t *mtr); /*!< in: mtr */
/** This function runs a purge batch.
@return number of undo log pages handled in the batch */
ulint trx_purge(ulint n_purge_threads, /*!< in: number of purge tasks to
submit to task queue. */
ulint limit, /*!< in: the maximum number of
records to purge in one batch */
bool truncate, /*!< in: truncate history if true */
bool *blocked = NULL); /*!< out: is blocked by retention */
/** Stop purge and wait for it to stop, move to PURGE_STATE_STOP. */
void trx_purge_stop(void);
/** Resume purge, move to PURGE_STATE_RUN. */
void trx_purge_run(void);
/** Purge states */
enum purge_state_t {
PURGE_STATE_INIT, /*!< Purge instance created */
PURGE_STATE_RUN, /*!< Purge should be running */
PURGE_STATE_STOP, /*!< Purge should be stopped */
PURGE_STATE_EXIT, /*!< Purge has been shutdown */
PURGE_STATE_DISABLED /*!< Purge was never started */
};
/** Get the purge state.
@return purge state. */
purge_state_t trx_purge_state(void);
namespace lizard {
struct TxnUndoRsegsIterator;
} // namespace lizard
/** This is the purge pointer/iterator. We need both the undo no and the
transaction no up to which purge has parsed and applied the records. */
struct purge_iter_t {
purge_iter_t() : scn(), undo_no(), undo_rseg_space(SPACE_UNKNOWN) {
// Do nothing
}
scn_t scn; /*!< Purge has advanced past all
transactions whose SCN number is less or equal
than this */
undo_no_t undo_no; /*!< Purge has advanced past all records
whose undo number is less than this */
space_id_t undo_rseg_space;
/*!< Last undo record resided in this
space id. */
trx_id_t modifier_trx_id;
/*!< the transaction that created the
undo log record. Modifier trx id.*/
};
/* Namespace to hold all the related functions and variables needed
to truncate an undo tablespace. */
namespace undo {
/** Magic Number to indicate truncate action is complete. */
const ib_uint32_t s_magic = 76845412;
/** Truncate Log file Prefix. */
const char *const s_log_prefix = "undo_";
/** Truncate Log file Extension. */
const char *const s_log_ext = "trunc.log";
/** The currently used undo space IDs for an undo space number
along with a boolean showing whether the undo space number is in use. */
struct space_id_account {
space_id_t space_id;
bool in_use;
};
/** List of currently used undo space IDs for each undo space number
along with a boolean showing whether the undo space number is in use. */
extern struct space_id_account *space_id_bank;
/** Check if the space_id is an undo space ID in the reserved range.
@param[in] space_id undo tablespace ID
@return true if it is in the reserved undo space ID range. */
inline bool is_reserved(space_id_t space_id) {
return (space_id >= dict_sys_t::s_min_undo_space_id &&
space_id <= dict_sys_t::s_max_undo_space_id);
}
/** Convert an undo space number (from 1 to 127) into the undo space_id,
given an index indicating which space_id from the pool assigned to that
undo number.
@param[in] space_num undo tablespace number
@param[in] ndx index of the space_id within that undo number
@return space_id of the undo tablespace */
inline space_id_t num2id(space_id_t space_num, size_t ndx) {
ut_ad(space_num > 0);
ut_ad(space_num <= FSP_MAX_UNDO_TABLESPACES);
ut_ad(ndx < dict_sys_t::undo_space_id_range);
space_id_t space_id = dict_sys_t::s_max_undo_space_id + 1 - space_num -
static_cast<space_id_t>(ndx * FSP_MAX_UNDO_TABLESPACES);
return (space_id);
}
/** Convert an undo space number (from 1 to 127) into an undo space_id.
Use the undo::space_id_bank to return the curent space_id assigned to
that undo number.
@param[in] space_num undo tablespace number
@return space_id of the undo tablespace */
inline space_id_t num2id(space_id_t space_num) {
ut_ad(space_num > 0);
ut_ad(space_num <= FSP_MAX_UNDO_TABLESPACES);
size_t slot = space_num - 1;
/* The space_id_back is normally protected by undo::spaces::m_latch.
But this can only be called on a specific slot when truncation is not
happening on that slot, i.e. the undo tablespace is in use. */
ut_ad(undo::space_id_bank[slot].in_use);
return (undo::space_id_bank[slot].space_id);
}
/* clang-format off */
/** Convert an undo space ID into an undo space number.
NOTE: This may be an undo space_id from a pre-exisiting 5.7
database which used space_ids from 1 to 127. If so, the
space_id is the space_num.
The space_ids are assigned to number ranges in reverse from high to low.
In addition, the first space IDs for each undo number occur sequentionally
and descending before the second space_id.
Since s_max_undo_space_id = 0xFFFFFFEF, FSP_MAX_UNDO_TABLESPACES = 127
and undo_space_id_range = 512:
Space ID Space Num Space ID Space Num ... Space ID Space Num
0xFFFFFFEF 1 0xFFFFFFEe 2 ... 0xFFFFFF71 127
0xFFFFFF70 1 0xFFFFFF6F 2 ... 0xFFFFFEF2 127
0xFFFFFEF1 1 0xFFFFFEF0 2 ... 0xFFFFFE73 127
...
This is done to maintain backward compatibility to when there was only one
space_id per undo space number.
@param[in] space_id undo tablespace ID
@return space number of the undo tablespace */
/* clang-format on */
inline space_id_t id2num(space_id_t space_id) {
if (!is_reserved(space_id)) {
return (space_id);
}
return (((dict_sys_t::s_max_undo_space_id - space_id) %
FSP_MAX_UNDO_TABLESPACES) +
1);
}
/* Given a reserved undo space_id, return the next space_id for the associated
undo space number. */
inline space_id_t id2next_id(space_id_t space_id) {
ut_ad(is_reserved(space_id));
space_id_t space_num = id2num(space_id);
space_id_t first_id = dict_sys_t::s_max_undo_space_id + 1 - space_num;
space_id_t last_id = first_id - (FSP_MAX_UNDO_TABLESPACES *
(dict_sys_t::undo_space_id_range - 1));
return (space_id == SPACE_UNKNOWN || space_id == last_id
? first_id
: space_id - FSP_MAX_UNDO_TABLESPACES);
}
/** Initialize the undo tablespace space_id bank which is a lock free
repository for information about the space IDs used for undo tablespaces.
It is used during creation in order to assign an unused space number and
during truncation in order to assign the next space_id within that
space_number range. */
void init_space_id_bank();
/** Note that the undo space number for a space ID is being used.
Put that space_id into the space_id_bank.
@param[in] space_id undo tablespace number */
void use_space_id(space_id_t space_id);
/** Mark that the given undo space number is being used and
return the next available space_id for that space number.
@param[in] space_num undo tablespace number
@return the next tablespace ID to use */
space_id_t use_next_space_id(space_id_t space_num);
/** Mark an undo number associated with a given space_id as unused and
available to be resused. This happens when the fil_space_t is closed
associated with a drop undo tablespace.
@param[in] space_id Undo Tablespace ID */
void unuse_space_id(space_id_t space_id);
/** Given a valid undo space_id or SPACE_UNKNOWN, return the next space_id
for the given space number.
@param[in] space_id undo tablespace ID
@param[in] space_num undo tablespace number
@return the next tablespace ID to use */
space_id_t next_space_id(space_id_t space_id, space_id_t space_num);
/** Given a valid undo space_id, return the next space_id for that
space number.
@param[in] space_id undo tablespace ID
@return the next tablespace ID to use */
space_id_t next_space_id(space_id_t space_id);
/** Return the next available undo space ID to be used for a new explicit
undo tablespaces.
@retval if success, next available undo space number.
@retval if failure, SPACE_UNKNOWN */
space_id_t get_next_available_space_num();
/** Build a standard undo tablespace name from a space_id.
@param[in] space_id id of the undo tablespace.
@return tablespace name of the undo tablespace file */
char *make_space_name(space_id_t space_id);
/** Build a standard undo tablespace file name from a space_id.
@param[in] space_id id of the undo tablespace.
@return file_name of the undo tablespace file */
char *make_file_name(space_id_t space_id);
/** An undo::Tablespace object is used to easily convert between
undo_space_id and undo_space_num and to create the automatic file_name
and space name. In addition, it is used in undo::Tablespaces to track
the trx_rseg_t objects in an Rsegs vector. So we do not allocate the
Rsegs vector for each object, only when requested by the constructor. */
struct Tablespace {
/** Constructor
@param[in] id tablespace id */
explicit Tablespace(space_id_t id)
: m_id(id),
m_num(undo::id2num(id)),
m_implicit(true),
m_new(false),
m_space_name(),
m_file_name(),
m_log_file_name(),
m_rsegs(),
m_txn(false) {}
/** Copy Constructor
@param[in] other undo tablespace to copy */
Tablespace(Tablespace &other)
: m_id(other.id()),
m_num(undo::id2num(other.id())),
m_implicit(other.is_implicit()),
m_new(other.is_new()),
m_space_name(),
m_file_name(),
m_log_file_name(),
m_rsegs(),
m_txn(other.is_txn()) {
ut_ad(m_id == 0 || is_reserved(m_id));
set_space_name(other.space_name());
set_file_name(other.file_name());
/* When the copy constructor is used, add an Rsegs
vector. This constructor is only used in the global
undo::Tablespaces object where rollback segments are
tracked. */
m_rsegs = UT_NEW_NOKEY(Rsegs());
}
/** Destructor */
~Tablespace() {
if (m_space_name != nullptr) {
ut_free(m_space_name);
m_space_name = nullptr;
}
if (m_file_name != nullptr) {
ut_free(m_file_name);
m_file_name = nullptr;
}
if (m_log_file_name != nullptr) {
ut_free(m_log_file_name);
m_log_file_name = nullptr;
}
/* Clear the cached rollback segments. */
if (m_rsegs != nullptr) {
UT_DELETE(m_rsegs);
m_rsegs = nullptr;
}
}
/* Determine if this undo space needs to be truncated.
@return true if it should be truncated, false if not. */
bool needs_truncation() {
/* If it is already inactive, even implicitly, then proceed. */
if (m_rsegs->is_inactive_implicit() || m_rsegs->is_inactive_explicit()) {
return (true);
}
/* If implicit undo truncation is turned off, or if the rsegs don't exist
yet, don't bother checking the size. */
if (!srv_undo_log_truncate || m_rsegs == nullptr || m_rsegs->is_empty() ||
m_rsegs->is_init()) {
return (false);
}
ut_ad(m_rsegs->is_active());
page_no_t trunc_size = ut_max(
static_cast<page_no_t>(srv_max_undo_tablespace_size / srv_page_size),
static_cast<page_no_t>(SRV_UNDO_TABLESPACE_SIZE_IN_PAGES));
if (fil_space_get_size(id()) > trunc_size) {
return (true);
}
return (false);
}
/** Change the space_id from its current value.
@param[in] space_id The new undo tablespace ID */
void set_space_id(space_id_t space_id);
/** Replace the standard undo space name if it exists with a copy
of the undo tablespace name provided.
@param[in] new_space_name non-standard undo space name */
void set_space_name(const char *new_space_name);
/** Get the undo tablespace name. Make it if not yet made.
NOTE: This is only called from stack objects so there is no
race condition. If it is ever called from a shared object
like undo::spaces, then it must be protected by the caller.
@return tablespace name created from the space_id */
char *space_name() {
if (m_space_name == nullptr) {
#ifndef UNIV_HOTBACKUP
m_space_name = make_space_name(m_id);
#endif /* !UNIV_HOTBACKUP */
}
return (m_space_name);
}
/** Replace the standard undo file name if it exists with a copy
of the file name provided. This name can come in three forms:
absolute path, relative path, and basename. Undo ADD DATAFILE
does not accept a relative path. So if that comes in here, it
was the scaneed name and is relative to the datadir.
If this is just a basename, add it to srv_undo_dir.
@param[in] file_name explicit undo file name */
void set_file_name(const char *file_name);
/** Get the undo space filename. Make it if not yet made.
NOTE: This is only called from stack objects so there is no
race condition. If it is ever called from a shared object
like undo::spaces, then it must be protected by the caller.
@return tablespace filename created from the space_id */
char *file_name() {
if (m_file_name == nullptr) {
m_file_name = make_file_name(m_id);
}
return (m_file_name);
}
/** Build a log file name based on space_id
@param[in] space_id id of the undo tablespace.
@return DB_SUCCESS or error code */
char *make_log_file_name(space_id_t space_id);
/** Get the undo log filename. Make it if not yet made.
NOTE: This is only called from stack objects so there is no
race condition. If it is ever called from a shared object
like undo::spaces, then it must be protected by the caller.
@return tablespace filename created from the space_id */
char *log_file_name() {
if (m_log_file_name == nullptr) {
m_log_file_name = make_log_file_name(m_id);
}
return (m_log_file_name);
}
/** Get the undo tablespace ID.
@return tablespace ID */
space_id_t id() { return (m_id); }
/** Get the undo tablespace number. This is the same as m_id
if m_id is 0 or this is a v5.6-5.7 undo tablespace. v8+ undo
tablespaces use a space_id from the reserved range.
@return undo tablespace number */
space_id_t num() {
ut_ad(m_num < FSP_MAX_ROLLBACK_SEGMENTS);
return (m_num);
}
/** Get a reference to the List of rollback segments within
this undo tablespace.
@return a reference to the Rsegs vector. */
Rsegs *rsegs() { return (m_rsegs); }
/** Report whether this undo tablespace was explicitly created
by an SQL statement.
@return true if the tablespace was created explicitly. */
bool is_explicit() { return (!m_implicit); }
/** Report whether this undo tablespace was implicitly created.
@return true if the tablespace was created implicitly. */
bool is_implicit() { return (m_implicit); }
/** Report whether this undo tablespace was created at startup.
@retval true if created at startup.
@retval false if pre-existed at startup. */
bool is_new() { return (m_new); }
/** Note that this undo tablespace is being created. */
void set_new() { m_new = true; }
/** Return whether the undo tablespace is active.
@return true if active */
bool is_active() {
if (m_rsegs == nullptr) {
return (false);
}
m_rsegs->s_lock();
bool ret = m_rsegs->is_active();
m_rsegs->s_unlock();
return (ret);
}
/** Return whether the undo tablespace is active. For optimization purposes,
do not take a latch.
@return true if active */
bool is_active_no_latch() {
if (m_rsegs == nullptr) {
return (false);
}
return (m_rsegs->is_active());
}
/** Return the rseg at the requested rseg slot if the undo space is active.
@param[in] slot The slot of the rseg. 1 to 127
@return Rseg pointer of nullptr if the space is not active. */
trx_rseg_t *get_active(ulint slot) {
m_rsegs->s_lock();
if (!m_rsegs->is_active()) {
m_rsegs->s_unlock();
return (nullptr);
}
/* Mark the chosen rseg so that it will not be selected
for UNDO truncation. */
trx_rseg_t *rseg = m_rsegs->at(slot);
rseg->trx_ref_count++;
m_rsegs->s_unlock();
return (rseg);
}
/** Return whether the undo tablespace is inactive due to
implicit selection by the purge thread.
@return true if marked for truncation by the purge thread */
bool is_inactive_implicit() {
if (m_rsegs == nullptr) {
return (false);
}
m_rsegs->s_lock();
bool ret = m_rsegs->is_inactive_implicit();
m_rsegs->s_unlock();
return (ret);
}
/** Return whether the undo tablespace was made inactive by
ALTER TABLESPACE.
@return true if altered inactive */
bool is_inactive_explicit() {
if (m_rsegs == nullptr) {
return (false);
}
m_rsegs->s_lock();
bool ret = m_rsegs->is_inactive_explicit();
m_rsegs->s_unlock();
return (ret);
}
/** Return whether the undo tablespace is empty and ready
to be dropped.
@return true if empty */
bool is_empty() {
if (m_rsegs == nullptr) {
return (true);
}
m_rsegs->s_lock();
bool ret = m_rsegs->is_empty();
m_rsegs->s_unlock();
return (ret);
}
/** Set the undo tablespace active for use by transactions. */
void set_active() {
m_rsegs->x_lock();
m_rsegs->set_active();
m_rsegs->x_unlock();
}
/** Set the state of the rollback segments in this undo tablespace to
inactive_implicit if currently active. If the state is inactive_explicit,
leave as is. Then put the space_id into the callers marked_space_id.
This is done when marking a space for truncate. It will not be used
for new transactions until it becomes active again. */
void set_inactive_implicit(space_id_t *marked_space_id) {
m_rsegs->x_lock();
if (m_rsegs->is_active()) {
m_rsegs->set_inactive_implicit();
}
*marked_space_id = m_id;
m_rsegs->x_unlock();
}
/** Make the undo tablespace inactive so that it will not be
used for new transactions. The purge thread will clear out
all the undo logs, truncate it, and then mark it empty. */
void set_inactive_explicit() {
m_rsegs->x_lock();
if (m_rsegs->is_active() || m_rsegs->is_inactive_implicit()) {
m_rsegs->set_inactive_explicit();
}
m_rsegs->x_unlock();
}
/** Make the undo tablespace active again so that it will
be used for new transactions.
If current State is ___ then do:
empty: Set active.
active_implicit: Ignore. It was not altered inactive. When it is done
being truncated it will go back to active.
active_explicit: Depends if it is marked for truncation.
marked: Set to inactive_implicit. the next state will be active.
not yet: Set to active so that it does not get truncated. */
void alter_active();
/** Set the state of the undo tablespace to empty so that it
can be dropped. */
void set_empty() {
m_rsegs->x_lock();
m_rsegs->set_empty();
m_rsegs->x_unlock();
}
private:
/** Undo Tablespace ID. */
space_id_t m_id;
/** Undo Tablespace number, from 1 to 127. This is the
7-bit number that is used in a rollback pointer.
Use id2num() to get this number from a space_id. */
space_id_t m_num;
/** True if this is an implicit undo tablespace */
bool m_implicit;
/** True if this undo tablespace was implicitly created when
this instance started up. False if it pre-existed. */
bool m_new;
/** The tablespace name, auto-generated when needed from
the space number. */
char *m_space_name;
/** The tablespace file name, auto-generated when needed
from the space number. */
char *m_file_name;
/** The tablespace log file name, auto-generated when needed
from the space number. */
char *m_log_file_name;
/** List of rollback segments within this tablespace.
This is not always used. Must call init_rsegs to use it. */
Rsegs *m_rsegs;
/** Lizard transaction tablespace */
bool m_txn;
public:
bool is_txn() const { return m_txn; }
void set_txn() { m_txn = true; }
};
/** List of undo tablespaces, each containing a list of
rollback segments. */
class Tablespaces {
using Tablespaces_Vector =
std::vector<Tablespace *, ut_allocator<Tablespace *>>;
public:
Tablespaces() { init(); }
~Tablespaces() { deinit(); }
/** Initialize */
void init();
/** De-initialize */
void deinit();
/** Clear the contents of the list of Tablespace objects.
This does not deallocate any memory. */
void clear() {
for (auto undo_space : m_spaces) {
UT_DELETE(undo_space);
}
m_spaces.clear();
}
/** Get the number of tablespaces tracked by this object. */
ulint size() { return (m_spaces.size()); }
/** See if the list of tablespaces is empty. */
bool empty() { return (m_spaces.empty()); }
/** Get the Tablespace tracked at a position. */
Tablespace *at(size_t pos) { return (m_spaces.at(pos)); }
/** Get the Tablespace at back. */
Tablespace *back() { return m_spaces.back(); }
/** Add a new undo::Tablespace to the back of the vector.
The vector has been pre-allocated to 128 so read threads will
not loose what is pointed to. If tablespace_name and file_name
are standard names, they are optional.
@param[in] ref_undo_space undo tablespace */
void add(Tablespace &ref_undo_space, int pos);
/** Drop an existing explicit undo::Tablespace.
@param[in] undo_space pointer to undo space */
void drop(Tablespace *undo_space);
/** Drop an existing explicit undo::Tablespace.
@param[in] ref_undo_space reference to undo space */
void drop(Tablespace &ref_undo_space);
/** Check if the given space_id is in the vector.
@param[in] num undo tablespace number
@return true if space_id is found, else false */
bool contains(space_id_t num) { return (find(num) != nullptr); }
/** Lizard : mark transaction tablespace */
void mark_txn();
/** Find the given space_num in the vector.
@param[in] num undo tablespace number
@return pointer to an undo::Tablespace struct */
Tablespace *find(space_id_t num) {
if (m_spaces.empty()) {
return (nullptr);
}
/* The sort method above puts this vector in order by
Tablespace::num. If there are no gaps, then we should
be able to find it quickly. */
space_id_t slot = num - 1;
if (slot < m_spaces.size()) {
auto undo_space = m_spaces.at(slot);
if (undo_space->num() == num) {
return (undo_space);
}
}
/* If there are gaps in the numbering, do a search. */
for (auto undo_space : m_spaces) {
if (undo_space->num() == num) {
return (undo_space);
}
}
return (nullptr);
}
#ifdef UNIV_DEBUG
/** Determine if this thread owns a lock on m_latch. */
bool own_latch() {
return (rw_lock_own(m_latch, RW_LOCK_X) || rw_lock_own(m_latch, RW_LOCK_S));
}
#endif /* UNIV_DEBUG */
/** Get a shared lock on m_spaces. */
void s_lock() { rw_lock_s_lock(m_latch); }
/** Release a shared lock on m_spaces. */
void s_unlock() { rw_lock_s_unlock(m_latch); }
/** Get an exclusive lock on m_spaces. */
void x_lock() { rw_lock_x_lock(m_latch); }
/** Release an exclusive lock on m_spaces. */
void x_unlock() { rw_lock_x_unlock(m_latch); }
Tablespaces_Vector m_spaces;
private:
/** RW lock to protect m_spaces.
x for adding elements, s for scanning, size() etc. */
rw_lock_t *m_latch;
};
/** Mutext for serializing undo tablespace related DDL. These have to do with
creating and dropping undo tablespaces. */
extern ib_mutex_t ddl_mutex;
/** A global object that contains a vector of undo::Tablespace structs. */
extern Tablespaces *spaces;
/** Create the truncate log file. Needed to track the state of truncate during
a crash. An auxiliary redo log file undo_<space_id>_trunc.log will be created
while the truncate of the UNDO is in progress. This file is required during
recovery to complete the truncate.
@param[in] undo_space undo tablespace to truncate.
@return DB_SUCCESS or error code.*/
dberr_t start_logging(Tablespace *undo_space);
/** 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 successful
and ref-count = 0.
@param[in] space_num number of the undo tablespace to truncate. */
void done_logging(space_id_t space_num);
/** Check if TRUNCATE_DDL_LOG file exist.
@param[in] space_num undo tablespace number
@return true if exist else false. */
bool is_active_truncate_log_present(space_id_t space_num);
/** list of undo tablespaces that need header pages and rollback
segments written to them at startup. This can be because they are
newly initialized, were being truncated and the system crashed, or
they were an old format at startup and were replaced when they were
opened. Old format undo tablespaces do not have space_ids between
dict_sys_t::s_min_undo_space_id and dict_sys_t::s_max_undo_space_id
and they do not contain an RSEG_ARRAY page. */
extern Space_Ids s_under_construction;
/** Add undo tablespace to s_under_construction vector.
@param[in] space_id space id of tablespace to
truncate */
void add_space_to_construction_list(space_id_t space_id);
/** Clear the s_under_construction vector. */
void clear_construction_list();
/** Is an undo tablespace under constuction at the moment.
@param[in] space_id space id to check
@return true if marked for truncate, else false. */
bool is_under_construction(space_id_t space_id);
/** Set an undo tablespace active. */
void set_active(space_id_t space_id);
/* Return whether the undo tablespace is active. If this is a
non-undo tablespace, then it will not be found in spaces and it
will not be under construction, so this function will return true.
@param[in] space_id Undo Tablespace ID
@param[in] get_latch Specifies whether the rsegs->s_lock() is needed.
@return true if active (non-undo spaces are always active) */
bool is_active(space_id_t space_id, bool get_latch = true);
/** Track an UNDO tablespace marked for truncate. */
class Truncate {
public:
Truncate()
: m_space_id_marked(SPACE_UNKNOWN),
m_purge_rseg_truncate_frequency(
static_cast<ulint>(srv_purge_rseg_truncate_frequency)) {
/* Do Nothing. */
}
/** Is tablespace selected for truncate.
@return true if undo tablespace is marked for truncate */
bool is_marked() const { return (m_space_id_marked != SPACE_UNKNOWN); }
/** Mark the undo tablespace selected for truncate as empty
so that it will be truncated next. */
void set_marked_space_empty() { m_marked_space_is_empty = true; }
/** Is tablespace selected for truncate empty of undo logs yet?
@return true if the marked undo tablespace has no more undo logs */
bool is_marked_space_empty() const { return (m_marked_space_is_empty); }
/** Mark the tablespace for truncate.
@param[in] undo_space undo tablespace to truncate. */
void mark(Tablespace *undo_space) {
/* Set the internal state of this undo space to inactive_implicit
so that its rsegs will not be allocated to any new transaction.
If the space is already in the inactive_explicit state, it will
stay there.
Note that the DD is not modified since in case of crash, the
action must be completed before the DD is available.
Set both the state and this marked id while this routine has
an x_lock on m_rsegs because a concurrent user thread might issue
undo_space->alter_active(). */
undo_space->set_inactive_implicit(&m_space_id_marked);
m_marked_space_is_empty = false;
/* We found an UNDO-tablespace to truncate so set the
local purge rseg truncate frequency to 3. This will help
accelerate the purge action and in turn truncate. */
set_rseg_truncate_frequency(3);
}
/** Get the ID of the tablespace marked for truncate.
@return tablespace ID marked for truncate. */
space_id_t get_marked_space_num() const {
return (id2num(m_space_id_marked));
}
/** Reset for next rseg truncate. */
void reset() {
/* Sync with global value as we are done with
truncate now. */
set_rseg_truncate_frequency(
static_cast<ulint>(srv_purge_rseg_truncate_frequency));
m_marked_space_is_empty = false;
m_space_id_marked = SPACE_UNKNOWN;
}
/** Get the undo tablespace number to start a scan.
Re-adjust in case the spaces::size() went down.
@return UNDO space_num to start scanning. */
space_id_t get_scan_space_num() const {
s_scan_pos = s_scan_pos % undo::spaces->size();
Tablespace *undo_space = undo::spaces->at(s_scan_pos);
return (undo_space->num());
}
/** Increment the scanning position in a round-robin fashion.
@return UNDO space_num at incremented scanning position. */
space_id_t increment_scan() const {
/** Round-robin way of selecting an undo tablespace
for the truncate operation. Once we reach the end of
the list of known undo tablespace IDs, move back to
the first undo tablespace ID. This will scan active
as well as inactive undo tablespaces. */
s_scan_pos = (s_scan_pos + 1) % undo::spaces->size();
return (get_scan_space_num());
}
/** Get local rseg purge truncate frequency
@return rseg purge truncate frequency. */
ulint get_rseg_truncate_frequency() const {
return (m_purge_rseg_truncate_frequency);
}
/** Set local rseg purge truncate frequency */
void set_rseg_truncate_frequency(ulint frequency) {
m_purge_rseg_truncate_frequency = frequency;
}
private:
/** UNDO space ID that is marked for truncate. */
space_id_t m_space_id_marked;
/** This is true if the marked space is empty of undo logs
and ready to truncate. We leave the rsegs object 'inactive'
until after it is truncated and rebuilt. This allow the
code to do the check for undo logs only once. */
bool m_marked_space_is_empty;
/** Rollback segment(s) purge frequency. This is a local
value maintained along with the global value. It is set
to the global value in the before each truncate. But when
a tablespace is marked for truncate it is updated to 1 and
then minimum value among 2 is used by the purge action. */
ulint m_purge_rseg_truncate_frequency;
/** Start scanning for UNDO tablespace from this
vector position. This is to avoid bias selection
of one tablespace always. */
static size_t s_scan_pos;
}; /* class Truncate */
} /* namespace undo */
/** The control structure used in the purge operation */
struct trx_purge_t {
sess_t *sess; /*!< System session running the purge
query */
trx_t *trx; /*!< System transaction running the
purge query: this trx is not in the
trx list of the trx system and it
never ends */
#ifndef UNIV_HOTBACKUP
rw_lock_t latch; /*!< The latch protecting the purge
view. A purge operation must acquire an
x-latch here for the instant at which
it changes the purge view: an undo
log operation can prevent this by
obtaining an s-latch here. It also
protects state and running */
#endif /* !UNIV_HOTBACKUP */
os_event_t event; /*!< State signal event */
ulint n_stop; /*!< Counter to track number stops */
volatile bool running; /*!< true, if purge is active,
we check this without the latch too */
volatile purge_state_t state; /*!< Purge coordinator thread states,
we check this in several places
without holding the latch. */
que_t *query; /*!< The query graph which will do the
parallelized purge operation */
lizard::Vision vision; /*!< The purge will not remove undo logs
which are > this vision(purge vision) */
bool view_active; /*!< true if view is active */
volatile ulint n_submitted; /*!< Count of total tasks submitted
to the task queue */
volatile ulint n_completed; /*!< Count of total tasks completed */
/*------------------------------*/
/* The following two fields form the 'purge pointer' which advances
during a purge, and which is used in history list truncation */
purge_iter_t iter; /* Limit up to which we have read and
parsed the UNDO log records. Not
necessarily purged from the indexes.
Note that this can never be less than
the limit below, we check for this
invariant in trx0purge.cc */
purge_iter_t limit; /* The 'purge pointer' which advances
during a purge, and which is used in
history list truncation */
#ifdef UNIV_DEBUG
purge_iter_t done; /* Indicate 'purge pointer' which have
purged already accurately. */
#endif /* UNIV_DEBUG */
/*-----------------------------*/
ibool next_stored; /*!< TRUE if the info of the next record
to purge is stored below: if yes, then
the transaction number and the undo
number of the record are stored in
purge_trx_no and purge_undo_no above */
trx_rseg_t *rseg; /*!< Rollback segment for the next undo
record to purge */
page_no_t page_no; /*!< Page number for the next undo
record to purge, page number of the
log header, if dummy record */
ulint offset; /*!< Page offset for the next undo
record to purge, 0 if the dummy
record */
page_no_t hdr_page_no; /*!< Header page of the undo log where
the next record to purge belongs */
ulint hdr_offset; /*!< Header byte offset on the page */
lizard::TxnUndoRsegsIterator *rseg_iter; /*!< Iterator to get the next rseg
to process */
lizard::purge_heap_t *purge_heap; /*!< Binary min-heap, ordered on
TxnUndoRsegs::scn. It is protected
by the pq_mutex */
PQMutex pq_mutex; /*!< Mutex protecting purge_queue */
undo::Truncate undo_trunc; /*!< Track UNDO tablespace marked
for truncate. */
mem_heap_t *heap; /*!< Heap for reading the undo log
records */
/** All transactions whose scn <= purged_scn must have been purged.
Only the purge sys coordinator thread and recover thread can modify it. */
std::atomic<scn_t> purged_scn;
utc_t top_undo_utc;
/** Similar with purged_scn */
Purged_gcn purged_gcn;
};
#include "trx0purge.ic"
#endif /* trx0purge_h */