mariadb/storage/xtradb/include/trx0sys.ic
Jan Lindström df4dd593f2 MDEV-6247: Merge 10.0-galera to 10.1.
Merged lp:maria/maria-10.0-galera up to revision 3879.

Added a new functions to handler API to forcefully abort_transaction,
producing fake_trx_id, get_checkpoint and set_checkpoint for XA. These
were added for future possiblity to add more storage engines that
could use galera replication.
2014-08-26 15:43:46 +03:00

568 lines
15 KiB
Text

/*****************************************************************************
Copyright (c) 1996, 2011, 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 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 include/trx0sys.ic
Transaction system
Created 3/26/1996 Heikki Tuuri
*******************************************************/
#include "trx0trx.h"
#include "data0type.h"
#ifndef UNIV_HOTBACKUP
# include "srv0srv.h"
# include "mtr0log.h"
/* The typedef for rseg slot in the file copy */
typedef byte trx_sysf_rseg_t;
/* Rollback segment specification slot offsets */
/*-------------------------------------------------------------*/
#define TRX_SYS_RSEG_SPACE 0 /* space where the segment
header is placed; starting with
MySQL/InnoDB 5.1.7, this is
UNIV_UNDEFINED if the slot is unused */
#define TRX_SYS_RSEG_PAGE_NO 4 /* page number where the segment
header is placed; this is FIL_NULL
if the slot is unused */
/*-------------------------------------------------------------*/
/* Size of a rollback segment specification slot */
#define TRX_SYS_RSEG_SLOT_SIZE 8
/*****************************************************************//**
Writes the value of max_trx_id to the file based trx system header. */
UNIV_INTERN
void
trx_sys_flush_max_trx_id(void);
/*==========================*/
/***************************************************************//**
Checks if a page address is the trx sys header page.
@return TRUE if trx sys header page */
UNIV_INLINE
ibool
trx_sys_hdr_page(
/*=============*/
ulint space, /*!< in: space */
ulint page_no)/*!< in: page number */
{
if ((space == TRX_SYS_SPACE) && (page_no == TRX_SYS_PAGE_NO)) {
return(TRUE);
}
return(FALSE);
}
/***************************************************************//**
Gets the pointer in the nth slot of the rseg array.
@return pointer to rseg object, NULL if slot not in use */
UNIV_INLINE
trx_rseg_t*
trx_sys_get_nth_rseg(
/*=================*/
trx_sys_t* sys, /*!< in: trx system */
ulint n) /*!< in: index of slot */
{
ut_ad(n < TRX_SYS_N_RSEGS);
return(sys->rseg_array[n]);
}
/**********************************************************************//**
Gets a pointer to the transaction system header and x-latches its page.
@return pointer to system header, page x-latched. */
UNIV_INLINE
trx_sysf_t*
trx_sysf_get(
/*=========*/
mtr_t* mtr) /*!< in: mtr */
{
buf_block_t* block;
trx_sysf_t* header;
ut_ad(mtr);
block = buf_page_get(TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO,
RW_X_LATCH, mtr);
buf_block_dbg_add_level(block, SYNC_TRX_SYS_HEADER);
header = TRX_SYS + buf_block_get_frame(block);
return(header);
}
/*****************************************************************//**
Gets the space of the nth rollback segment slot in the trx system
file copy.
@return space id */
UNIV_INLINE
ulint
trx_sysf_rseg_get_space(
/*====================*/
trx_sysf_t* sys_header, /*!< in: trx sys header */
ulint i, /*!< in: slot index == rseg id */
mtr_t* mtr) /*!< in: mtr */
{
ut_ad(sys_header);
ut_ad(i < TRX_SYS_N_RSEGS);
return(mtr_read_ulint(sys_header + TRX_SYS_RSEGS
+ i * TRX_SYS_RSEG_SLOT_SIZE
+ TRX_SYS_RSEG_SPACE, MLOG_4BYTES, mtr));
}
/*****************************************************************//**
Gets the page number of the nth rollback segment slot in the trx system
header.
@return page number, FIL_NULL if slot unused */
UNIV_INLINE
ulint
trx_sysf_rseg_get_page_no(
/*======================*/
trx_sysf_t* sys_header, /*!< in: trx system header */
ulint i, /*!< in: slot index == rseg id */
mtr_t* mtr) /*!< in: mtr */
{
ut_ad(sys_header);
ut_ad(i < TRX_SYS_N_RSEGS);
return(mtr_read_ulint(sys_header + TRX_SYS_RSEGS
+ i * TRX_SYS_RSEG_SLOT_SIZE
+ TRX_SYS_RSEG_PAGE_NO, MLOG_4BYTES, mtr));
}
/*****************************************************************//**
Sets the space id of the nth rollback segment slot in the trx system
file copy. */
UNIV_INLINE
void
trx_sysf_rseg_set_space(
/*====================*/
trx_sysf_t* sys_header, /*!< in: trx sys file copy */
ulint i, /*!< in: slot index == rseg id */
ulint space, /*!< in: space id */
mtr_t* mtr) /*!< in: mtr */
{
ut_ad(sys_header);
ut_ad(i < TRX_SYS_N_RSEGS);
mlog_write_ulint(sys_header + TRX_SYS_RSEGS
+ i * TRX_SYS_RSEG_SLOT_SIZE
+ TRX_SYS_RSEG_SPACE,
space,
MLOG_4BYTES, mtr);
}
/*****************************************************************//**
Sets the page number of the nth rollback segment slot in the trx system
header. */
UNIV_INLINE
void
trx_sysf_rseg_set_page_no(
/*======================*/
trx_sysf_t* sys_header, /*!< in: trx sys header */
ulint i, /*!< in: slot index == rseg id */
ulint page_no, /*!< in: page number, FIL_NULL if the
slot is reset to unused */
mtr_t* mtr) /*!< in: mtr */
{
ut_ad(sys_header);
ut_ad(i < TRX_SYS_N_RSEGS);
mlog_write_ulint(sys_header + TRX_SYS_RSEGS
+ i * TRX_SYS_RSEG_SLOT_SIZE
+ TRX_SYS_RSEG_PAGE_NO,
page_no,
MLOG_4BYTES, mtr);
}
#endif /* !UNIV_HOTBACKUP */
/*****************************************************************//**
Writes a trx id to an index page. In case that the id size changes in
some future version, this function should be used instead of
mach_write_... */
UNIV_INLINE
void
trx_write_trx_id(
/*=============*/
byte* ptr, /*!< in: pointer to memory where written */
trx_id_t id) /*!< in: id */
{
#if DATA_TRX_ID_LEN != 6
# error "DATA_TRX_ID_LEN != 6"
#endif
mach_write_to_6(ptr, id);
}
#ifndef UNIV_HOTBACKUP
/*****************************************************************//**
Reads a trx id from an index page. In case that the id size changes in
some future version, this function should be used instead of
mach_read_...
@return id */
UNIV_INLINE
trx_id_t
trx_read_trx_id(
/*============*/
const byte* ptr) /*!< in: pointer to memory from where to read */
{
#if DATA_TRX_ID_LEN != 6
# error "DATA_TRX_ID_LEN != 6"
#endif
return(mach_read_from_6(ptr));
}
/****************************************************************//**
Looks for the trx handle with the given id in rw_trx_list.
The caller must be holding trx_sys->mutex.
@return the trx handle or NULL if not found;
the pointer must not be dereferenced unless lock_sys->mutex was
acquired before calling this function and is still being held */
UNIV_INLINE
trx_t*
trx_get_rw_trx_by_id(
/*=================*/
trx_id_t trx_id) /*!< in: trx id to search for */
{
trx_t* trx;
ulint len;
trx_t* first;
ut_ad(mutex_own(&trx_sys->mutex));
len = UT_LIST_GET_LEN(trx_sys->rw_trx_list);
if (len == 0) {
return(NULL);
}
/* Because the list is ordered on trx id in descending order,
we try to speed things up a bit. */
trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list);
assert_trx_in_rw_list(trx);
if (trx_id == trx->id) {
return(trx);
} else if (len == 1 || trx_id > trx->id) {
return(NULL);
}
first = trx;
trx = UT_LIST_GET_LAST(trx_sys->rw_trx_list);
assert_trx_in_rw_list(trx);
if (trx_id == trx->id) {
return(trx);
} else if (len == 2 || trx_id < trx->id) {
return(NULL);
}
/* Search the list from the lower end (tail). */
if (trx_id < (first->id + trx->id) >> 1) {
for (trx = UT_LIST_GET_PREV(trx_list, trx);
trx != NULL && trx_id > trx->id;
trx = UT_LIST_GET_PREV(trx_list, trx)) {
assert_trx_in_rw_list(trx);
}
} else {
for (trx = UT_LIST_GET_NEXT(trx_list, first);
trx != NULL && trx_id < trx->id;
trx = UT_LIST_GET_NEXT(trx_list, trx)) {
assert_trx_in_rw_list(trx);
}
}
return((trx != NULL && trx->id == trx_id) ? trx : NULL);
}
/****************************************************************//**
Returns the minimum trx id in trx list. This is the smallest id for which
the trx can possibly be active. (But, you must look at the trx->state
to find out if the minimum trx id transaction itself is active, or already
committed.). The caller must be holding the trx_sys_t::mutex in shared mode.
@return the minimum trx id, or trx_sys->max_trx_id if the trx list is empty */
UNIV_INLINE
trx_id_t
trx_rw_min_trx_id_low(void)
/*=======================*/
{
trx_id_t id;
const trx_t* trx;
ut_ad(mutex_own(&trx_sys->mutex));
trx = UT_LIST_GET_LAST(trx_sys->rw_trx_list);
if (trx == NULL) {
id = trx_sys->max_trx_id;
} else {
assert_trx_in_rw_list(trx);
id = trx->id;
}
return(id);
}
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
/***********************************************************//**
Assert that a transaction has been recovered.
@return TRUE */
UNIV_INLINE
ibool
trx_assert_recovered(
/*=================*/
trx_id_t trx_id) /*!< in: transaction identifier */
{
const trx_t* trx;
mutex_enter(&trx_sys->mutex);
trx = trx_get_rw_trx_by_id(trx_id);
ut_a(trx->is_recovered);
mutex_exit(&trx_sys->mutex);
return(TRUE);
}
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
/****************************************************************//**
Returns the minimum trx id in rw trx list. This is the smallest id for which
the rw trx can possibly be active. (But, you must look at the trx->state
to find out if the minimum trx id transaction itself is active, or already
committed.)
@return the minimum trx id, or trx_sys->max_trx_id if rw trx list is empty */
UNIV_INLINE
trx_id_t
trx_rw_min_trx_id(void)
/*===================*/
{
trx_id_t id;
mutex_enter(&trx_sys->mutex);
id = trx_rw_min_trx_id_low();
mutex_exit(&trx_sys->mutex);
return(id);
}
/****************************************************************//**
Returns pointer to a transaction instance if a rw transaction with the given id
is active. Caller must hold trx_sys->mutex. If the caller is not holding
lock_sys->mutex, the transaction may already have been committed.
@return transaction instance if active, or NULL;
the pointer must not be dereferenced unless lock_sys->mutex was
acquired before calling this function and is still being held */
UNIV_INLINE
trx_t*
trx_rw_get_active_trx_by_id(
/*========================*/
trx_id_t trx_id, /*!< in: trx id of the transaction */
ibool* corrupt) /*!< in: NULL or pointer to a flag
that will be set if corrupt */
{
trx_t* trx;
ut_ad(mutex_own(&trx_sys->mutex));
if (trx_id < trx_rw_min_trx_id_low()) {
trx = NULL;
} else if (trx_id >= trx_sys->max_trx_id) {
/* There must be corruption: we let the caller handle the
diagnostic prints in this case. */
trx = NULL;
if (corrupt != NULL) {
*corrupt = TRUE;
}
} else {
trx = trx_get_rw_trx_by_id(trx_id);
if (trx != NULL
&& trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)) {
trx = NULL;
}
}
return(trx);
}
/****************************************************************//**
Checks if a rw transaction with the given id is active. Caller must hold
trx_sys->mutex. If the caller is not holding lock_sys->mutex, the
transaction may already have been committed.
@return true if rw transaction it with a given id is active. */
UNIV_INLINE
bool
trx_rw_is_active_low(
/*=================*/
trx_id_t trx_id, /*!< in: trx id of the transaction */
ibool* corrupt) /*!< in: NULL or pointer to a flag
that will be set if corrupt */
{
ut_ad(mutex_own(&trx_sys->mutex));
if (UNIV_UNLIKELY(trx_id >= trx_sys->max_trx_id)) {
/* There must be corruption: we let the caller handle the
diagnostic prints in this case. */
if (corrupt != NULL) {
*corrupt = TRUE;
}
return(false);
}
return(trx_find_descriptor(trx_sys->descriptors, trx_sys->descr_n_used,
trx_id) != NULL);
}
/****************************************************************//**
Checks if a rw transaction with the given id is active. If the caller is
not holding lock_sys->mutex, the transaction may already have been
committed.
@return true if rw transaction it with a given id is active. */
UNIV_INLINE
bool
trx_rw_is_active(
/*=============*/
trx_id_t trx_id, /*!< in: trx id of the transaction */
ibool* corrupt) /*!< in: NULL or pointer to a flag
that will be set if corrupt */
{
bool res;
mutex_enter(&trx_sys->mutex);
res = trx_rw_is_active_low(trx_id, corrupt);
mutex_exit(&trx_sys->mutex);
return(res);
}
/*****************************************************************//**
Allocates a new transaction id.
@return new, allocated trx id */
UNIV_INLINE
trx_id_t
trx_sys_get_new_trx_id(void)
/*========================*/
{
#ifndef WITH_WSREP
/* wsrep_fake_trx_id violates this assert */
ut_ad(mutex_own(&trx_sys->mutex));
#endif /* WITH_WSREP */
/* VERY important: after the database is started, max_trx_id value is
divisible by TRX_SYS_TRX_ID_WRITE_MARGIN, and the following if
will evaluate to TRUE when this function is first time called,
and the value for trx id will be written to disk-based header!
Thus trx id values will not overlap when the database is
repeatedly started! */
if (!(trx_sys->max_trx_id % (trx_id_t) TRX_SYS_TRX_ID_WRITE_MARGIN)) {
trx_sys_flush_max_trx_id();
}
return(trx_sys->max_trx_id++);
}
/*****************************************************************//**
Determines the maximum transaction id.
@return maximum currently allocated trx id; will be stale after the
next call to trx_sys_get_new_trx_id() */
UNIV_INLINE
trx_id_t
trx_sys_get_max_trx_id(void)
/*========================*/
{
#if UNIV_WORD_SIZE < DATA_TRX_ID_LEN
trx_id_t max_trx_id;
#endif
ut_ad(!mutex_own(&trx_sys->mutex));
#if UNIV_WORD_SIZE < DATA_TRX_ID_LEN
/* Avoid torn reads. */
mutex_enter(&trx_sys->mutex);
max_trx_id = trx_sys->max_trx_id;
mutex_exit(&trx_sys->mutex);
return(max_trx_id);
#else
/* Perform a dirty read. Callers should be prepared for stale
values, and we know that the value fits in a machine word, so
that it will be read and written atomically. */
return(trx_sys->max_trx_id);
#endif
}
/*****************************************************************//**
Get the number of transaction in the system, independent of their state.
@return count of transactions in trx_sys_t::rw_trx_list */
UNIV_INLINE
ulint
trx_sys_get_n_rw_trx(void)
/*======================*/
{
ulint n_trx;
mutex_enter(&trx_sys->mutex);
n_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list);
mutex_exit(&trx_sys->mutex);
return(n_trx);
}
/*************************************************************//**
Find a slot for a given trx ID in a descriptors array.
@return: slot pointer */
UNIV_INLINE
trx_id_t*
trx_find_descriptor(
/*================*/
const trx_id_t* descriptors, /*!< in: descriptors array */
ulint n_descr, /*!< in: array size */
trx_id_t trx_id) /*!< in: trx id */
{
ut_ad(descriptors != trx_sys->descriptors ||
mutex_own(&trx_sys->mutex));
if (UNIV_UNLIKELY(n_descr == 0)) {
return(NULL);
}
return((trx_id_t *) bsearch(&trx_id, descriptors, n_descr,
sizeof(trx_id_t), trx_descr_cmp));
}
#endif /* !UNIV_HOTBACKUP */