Commit 124bae08 authored by Marko Mäkelä's avatar Marko Mäkelä

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.
parent 0b9a13a8
Subproject commit be34e12ba0f9a23074be5051259db954b3a302d9
Subproject commit cae391f7bf47f17fb246cded8ddf0ef19dbfbdec
......@@ -207,7 +207,7 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
FOUND /Expected to open 3 undo tablespaces but was able to find only 1 undo tablespaces/ in mysqld.1.err
FOUND /InnoDB: Unable to open undo tablespace.*undo002/ in mysqld.1.err
bak_ib_logfile0
bak_ib_logfile1
bak_ib_logfile2
......@@ -244,7 +244,7 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
FOUND /Expected to open 3 undo tablespaces but was able to find only 0 undo tablespaces/ in mysqld.1.err
FOUND /InnoDB: Unable to open undo tablespace.*undo001/ in mysqld.1.err
bak_ib_logfile0
bak_ib_logfile1
bak_ib_logfile2
......
......@@ -41,7 +41,7 @@ AND support IN ('YES', 'DEFAULT', 'ENABLED');
--let $ibp=$ibp --innodb-data-file-path=ibdata1:16M;ibdata2:10M:autoextend
--echo # Start mysqld without the possibility to create innodb_undo_tablespaces
--let $restart_parameters= $ibp --innodb-undo-tablespaces=3
--let $restart_parameters= $ibp
--mkdir $bugdir/undo002
--source include/restart_mysqld.inc
eval $check_no_innodb;
......@@ -172,7 +172,7 @@ let SEARCH_PATTERN=undo tablespace .*undo003.* exists\. Creating system tablespa
--source include/start_mysqld.inc
eval $check_no_innodb;
--source include/shutdown_mysqld.inc
let SEARCH_PATTERN=Expected to open 3 undo tablespaces but was able to find only 1 undo tablespaces;
let SEARCH_PATTERN=InnoDB: Unable to open undo tablespace.*undo002;
--source include/search_pattern_in_file.inc
# clean up & Restore
--source ../include/log_file_cleanup.inc
......@@ -184,7 +184,7 @@ let SEARCH_PATTERN=Expected to open 3 undo tablespaces but was able to find only
--source include/start_mysqld.inc
eval $check_no_innodb;
--source include/shutdown_mysqld.inc
let SEARCH_PATTERN=Expected to open 3 undo tablespaces but was able to find only 0 undo tablespaces;
let SEARCH_PATTERN=InnoDB: Unable to open undo tablespace.*undo001;
--source include/search_pattern_in_file.inc
# clean up & Restore
......
......@@ -22140,7 +22140,7 @@ innobase_undo_logs_init_default_max()
{
MYSQL_SYSVAR_NAME(undo_logs).max_val
= MYSQL_SYSVAR_NAME(undo_logs).def_val
= static_cast<unsigned long>(srv_available_undo_logs);
= srv_available_undo_logs;
}
/****************************************************************************
......
......@@ -334,9 +334,6 @@ extern my_bool srv_undo_log_truncate;
/* Enables or disables this prefix optimization. Disabled by default. */
extern my_bool srv_prefix_index_cluster_optimization;
/** UNDO logs not redo logged, these logs reside in the temp tablespace.*/
extern const ulong srv_tmp_undo_logs;
/** Default size of UNDO tablespace while it is created new. */
extern const ulint SRV_UNDO_TABLESPACE_SIZE_IN_PAGES;
......@@ -518,7 +515,8 @@ extern uint srv_spin_wait_delay;
extern ibool srv_priority_boost;
extern ulint srv_truncated_status_writes;
extern ulint srv_available_undo_logs;
/** Number of initialized rollback segments for persistent undo log */
extern ulong srv_available_undo_logs;
#if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG
extern my_bool srv_ibuf_disable_background_merge;
......
......@@ -85,16 +85,6 @@ trx_rsegf_undo_find_free(
/*=====================*/
trx_rsegf_t* rsegf, /*!< in: rollback segment header */
mtr_t* mtr); /*!< in: mtr */
/** Get a rollback segment.
@param[in] id rollback segment id
@return rollback segment */
UNIV_INLINE
trx_rseg_t*
trx_rseg_get_on_id(ulint id)
{
ut_a(id < TRX_SYS_N_RSEGS);
return(trx_sys->rseg_array[id]);
}
/** Creates a rollback segment header.
This function is called only when a new rollback segment is created in
......@@ -119,14 +109,14 @@ trx_rseg_array_init();
void
trx_rseg_mem_free(trx_rseg_t* rseg);
/*********************************************************************
Creates a rollback segment. */
/** Create a persistent rollback segment.
@param[in] space_id system or undo tablespace id */
trx_rseg_t*
trx_rseg_create(
/*============*/
ulint space_id, /*!< in: id of UNDO tablespace */
ulint nth_free_slot); /*!< in: allocate nth free slot.
0 means next free slots. */
trx_rseg_create(ulint space_id);
/** Create the temporary rollback segments. */
void
trx_temp_rseg_create();
/********************************************************************
Get the number of unique rollback tablespaces in use except space id 0.
......@@ -205,6 +195,14 @@ struct trx_rseg_t {
/** If true, then skip allocating this rseg as it reside in
UNDO-tablespace marked for truncate. */
bool skip_allocation;
/** @return whether the rollback segment is persistent */
bool is_persistent() const
{
ut_ad(space == SRV_TMP_SPACE_ID
|| space <= srv_undo_tablespaces);
return(space != SRV_TMP_SPACE_ID);
}
};
/* Undo log segment slot in a rollback segment header */
......
......@@ -75,17 +75,10 @@ Creates and initializes the transaction system at the database creation. */
void
trx_sys_create_sys_pages(void);
/*==========================*/
/****************************************************************//**
Looks for a free slot for a rollback segment in the trx system file copy.
@return slot index or ULINT_UNDEFINED if not found */
/** @return an unallocated rollback segment slot in the TRX_SYS header
@retval ULINT_UNDEFINED if not found */
ulint
trx_sysf_rseg_find_free(
/*====================*/
mtr_t* mtr, /*!< in/out: mtr */
bool include_tmp_slots, /*!< in: if true, report slots reserved
for temp-tablespace as free slots. */
ulint nth_free_slots); /*!< in: allocate nth free slot.
0 means next free slot. */
trx_sysf_rseg_find_free(mtr_t* mtr);
/**********************************************************************//**
Gets a pointer to the transaction system file copy and x-locks its page.
@return pointer to system file copy, page x-locked */
......@@ -160,14 +153,6 @@ trx_sys_get_max_trx_id(void);
extern uint trx_rseg_n_slots_debug;
#endif
/*****************************************************************//**
Check if slot-id is reserved slot-id for noredo rsegs. */
UNIV_INLINE
bool
trx_sys_is_noredo_rseg_slot(
/*========================*/
ulint slot_id); /*!< in: slot_id to check */
/*****************************************************************//**
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
......@@ -319,16 +304,10 @@ trx_sys_file_format_max_set(
ulint format_id, /*!< in: file format id */
const char** name); /*!< out: max file format name or
NULL if not needed. */
/*********************************************************************
Creates the rollback segments
@return number of rollback segments that are active. */
ulint
trx_sys_create_rsegs(
/*=================*/
ulint n_spaces, /*!< number of tablespaces for UNDO logs */
ulint n_rsegs, /*!< number of rollback segments to create */
ulint n_tmp_rsegs); /*!< number of rollback segments reserved for
temp-tables. */
/** Create the rollback segments.
@return whether the creation succeeded */
bool
trx_sys_create_rsegs();
/*****************************************************************//**
Get the number of transaction in the system, independent of their state.
@return count of transactions in trx_sys_t::trx_list */
......@@ -556,13 +535,15 @@ struct trx_sys_t {
transactions which exist or existed */
#endif /* UNIV_DEBUG */
char pad1[64]; /*!< To avoid false sharing */
/** Avoid false sharing */
const char pad1[CACHE_LINE_SIZE];
trx_ut_list_t rw_trx_list; /*!< List of active and committed in
memory read-write transactions, sorted
on trx id, biggest first. Recovered
transactions are always on this list. */
char pad2[64]; /*!< To avoid false sharing */
/** Avoid false sharing */
const char pad2[CACHE_LINE_SIZE];
trx_ut_list_t mysql_trx_list; /*!< List of transactions created
for MySQL. All user transactions are
on mysql_trx_list. The rw_trx_list
......@@ -582,7 +563,13 @@ struct trx_sys_t {
to ensure right order of removal and
consistent snapshot. */
char pad3[64]; /*!< To avoid false sharing */
/** Avoid false sharing */
const char pad3[CACHE_LINE_SIZE];
/** Temporary rollback segments */
trx_rseg_t* temp_rsegs[TRX_SYS_N_RSEGS];
/** Avoid false sharing */
const char pad4[CACHE_LINE_SIZE];
trx_rseg_t* rseg_array[TRX_SYS_N_RSEGS];
/*!< Pointer array to rollback
segments; NULL if slot not in use;
......
......@@ -191,18 +191,6 @@ trx_write_trx_id(
mach_write_to_6(ptr, id);
}
/*****************************************************************//**
Check if slot-id is reserved slot-id for noredo rsegs. */
UNIV_INLINE
bool
trx_sys_is_noredo_rseg_slot(
/*========================*/
ulint slot_id) /*!< in: slot_id to check */
{
/* Slots allocated from temp-tablespace are no-redo slots. */
return(slot_id > 0 && slot_id < (srv_tmp_undo_logs + 1));
}
/*****************************************************************//**
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
......
......@@ -507,14 +507,6 @@ trx_id_t
trx_get_id_for_print(
const trx_t* trx);
/****************************************************************//**
Assign a transaction temp-tablespace bound rollback-segment. */
void
trx_assign_rseg(
/*============*/
trx_t* trx); /*!< transaction that involves write
to temp-table. */
/** Create the trx_t pool */
void
trx_pool_init();
......@@ -869,12 +861,6 @@ struct trx_rsegs_t {
trx_temp_undo_t m_noredo;
};
enum trx_rseg_type_t {
TRX_RSEG_TYPE_NONE = 0, /*!< void rollback segment type. */
TRX_RSEG_TYPE_REDO, /*!< redo rollback segment. */
TRX_RSEG_TYPE_NOREDO /*!< non-redo rollback segment. */
};
struct TrxVersion {
TrxVersion(trx_t* trx);
......@@ -1295,6 +1281,22 @@ struct trx_t {
{
return(has_logged_persistent() || rsegs.m_noredo.undo);
}
/** @return rollback segment for modifying temporary tables */
trx_rseg_t* get_temp_rseg()
{
if (trx_rseg_t* rseg = rsegs.m_noredo.rseg) {
ut_ad(id != 0);
return(rseg);
}
return(assign_temp_rseg());
}
private:
/** Assign a rollback segment for modifying temporary tables.
@return the assigned rollback segment */
trx_rseg_t* assign_temp_rseg();
};
/**
......
......@@ -741,10 +741,11 @@ row_purge_upd_exist_or_extern_func(
&is_insert, &rseg_id,
&page_no, &offset);
rseg = trx_rseg_get_on_id(rseg_id);
rseg = trx_sys->rseg_array[rseg_id];
ut_a(rseg != NULL);
ut_a(rseg->id == rseg_id);
ut_ad(rseg->id == rseg_id);
ut_ad(rseg->is_persistent());
mtr_start(&mtr);
......
......@@ -139,10 +139,6 @@ my_bool srv_undo_log_truncate = FALSE;
/** Maximum size of undo tablespace. */
unsigned long long srv_max_undo_log_size;
/** UNDO logs that are not redo logged.
These logs reside in the temp tablespace.*/
const ulong srv_tmp_undo_logs = 32;
/** Default undo tablespace size in UNIV_PAGEs count (10MB). */
const ulint SRV_UNDO_TABLESPACE_SIZE_IN_PAGES =
((1024 * 1024) * 10) / UNIV_PAGE_SIZE_DEF;
......@@ -403,37 +399,38 @@ UNIV_INTERN ulong srv_n_spin_wait_rounds = 15;
uint srv_spin_wait_delay;
ibool srv_priority_boost = TRUE;
static ulint srv_n_rows_inserted_old = 0;
static ulint srv_n_rows_updated_old = 0;
static ulint srv_n_rows_deleted_old = 0;
static ulint srv_n_rows_read_old = 0;
static ulint srv_n_system_rows_inserted_old = 0;
static ulint srv_n_system_rows_updated_old = 0;
static ulint srv_n_system_rows_deleted_old = 0;
static ulint srv_n_system_rows_read_old = 0;
ulint srv_truncated_status_writes = 0;
ulint srv_available_undo_logs = 0;
UNIV_INTERN ib_uint64_t srv_page_compression_saved = 0;
UNIV_INTERN ib_uint64_t srv_page_compression_trim_sect512 = 0;
UNIV_INTERN ib_uint64_t srv_page_compression_trim_sect4096 = 0;
UNIV_INTERN ib_uint64_t srv_index_pages_written = 0;
UNIV_INTERN ib_uint64_t srv_non_index_pages_written = 0;
UNIV_INTERN ib_uint64_t srv_pages_page_compressed = 0;
UNIV_INTERN ib_uint64_t srv_page_compressed_trim_op = 0;
UNIV_INTERN ib_uint64_t srv_page_compressed_trim_op_saved = 0;
UNIV_INTERN ib_uint64_t srv_index_page_decompressed = 0;
static ulint srv_n_rows_inserted_old;
static ulint srv_n_rows_updated_old;
static ulint srv_n_rows_deleted_old;
static ulint srv_n_rows_read_old;
static ulint srv_n_system_rows_inserted_old;
static ulint srv_n_system_rows_updated_old;
static ulint srv_n_system_rows_deleted_old;
static ulint srv_n_system_rows_read_old;
ulint srv_truncated_status_writes;
/** Number of initialized rollback segments for persistent undo log */
ulong srv_available_undo_logs;
UNIV_INTERN ib_uint64_t srv_page_compression_saved;
UNIV_INTERN ib_uint64_t srv_page_compression_trim_sect512;
UNIV_INTERN ib_uint64_t srv_page_compression_trim_sect4096;
UNIV_INTERN ib_uint64_t srv_index_pages_written;
UNIV_INTERN ib_uint64_t srv_non_index_pages_written;
UNIV_INTERN ib_uint64_t srv_pages_page_compressed;
UNIV_INTERN ib_uint64_t srv_page_compressed_trim_op;
UNIV_INTERN ib_uint64_t srv_page_compressed_trim_op_saved;
UNIV_INTERN ib_uint64_t srv_index_page_decompressed;
/* Defragmentation */
UNIV_INTERN my_bool srv_defragment = FALSE;
UNIV_INTERN my_bool srv_defragment;
UNIV_INTERN uint srv_defragment_n_pages = 7;
UNIV_INTERN uint srv_defragment_stats_accuracy = 0;
UNIV_INTERN uint srv_defragment_stats_accuracy;
UNIV_INTERN uint srv_defragment_fill_factor_n_recs = 20;
UNIV_INTERN double srv_defragment_fill_factor = 0.9;
UNIV_INTERN uint srv_defragment_frequency =
SRV_DEFRAGMENT_FREQUENCY_DEFAULT;
UNIV_INTERN ulonglong srv_defragment_interval = 0;
UNIV_INTERN ulonglong srv_defragment_interval;
/* Set the following to 0 if you want InnoDB to write messages on
stderr on startup/shutdown. */
......
......@@ -804,20 +804,12 @@ srv_check_undo_redo_logs_exists()
undo::undo_spaces_t undo::Truncate::s_fix_up_spaces;
/********************************************************************
Opens the configured number of undo tablespaces.
/** Open the configured number of dedicated undo tablespaces.
@param[in] create_new_db whether the database is being initialized
@return DB_SUCCESS or error code */
static
dberr_t
srv_undo_tablespaces_init(
/*======================*/
bool create_new_db, /*!< in: TRUE if new db being
created */
const ulint n_conf_tablespaces, /*!< in: configured undo
tablespaces */
ulint* n_opened) /*!< out: number of UNDO
tablespaces successfully
discovered and opened */
srv_undo_tablespaces_init(bool create_new_db)
{
ulint i;
dberr_t err = DB_SUCCESS;
......@@ -825,9 +817,9 @@ srv_undo_tablespaces_init(
ulint n_undo_tablespaces;
ulint undo_tablespace_ids[TRX_SYS_N_RSEGS + 1];
*n_opened = 0;
srv_undo_tablespaces_open = 0;
ut_a(n_conf_tablespaces <= TRX_SYS_N_RSEGS);
ut_a(srv_undo_tablespaces <= TRX_SYS_N_RSEGS);
memset(undo_tablespace_ids, 0x0, sizeof(undo_tablespace_ids));
......@@ -839,7 +831,7 @@ srv_undo_tablespaces_init(
the location of the undo tablespaces and their space ids this
restriction will/should be lifted. */
for (i = 0; create_new_db && i < n_conf_tablespaces; ++i) {
for (i = 0; create_new_db && i < srv_undo_tablespaces; ++i) {
char name[OS_FILE_MAX_PATH];
ut_snprintf(
......@@ -902,7 +894,7 @@ srv_undo_tablespaces_init(
}
}
} else {
n_undo_tablespaces = n_conf_tablespaces;
n_undo_tablespaces = srv_undo_tablespaces;
for (i = 1; i <= n_undo_tablespaces; ++i) {
undo_tablespace_ids[i - 1] = i;
......@@ -944,7 +936,7 @@ srv_undo_tablespaces_init(
prev_space_id = undo_tablespace_ids[i];
++*n_opened;
++srv_undo_tablespaces_open;
}
/* Open any extra unused undo tablespaces. These must be contiguous.
......@@ -968,19 +960,17 @@ srv_undo_tablespaces_init(
++n_undo_tablespaces;
++*n_opened;
++srv_undo_tablespaces_open;
}
/* If the user says that there are fewer than what we find we
tolerate that discrepancy but not the inverse. Because there could
be unused undo tablespaces for future use. */
if (n_conf_tablespaces > n_undo_tablespaces) {
ib::error() << "Expected to open " << n_conf_tablespaces
<< " undo tablespaces but was able to find only "
<< n_undo_tablespaces << " undo tablespaces. Set the"
" innodb_undo_tablespaces parameter to the correct"
" value and retry. Suggested value is "
if (srv_undo_tablespaces > n_undo_tablespaces) {
ib::error() << "Expected to open innodb_undo_tablespaces="
<< srv_undo_tablespaces
<< " but was able to find only "
<< n_undo_tablespaces;
return(err != DB_SUCCESS ? err : DB_ERROR);
......@@ -988,15 +978,13 @@ srv_undo_tablespaces_init(
} else if (n_undo_tablespaces > 0) {
ib::info() << "Opened " << n_undo_tablespaces
<< " undo tablespaces";
ib::info() << srv_undo_tablespaces_active << " undo tablespaces"
<< " made active";
<< " undo tablespaces ("
<< srv_undo_tablespaces_active
<< " active)";
if (n_conf_tablespaces == 0) {
ib::warn() << "Will use system tablespace for all newly"
<< " created rollback-segment as"
<< " innodb_undo_tablespaces=0";
if (srv_undo_tablespaces == 0) {
ib::warn() << "innodb_undo_tablespaces=0 disables"
" dedicated undo log tablespaces";
}
}
......@@ -2087,10 +2075,7 @@ innobase_start_or_create_for_mysql(void)
fil_open_log_and_system_tablespace_files();
ut_d(fil_space_get(0)->recv_size = srv_sys_space_size_debug);
err = srv_undo_tablespaces_init(
create_new_db,
srv_undo_tablespaces,
&srv_undo_tablespaces_open);
err = srv_undo_tablespaces_init(create_new_db);
/* If the force recovery is set very high then we carry on regardless
of all errors. Basically this is fingers crossed mode. */
......@@ -2512,22 +2497,7 @@ innobase_start_or_create_for_mysql(void)
ut_a(srv_undo_logs > 0);
ut_a(srv_undo_logs <= TRX_SYS_N_RSEGS);
/* The number of rsegs that exist in InnoDB is given by status
variable srv_available_undo_logs. The number of rsegs to use can
be set using the dynamic global variable srv_undo_logs. */
srv_available_undo_logs = trx_sys_create_rsegs(
srv_undo_tablespaces, srv_undo_logs, srv_tmp_undo_logs);
if (srv_available_undo_logs == ULINT_UNDEFINED) {
/* Can only happen if server is read only. */
ut_a(srv_read_only_mode);
srv_undo_logs = ULONG_UNDEFINED;
} else if (srv_available_undo_logs < srv_undo_logs
&& !srv_force_recovery && !recv_needed_recovery) {
ib::error() << "System or UNDO tablespace is running of out"
<< " of space";
/* Should due to out of file space. */
if (!trx_sys_create_rsegs()) {
return(srv_init_abort(DB_ERROR));
}
......
......@@ -327,17 +327,12 @@ trx_purge_remove_log_hdr(
my_atomic_addlint(&trx_sys->rseg_history_len, -1);
}
/** Frees an undo log segment which is in the history list. Removes the
undo log hdr from the history list.
/** Free an undo log segment, and remove the header from the history list.
@param[in,out] rseg rollback segment
@param[in] hdr_addr file address of log_hdr
@param[in] noredo skip redo logging. */
@param[in] hdr_addr file address of log_hdr */
static
void
trx_purge_free_segment(
trx_rseg_t* rseg,
fil_addr_t hdr_addr,
bool noredo)
trx_purge_free_segment(trx_rseg_t* rseg, fil_addr_t hdr_addr)
{
mtr_t mtr;
trx_rsegf_t* rseg_hdr;
......@@ -345,16 +340,12 @@ trx_purge_free_segment(
trx_usegf_t* seg_hdr;
ulint seg_size;
ulint hist_size;
bool marked = noredo;
bool marked = false;
for (;;) {
page_t* undo_page;
mtr_start(&mtr);
if (noredo) {
mtr.set_log_mode(MTR_LOG_NO_REDO);
}
ut_ad(noredo == trx_sys_is_noredo_rseg_slot(rseg->id));
mutex_enter(&rseg->mutex);
......@@ -428,14 +419,12 @@ trx_purge_free_segment(
mtr_commit(&mtr);
}
/********************************************************************//**
Removes unnecessary history data from a rollback segment. */
/** Remove unnecessary history data from a rollback segment.
@param[in,out] rseg rollback segment
@param[in] limit truncate offset */
static
void
trx_purge_truncate_rseg_history(
/*============================*/
trx_rseg_t* rseg, /*!< in: rollback segment */
const purge_iter_t* limit) /*!< in: truncate offset */
trx_purge_truncate_rseg_history(trx_rseg_t* rseg, const purge_iter_t* limit)
{
fil_addr_t hdr_addr;
fil_addr_t prev_hdr_addr;
......@@ -445,13 +434,9 @@ trx_purge_truncate_rseg_history(
trx_usegf_t* seg_hdr;
mtr_t mtr;
trx_id_t undo_trx_no;
const bool noredo = trx_sys_is_noredo_rseg_slot(
rseg->id);
mtr_start(&mtr);
if (noredo) {
mtr.set_log_mode(MTR_LOG_NO_REDO);
}
ut_ad(rseg->is_persistent());
mutex_enter(&(rseg->mutex));
rseg_hdr = trx_rsegf_get(rseg->space, rseg->page_no, &mtr);
......@@ -509,7 +494,7 @@ trx_purge_truncate_rseg_history(
/* calls the trx_purge_remove_log_hdr()
inside trx_purge_free_segment(). */
trx_purge_free_segment(rseg, hdr_addr, noredo);
trx_purge_free_segment(rseg, hdr_addr);
} else {
/* Remove the log hdr from the rseg history. */
trx_purge_remove_log_hdr(rseg_hdr, log_hdr, &mtr);
......@@ -519,9 +504,6 @@ trx_purge_truncate_rseg_history(
}
mtr_start(&mtr);
if (noredo) {
mtr.set_log_mode(MTR_LOG_NO_REDO);
}
mutex_enter(&(rseg->mutex));
rseg_hdr = trx_rsegf_get(rseg->space, rseg->page_no, &mtr);
......@@ -806,10 +788,9 @@ trx_purge_mark_undo_for_truncate(
/* 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.
b. At-least 2 UNDO redo rseg/undo logs (besides the default rseg-0)
b. At-least 2 persistent UNDO logs (besides the default rseg-0)
b. At-least 1 UNDO tablespace size > threshold. */
if (srv_undo_tablespaces_active < 2
|| (srv_undo_logs < (1 + srv_tmp_undo_logs + 2))) {
if (srv_undo_tablespaces_active < 2 || srv_undo_logs < 3) {
return;
}
......@@ -846,11 +827,9 @@ trx_purge_mark_undo_for_truncate(
/* 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) {
trx_rseg_t* rseg = trx_sys->rseg_array[i];
if (rseg != NULL && !trx_sys_is_noredo_rseg_slot(rseg->id)) {
if (rseg->space
== undo_trunc->get_marked_space_id()) {
if (trx_rseg_t* rseg = trx_sys->rseg_array[i]) {
ut_ad(rseg->is_persistent());
if (rseg->space == undo_trunc->get_marked_space_id()) {
/* Once set this rseg will not be allocated
to new booting transaction but we will wait
......
......@@ -1893,13 +1893,7 @@ trx_undo_report_row_operation(
if (is_temp) {
mtr.set_log_mode(MTR_LOG_NO_REDO);
rseg = trx->rsegs.m_noredo.rseg;
if (!rseg) {
trx_assign_rseg(trx);
rseg = trx->rsegs.m_noredo.rseg;
}
rseg = trx->get_temp_rseg();
pundo = &trx->rsegs.m_noredo.undo;
} else {
ut_ad(!trx->read_only);
......@@ -2057,16 +2051,16 @@ trx_undo_report_row_operation(
/*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/
/******************************************************************//**
Copies an undo record to heap. This function can be called if we know that
the undo log record exists.
@return own: copy of the record */
/** Copy an undo record to heap.
@param[in] roll_ptr roll pointer to a record that exists
@param[in] is_temp whether this is a temporary table
@param[in,out] heap memory heap where copied */
static
trx_undo_rec_t*
trx_undo_get_undo_rec_low(
/*======================*/
roll_ptr_t roll_ptr, /*!< in: roll pointer to record */
mem_heap_t* heap) /*!< in: memory heap where copied */
roll_ptr_t roll_ptr,
bool is_temp,
mem_heap_t* heap)
{
trx_undo_rec_t* undo_rec;
ulint rseg_id;
......@@ -2079,7 +2073,10 @@ trx_undo_get_undo_rec_low(
trx_undo_decode_roll_ptr(roll_ptr, &is_insert, &rseg_id, &page_no,
&offset);
rseg = trx_rseg_get_on_id(rseg_id);
rseg = is_temp
? trx_sys->temp_rsegs[rseg_id]
: trx_sys->rseg_array[rseg_id];
ut_ad(is_temp == !rseg->is_persistent());
mtr_start(&mtr);
......@@ -2093,13 +2090,13 @@ trx_undo_get_undo_rec_low(
return(undo_rec);
}
/******************************************************************//**
Copies an undo record to heap.
/** Copy an undo record to heap.
@param[in] roll_ptr roll pointer to record
@param[in] is_temp whether this is a temporary table
@param[in,out] heap memory heap where copied
@param[in] trx_id id of the trx that generated
the roll pointer: it points to an
undo log of this transaction
@param[in] heap memory heap where copied
@param[in] name table name
@param[out] undo_rec own: copy of the record
@retval true if the undo log has been
......@@ -2109,10 +2106,10 @@ NOTE: the caller must have latches on the clustered index page. */
static MY_ATTRIBUTE((warn_unused_result))
bool
trx_undo_get_undo_rec(
/*==================*/
roll_ptr_t roll_ptr,
trx_id_t trx_id,
bool is_temp,
mem_heap_t* heap,
trx_id_t trx_id,
const table_name_t& name,
trx_undo_rec_t** undo_rec)
{
......@@ -2122,7 +2119,7 @@ trx_undo_get_undo_rec(
missing_history = purge_sys->view.changes_visible(trx_id, name);
if (!missing_history) {
*undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
*undo_rec = trx_undo_get_undo_rec_low(roll_ptr, is_temp, heap);
}
rw_lock_s_unlock(&purge_sys->latch);
......@@ -2203,13 +2200,17 @@ trx_undo_prev_version_build(
return(true);
}
const bool is_temp = dict_table_is_temporary(index->table);
rec_trx_id = row_get_rec_trx_id(rec, index, offsets);
if (trx_undo_get_undo_rec(
roll_ptr, rec_trx_id, heap, index->table->name, &undo_rec)) {
roll_ptr, is_temp, heap, rec_trx_id, index->table->name,
&undo_rec)) {
if (v_status & TRX_UNDO_PREV_IN_PURGE) {
/* We are fetching the record being purged */
undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
ut_ad(!is_temp);
undo_rec = trx_undo_get_undo_rec_low(
roll_ptr, is_temp, heap);
} else {
/* The undo record may already have been purged,
during purge or semi-consistent read. */
......
......@@ -90,12 +90,7 @@ trx_rseg_header_create(
trx_rsegf_set_nth_undo(rsegf, i, FIL_NULL, mtr);
}
if (!trx_sys_is_noredo_rseg_slot(rseg_slot_no)) {
/* Non-redo rseg are re-created on restart and so no need
to persist this information in sys-header. Anyway, on restart
this information is not valid too as there is no space with
persisted space-id on restart. */
if (space != SRV_TMP_SPACE_ID) {
/* Add the rollback segment info to the free slot in
the trx system header */
......@@ -152,51 +147,48 @@ trx_rseg_mem_free(trx_rseg_t* rseg)
ut_free(rseg);
}
/** Creates and initializes a rollback segment object.
The values for the fields are read from the header. The object is inserted to
the rseg list of the trx system object and a pointer is inserted in the rseg
array in the trx system object.
/** Create a rollback segment object.
@param[in] id rollback segment id
@param[in] space space where the segment is placed
@param[in] page_no page number of the segment header
@param[in,out] mtr mini-transaction */
@param[in] page_no page number of the segment header */
static
void
trx_rseg_mem_create(
ulint id,
ulint space,
ulint page_no,
mtr_t* mtr)
trx_rseg_t*
trx_rseg_mem_create(ulint id, ulint space, ulint page_no)
{
ulint len;
trx_rseg_t* rseg;
fil_addr_t node_addr;
trx_rsegf_t* rseg_header;
trx_ulogf_t* undo_log_hdr;
ulint sum_of_undo_sizes;
rseg = static_cast<trx_rseg_t*>(ut_zalloc_nokey(sizeof(trx_rseg_t)));
trx_rseg_t* rseg = static_cast<trx_rseg_t*>(
ut_zalloc_nokey(sizeof *rseg));
rseg->id = id;
rseg->space = space;
rseg->page_no = page_no;
rseg->trx_ref_count = 0;
rseg->skip_allocation = false;
rseg->last_page_no = FIL_NULL;
if (fsp_is_system_temporary(space)) {
mutex_create(LATCH_ID_NOREDO_RSEG, &rseg->mutex);
} else {
mutex_create(LATCH_ID_REDO_RSEG, &rseg->mutex);
}
mutex_create(rseg->is_persistent()
? LATCH_ID_REDO_RSEG : LATCH_ID_NOREDO_RSEG,
&rseg->mutex);
UT_LIST_INIT(rseg->update_undo_list, &trx_undo_t::undo_list);
UT_LIST_INIT(rseg->update_undo_cached, &trx_undo_t::undo_list);
UT_LIST_INIT(rseg->insert_undo_list, &trx_undo_t::undo_list);
UT_LIST_INIT(rseg->insert_undo_cached, &trx_undo_t::undo_list);
trx_sys->rseg_array[id] = rseg;
return(rseg);
}
/** Restore the state of a persistent rollback segment.
@param[in,out] rseg persistent rollback segment
@param[in,out] mtr mini-transaction */
static
void
trx_rseg_mem_restore(trx_rseg_t* rseg, mtr_t* mtr)
{
ulint len;
fil_addr_t node_addr;
trx_rsegf_t* rseg_header;
trx_ulogf_t* undo_log_hdr;
ulint sum_of_undo_sizes;
rseg_header = trx_rsegf_get_new(space, page_no, mtr);
rseg_header = trx_rsegf_get_new(rseg->space, rseg->page_no, mtr);
rseg->max_size = mtr_read_ulint(
rseg_header + TRX_RSEG_MAX_SIZE, MLOG_4BYTES, mtr);
......@@ -240,8 +232,6 @@ trx_rseg_mem_create(
purge_sys->purge_queue.push(elem);
}
} else {
rseg->last_page_no = FIL_NULL;
}
}
......@@ -252,54 +242,45 @@ trx_rseg_array_init()
mtr_t mtr;
for (ulint i = 0; i < TRX_SYS_N_RSEGS; i++) {
ut_ad(!trx_rseg_get_on_id(i));
mtr.start();
trx_sysf_t* sys_header = trx_sysf_get(&mtr);
ulint page_no = trx_sysf_rseg_get_page_no(
sys_header, i, &mtr);
if (page_no != FIL_NULL) {
trx_rseg_mem_create(
trx_rseg_t* rseg = trx_rseg_mem_create(
i,
trx_sysf_rseg_get_space(sys_header, i, &mtr),
page_no, &mtr);
page_no);
ut_ad(rseg->is_persistent());
ut_ad(!trx_sys->rseg_array[rseg->id]);
trx_sys->rseg_array[rseg->id] = rseg;
trx_rseg_mem_restore(rseg, &mtr);
}
mtr.commit();
}
}
/*********************************************************************
Creates a rollback segment.
@return pointer to new rollback segment if create successful */
/** Create a persistent rollback segment.
@param[in] space_id system or undo tablespace id */
trx_rseg_t*
trx_rseg_create(
/*============*/
ulint space_id, /*!< in: id of UNDO tablespace */
ulint nth_free_slot) /*!< in: allocate nth free slot.
0 means next free slots. */
trx_rseg_create(ulint space_id)
{
mtr_t mtr;
trx_rseg_t* rseg = NULL;
mtr_t mtr;
mtr.start();
/* To obey the latching order, acquire the file space
x-latch before the trx_sys->mutex. */
const fil_space_t* space = mtr_x_lock_space(space_id, &mtr);
#ifdef UNIV_DEBUG
const fil_space_t* space =
#endif /* UNIV_DEBUG */
mtr_x_lock_space(space_id, &mtr);
ut_ad(space->purpose == FIL_TYPE_TABLESPACE);
switch (space->purpose) {
case FIL_TYPE_LOG:
case FIL_TYPE_IMPORT:
ut_ad(0);
case FIL_TYPE_TEMPORARY:
mtr.set_log_mode(MTR_LOG_NO_REDO);
break;
case FIL_TYPE_TABLESPACE:
break;
}
ulint slot_no = trx_sysf_rseg_find_free(
&mtr, space->purpose == FIL_TYPE_TEMPORARY, nth_free_slot);
ulint slot_no = trx_sysf_rseg_find_free(&mtr);
ulint page_no = slot_no == ULINT_UNDEFINED
? FIL_NULL
: trx_rseg_header_create(space_id, ULINT_MAX, slot_no, &mtr);
......@@ -309,14 +290,45 @@ trx_rseg_create(
ulint id = trx_sysf_rseg_get_space(
sys_header, slot_no, &mtr);
ut_a(id == space_id || trx_sys_is_noredo_rseg_slot(slot_no));
ut_a(id == space_id);
trx_rseg_mem_create(slot_no, space_id, page_no, &mtr);
rseg = trx_rseg_mem_create(slot_no, space_id, page_no);
ut_ad(rseg->is_persistent());
ut_ad(!trx_sys->rseg_array[rseg->id]);
trx_sys->rseg_array[rseg->id] = rseg;
trx_rseg_mem_restore(rseg, &mtr);
}
mtr.commit();
return(page_no == FIL_NULL ? NULL : trx_sys->rseg_array[slot_no]);
return(rseg);
}
/** Create the temporary rollback segments. */
void
trx_temp_rseg_create()
{
mtr_t mtr;
for (ulong i = 0; i < TRX_SYS_N_RSEGS; i++) {
mtr.start();
mtr.set_log_mode(MTR_LOG_NO_REDO);
#ifdef UNIV_DEBUG
const fil_space_t* space =
#endif /* UNIV_DEBUG */
mtr_x_lock_space(SRV_TMP_SPACE_ID, &mtr);
ut_ad(space->purpose == FIL_TYPE_TEMPORARY);
ulint page_no = trx_rseg_header_create(
SRV_TMP_SPACE_ID, ULINT_MAX, i, &mtr);
trx_rseg_t* rseg = trx_rseg_mem_create(
i, SRV_TMP_SPACE_ID, page_no);
ut_ad(!rseg->is_persistent());
ut_ad(!trx_sys->temp_rsegs[i]);
trx_sys->temp_rsegs[i] = rseg;
trx_rseg_mem_restore(rseg, &mtr);
mtr.commit();
}
}
/********************************************************************
......
......@@ -394,76 +394,41 @@ trx_sys_read_wsrep_checkpoint(
#endif /* WITH_WSREP */
/****************************************************************//**
Looks for a free slot for a rollback segment in the trx system file copy.
@return slot index or ULINT_UNDEFINED if not found */
/** @return an unallocated rollback segment slot in the TRX_SYS header
@retval ULINT_UNDEFINED if not found */
ulint
trx_sysf_rseg_find_free(
/*====================*/
mtr_t* mtr, /*!< in/out: mtr */
bool include_tmp_slots, /*!< in: if true, report slots reserved
for temp-tablespace as free slots. */
ulint nth_free_slots) /*!< in: allocate nth free slot.
0 means next free slot. */
trx_sysf_rseg_find_free(mtr_t* mtr)
{
ulint i;
trx_sysf_t* sys_header;
sys_header = trx_sysf_get(mtr);
ulint found_free_slots = 0;
for (i = 0; i < TRX_SYS_N_RSEGS; i++) {
ulint page_no;
if (!include_tmp_slots && trx_sys_is_noredo_rseg_slot(i)) {
continue;
}
page_no = trx_sysf_rseg_get_page_no(sys_header, i, mtr);
trx_sysf_t* sys_header = trx_sysf_get(mtr);
if (page_no == FIL_NULL
|| (include_tmp_slots
&& trx_sys_is_noredo_rseg_slot(i))) {
if (found_free_slots++ >= nth_free_slots) {
return(i);
}
for (ulint i = 0; i < TRX_SYS_N_RSEGS; i++) {
if (trx_sysf_rseg_get_page_no(sys_header, i, mtr)
== FIL_NULL) {
return(i);
}
}
return(ULINT_UNDEFINED);
}
/****************************************************************//**
Looks for used slots for redo rollback segment.
@return number of used slots */
/** Count the number of initialized persistent rollback segment slots. */
static
ulint
trx_sysf_used_slots_for_redo_rseg(
/*==============================*/
mtr_t* mtr) /*!< in: mtr */
void
trx_sysf_get_n_rseg_slots()
{
trx_sysf_t* sys_header;
ulint n_used = 0;
mtr_t mtr;
mtr.start();
sys_header = trx_sysf_get(mtr);
trx_sysf_t* sys_header = trx_sysf_get(&mtr);
srv_available_undo_logs = 0;
for (ulint i = 0; i < TRX_SYS_N_RSEGS; i++) {
if (trx_sys_is_noredo_rseg_slot(i)) {
continue;
}
ulint page_no;
page_no = trx_sysf_rseg_get_page_no(sys_header, i, mtr);
if (page_no != FIL_NULL) {
++n_used;
}
srv_available_undo_logs
+= trx_sysf_rseg_get_page_no(sys_header, i, &mtr)
!= FIL_NULL;
}
return(n_used);
mtr.commit();
}
/*****************************************************************//**
......@@ -532,7 +497,7 @@ trx_sysf_create(
+ page - sys_header, mtr);
/* Create the first rollback segment in the SYSTEM tablespace */
slot_no = trx_sysf_rseg_find_free(mtr, false, 0);
slot_no = trx_sysf_rseg_find_free(mtr);
page_no = trx_rseg_header_create(TRX_SYS_SPACE,
ULINT_MAX, slot_no, mtr);
......@@ -904,118 +869,69 @@ trx_sys_file_format_close(void)
mutex_free(&file_format_max.mutex);
}
/*********************************************************************
Creates non-redo rollback segments.
@return number of non-redo rollback segments created. */
static
ulint
trx_sys_create_noredo_rsegs(
/*========================*/
ulint n_nonredo_rseg) /*!< number of non-redo rollback segment
to create. */
{
ulint n_created = 0;
/* Create non-redo rollback segments residing in temp-tablespace.
non-redo rollback segments don't perform redo logging and so
are used for undo logging of objects/table that don't need to be
recover on crash.
(Non-Redo rollback segments are created on every server startup).
Slot-0: reserved for system-tablespace.
Slot-1....Slot-N: reserved for temp-tablespace.
Slot-N+1....Slot-127: reserved for system/undo-tablespace. */
for (ulint i = 0; i < n_nonredo_rseg; i++) {
if (trx_rseg_create(SRV_TMP_SPACE_ID, i) == NULL) {
break;
}
++n_created;
}
return(n_created);
}
/*********************************************************************
Creates the rollback segments.
@return number of rollback segments that are active. */
ulint
trx_sys_create_rsegs(
/*=================*/
ulint n_spaces, /*!< number of tablespaces for UNDO logs */
ulint n_rsegs, /*!< number of rollback segments to create */
ulint n_tmp_rsegs) /*!< number of rollback segments reserved for
temp-tables. */
/** Create the rollback segments.
@return whether the creation succeeded */
bool
trx_sys_create_rsegs()
{
mtr_t mtr;
ulint n_used;
ulint n_noredo_created;
/* srv_available_undo_logs reflects the number of persistent
rollback segments that have been initialized in the
transaction system header page.
ut_a(n_spaces < TRX_SYS_N_RSEGS);
ut_a(n_rsegs <= TRX_SYS_N_RSEGS);
ut_a(n_tmp_rsegs > 0 && n_tmp_rsegs < TRX_SYS_N_RSEGS);
srv_undo_logs determines how many of the
srv_available_undo_logs rollback segments may be used for
logging new transactions. */
ut_ad(srv_undo_tablespaces < TRX_SYS_N_RSEGS);
ut_ad(srv_undo_logs <= TRX_SYS_N_RSEGS);
if (srv_read_only_mode) {
return(ULINT_UNDEFINED);
srv_undo_logs = srv_available_undo_logs = ULONG_UNDEFINED;
return(true);
}
/* Create non-redo rollback segments. */
n_noredo_created = trx_sys_create_noredo_rsegs(n_tmp_rsegs);
/* Create temporary rollback segments. */
trx_temp_rseg_create();
/* This is executed in single-threaded mode therefore it is not
necessary to use the same mtr in trx_rseg_create(). n_used cannot
change while the function is executing. */
mtr_start(&mtr);
n_used = trx_sysf_used_slots_for_redo_rseg(&mtr) + n_noredo_created;
mtr_commit(&mtr);
trx_sysf_get_n_rseg_slots();
ut_ad(n_used <= TRX_SYS_N_RSEGS);
ut_ad(srv_available_undo_logs <= TRX_SYS_N_RSEGS);
/* By default 1 redo rseg is always active that is hosted in
system tablespace. */
ulint n_redo_active;
if (n_rsegs <= n_tmp_rsegs) {
n_redo_active = 1;
} else if (n_rsegs > n_used) {
n_redo_active = n_used - n_tmp_rsegs;
} else {
n_redo_active = n_rsegs - n_tmp_rsegs;
}
/* Do not create additional rollback segments if innodb_force_recovery
has been set and the database was not shutdown cleanly. */
if (!srv_force_recovery && !recv_needed_recovery && n_used < n_rsegs) {
ulint i;
ulint new_rsegs = n_rsegs - n_used;
for (i = 0; i < new_rsegs; ++i) {
ulint space;
/* Tablespace 0 is the system tablespace. All UNDO
log tablespaces start from 1. */
/* The first persistent rollback segment is always initialized
in the system tablespace. */
ut_a(srv_available_undo_logs > 0);
if (n_spaces > 0) {
space = (i % n_spaces) + 1;
} else {
space = 0; /* System tablespace */
}
if (trx_rseg_create(space, 0) != NULL) {
++n_used;
++n_redo_active;
} else {
break;
if (srv_force_recovery) {
/* Do not create additional rollback segments if
innodb_force_recovery has been set. */
if (srv_undo_logs > srv_available_undo_logs) {
srv_undo_logs = srv_available_undo_logs;
}
} else {
for (ulint i = 0; srv_available_undo_logs < srv_undo_logs;
i++, srv_available_undo_logs++) {
/* Tablespace 0 is the system tablespace.
Dedicated undo log tablespaces start from 1. */
ulint space = srv_undo_tablespaces > 0
? (i % srv_undo_tablespaces) + 1
: TRX_SYS_SPACE;
if (!trx_rseg_create(space)) {
ib::error() << "Unable to allocate the"
" requested innodb_undo_logs";
return(false);
}
}
}
ib::info() << n_used - srv_tmp_undo_logs
<< " redo rollback segment(s) found. "
<< n_redo_active
<< " redo rollback segment(s) are active.";
ut_ad(srv_undo_logs <= srv_available_undo_logs);
ib::info() << n_noredo_created << " non-redo rollback segment(s) are"
" active.";
ib::info() << srv_undo_logs << " out of " << srv_available_undo_logs
<< " rollback segments are active.";
return(n_used);
return(true);
}
/*********************************************************************
......@@ -1027,9 +943,7 @@ trx_sys_close(void)
ut_ad(trx_sys != NULL);
ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS);
ulint size = trx_sys->mvcc->size();
if (size > 0) {
if (ulint size = trx_sys->mvcc->size()) {
ib::error() << "All read views were not closed before"
" shutdown: " << size << " read views open";
}
......@@ -1060,6 +974,10 @@ trx_sys_close(void)
if (trx_rseg_t* rseg = trx_sys->rseg_array[i]) {
trx_rseg_mem_free(rseg);
}
if (trx_rseg_t* rseg = trx_sys->temp_rsegs[i]) {
trx_rseg_mem_free(rseg);
}
}
UT_DELETE(trx_sys->mvcc);
......
......@@ -1090,42 +1090,33 @@ trx_lists_init_at_db_start()
}
}
/******************************************************************//**
Get next redo rollback segment. (Segment are assigned in round-robin fashion).
@return assigned rollback segment instance */
/** Assign a persistent rollback segment in a round-robin fashion,
evenly distributed between 0 and innodb_undo_logs-1
@return persistent rollback segment
@retval NULL if innodb_read_only */
static
trx_rseg_t*
get_next_redo_rseg(
/*===============*/
ulong max_undo_logs, /*!< in: maximum number of UNDO logs to use */
ulint n_tablespaces) /*!< in: number of rollback tablespaces */
trx_assign_rseg_low()
{
trx_rseg_t* rseg;
static ulint redo_rseg_slot = 0;
ulint slot = 0;
slot = redo_rseg_slot++;
slot = slot % max_undo_logs;
/* Skip slots alloted to non-redo also ensure even distribution
in selecting next redo slots.
For example: If we don't do even distribution then for any value of
slot between 1 - 32 ... 33rd slots will be alloted creating
skewed distribution. */
if (trx_sys_is_noredo_rseg_slot(slot)) {
if (max_undo_logs > srv_tmp_undo_logs) {
if (srv_read_only_mode) {
ut_ad(srv_undo_logs == ULONG_UNDEFINED);
return(NULL);
}
slot %= (max_undo_logs - srv_tmp_undo_logs);
/* The first slot is always assigned to the system tablespace. */
ut_ad(trx_sys->rseg_array[0]->space == TRX_SYS_SPACE);
if (trx_sys_is_noredo_rseg_slot(slot)) {
slot += srv_tmp_undo_logs;
}
/* Choose a rollback segment evenly distributed between 0 and
innodb_undo_logs-1 in a round-robin fashion, skipping those
undo tablespaces that are scheduled for truncation.
} else {
slot = 0;
}
}
Because rseg_slot is not protected by atomics or any mutex, race
conditions are possible, meaning that multiple transactions
that start modifications concurrently will write their undo
log to the same rollback segment. */
static ulong rseg_slot;
ulint slot = rseg_slot++ % srv_undo_logs;
trx_rseg_t* rseg;
#ifdef UNIV_DEBUG
ulint start_scan_slot = slot;
......@@ -1134,8 +1125,7 @@ get_next_redo_rseg(
bool allocated = false;
while (!allocated) {
do {
for (;;) {
rseg = trx_sys->rseg_array[slot];
......@@ -1148,37 +1138,31 @@ get_next_redo_rseg(
look_for_rollover = true;
#endif /* UNIV_DEBUG */
slot = (slot + 1) % max_undo_logs;
/* Skip slots allocated for noredo rsegs */
while (trx_sys_is_noredo_rseg_slot(slot)) {
slot = (slot + 1) % max_undo_logs;
}
slot = (slot + 1) % srv_undo_logs;
if (rseg == NULL) {
continue;
} else if (rseg->space == srv_sys_space.space_id()
&& n_tablespaces > 0
&& trx_sys->rseg_array[slot] != NULL
&& trx_sys->rseg_array[slot]->space
!= srv_sys_space.space_id()) {
/** If undo-tablespace is configured, skip
rseg from system-tablespace and try to use
undo-tablespace rseg unless it is not possible
due to lower limit of undo-logs. */
continue;
} else if (rseg->skip_allocation) {
/** This rseg resides in the tablespace that
has been marked for truncate so avoid using this
rseg. Also, this is possible only if there are
at-least 2 UNDO tablespaces active and 2 redo
rsegs active (other than default system bound
rseg-0). */
ut_ad(n_tablespaces > 1);
ut_ad(max_undo_logs
>= (1 + srv_tmp_undo_logs + 2));
continue;
}
ut_ad(rseg->is_persistent());
if (rseg->space != TRX_SYS_SPACE) {
ut_ad(srv_undo_tablespaces > 1);
if (rseg->skip_allocation) {
continue;
}
} else if (trx_rseg_t* next
= trx_sys->rseg_array[slot]) {
if (next->space != TRX_SYS_SPACE
&& srv_undo_tablespaces > 0) {
/** If dedicated
innodb_undo_tablespaces have
been configured, try to use them
instead of the system tablespace. */
continue;
}
}
break;
}
......@@ -1191,129 +1175,43 @@ get_next_redo_rseg(
allocated = true;
}
mutex_exit(&rseg->mutex);
}
} while (!allocated);
ut_ad(rseg->trx_ref_count > 0);
ut_ad(!trx_sys_is_noredo_rseg_slot(rseg->id));
return(rseg);
}
/******************************************************************//**
Get next noredo rollback segment.
@return assigned rollback segment instance */
static
trx_rseg_t*
get_next_noredo_rseg(
/*=================*/
ulong max_undo_logs) /*!< in: maximum number of UNDO logs to use */
{
trx_rseg_t* rseg;
static ulint noredo_rseg_slot = 1;
ulint slot = 0;
slot = noredo_rseg_slot++;
slot = slot % max_undo_logs;
while (!trx_sys_is_noredo_rseg_slot(slot)) {
slot = (slot + 1) % max_undo_logs;
}
for (;;) {
rseg = trx_sys->rseg_array[slot];
slot = (slot + 1) % max_undo_logs;
while (!trx_sys_is_noredo_rseg_slot(slot)) {
slot = (slot + 1) % max_undo_logs;
}
if (rseg != NULL) {
break;
}
}
ut_ad(fsp_is_system_temporary(rseg->space));
ut_ad(trx_sys_is_noredo_rseg_slot(rseg->id));
ut_ad(rseg->is_persistent());
return(rseg);
}
/******************************************************************//**
Assigns a rollback segment to a transaction in a round-robin fashion.
@return assigned rollback segment instance */
static
/** Assign a rollback segment for modifying temporary tables.
@return the assigned rollback segment */
trx_rseg_t*
trx_assign_rseg_low(
/*================*/
ulong max_undo_logs, /*!< in: maximum number of UNDO logs
to use */
ulint n_tablespaces, /*!< in: number of rollback
tablespaces */
trx_rseg_type_t rseg_type) /*!< in: type of rseg to assign. */
{
if (srv_read_only_mode) {
ut_a(max_undo_logs == ULONG_UNDEFINED);
return(NULL);
}
/* This breaks true round robin but that should be OK. */
ut_ad(max_undo_logs > 0 && max_undo_logs <= TRX_SYS_N_RSEGS);
/* Note: The assumption here is that there can't be any gaps in
the array. Once we implement more flexible rollback segment
management this may not hold. The assertion checks for that case. */
ut_ad(trx_sys->rseg_array[0] != NULL);
ut_ad(rseg_type == TRX_RSEG_TYPE_REDO
|| trx_sys->rseg_array[1] != NULL);
/* Slot-0 is always assigned to system-tablespace rseg. */
ut_ad(trx_sys->rseg_array[0]->space == srv_sys_space.space_id());
/* Slot-1 is always assigned to temp-tablespace rseg. */
ut_ad(rseg_type == TRX_RSEG_TYPE_REDO
|| fsp_is_system_temporary(trx_sys->rseg_array[1]->space));
trx_rseg_t* rseg = 0;
switch (rseg_type) {
case TRX_RSEG_TYPE_NONE:
ut_error;
case TRX_RSEG_TYPE_REDO:
rseg = get_next_redo_rseg(max_undo_logs, n_tablespaces);
break;
case TRX_RSEG_TYPE_NOREDO:
rseg = get_next_noredo_rseg(srv_tmp_undo_logs + 1);
break;
}
return(rseg);
}
/****************************************************************//**
Assign a transaction temp-tablespace bounded rollback-segment. */
void
trx_assign_rseg(
/*============*/
trx_t* trx) /*!< transaction that involves write
to temp-table. */
{
ut_a(trx->rsegs.m_noredo.rseg == 0);
ut_a(!trx_is_autocommit_non_locking(trx));
trx->rsegs.m_noredo.rseg = trx_assign_rseg_low(
srv_undo_logs, srv_undo_tablespaces, TRX_RSEG_TYPE_NOREDO);
if (trx->id == 0) {
trx_t::assign_temp_rseg()
{
ut_ad(!rsegs.m_noredo.rseg);
ut_ad(!trx_is_autocommit_non_locking(this));
compile_time_assert(ut_is_2pow(TRX_SYS_N_RSEGS));
/* Choose a temporary rollback segment between 0 and 127
in a round-robin fashion. Because rseg_slot is not protected by
atomics or any mutex, race conditions are possible, meaning that
multiple transactions that start modifications concurrently
will write their undo log to the same rollback segment. */
static ulong rseg_slot;
trx_rseg_t* rseg = trx_sys->temp_rsegs[
rseg_slot++ & (TRX_SYS_N_RSEGS - 1)];
ut_ad(!rseg->is_persistent());
rsegs.m_noredo.rseg = rseg;
if (id == 0) {
mutex_enter(&trx_sys->mutex);
trx->id = trx_sys_get_new_trx_id();
trx_sys->rw_trx_ids.push_back(trx->id);
trx_sys->rw_trx_set.insert(TrxTrack(trx->id, trx));
id = trx_sys_get_new_trx_id();
trx_sys->rw_trx_ids.push_back(id);
trx_sys->rw_trx_set.insert(TrxTrack(id, this));
mutex_exit(&trx_sys->mutex);
}
ut_ad(!rseg->is_persistent());
return(rseg);
}
/****************************************************************//**
......@@ -1388,9 +1286,7 @@ trx_start_low(
if (!trx->read_only
&& (trx->mysql_thd == 0 || read_write || trx->ddl)) {
trx->rsegs.m_redo.rseg = trx_assign_rseg_low(
srv_undo_logs, srv_undo_tablespaces,
TRX_RSEG_TYPE_REDO);
trx->rsegs.m_redo.rseg = trx_assign_rseg_low();
/* Temporary rseg is assigned only if the transaction
updates a temporary table */
......@@ -2969,8 +2865,6 @@ trx_start_if_not_started_xa_low(
trx_sys_t::rw_trx_list. */
if (!trx->read_only) {
trx_set_rw_mode(trx);
} else if (!srv_read_only_mode) {
trx_assign_rseg(trx);
}
}
return;
......@@ -3116,8 +3010,7 @@ trx_set_rw_mode(
that both threads are synced by acquring trx->mutex to avoid decision
based on in-consistent view formed during promotion. */
trx->rsegs.m_redo.rseg = trx_assign_rseg_low(
srv_undo_logs, srv_undo_tablespaces, TRX_RSEG_TYPE_REDO);
trx->rsegs.m_redo.rseg = trx_assign_rseg_low();
ut_ad(trx->rsegs.m_redo.rseg != 0);
......
......@@ -1027,7 +1027,7 @@ void
trx_undo_truncate_end(trx_undo_t* undo, undo_no_t limit, bool is_temp)
{
ut_ad(mutex_own(&undo->rseg->mutex));
ut_ad(is_temp == trx_sys_is_noredo_rseg_slot(undo->rseg->id));
ut_ad(is_temp == !undo->rseg->is_persistent());
for (;;) {
mtr_t mtr;
......@@ -1102,7 +1102,7 @@ trx_undo_truncate_start(
loop:
mtr_start(&mtr);
if (trx_sys_is_noredo_rseg_slot(rseg->id)) {
if (!rseg->is_persistent()) {
mtr.set_log_mode(MTR_LOG_NO_REDO);
}
......@@ -1856,7 +1856,7 @@ void
trx_undo_commit_cleanup(trx_undo_t* undo, bool is_temp)
{
trx_rseg_t* rseg = undo->rseg;
ut_ad(is_temp == trx_sys_is_noredo_rseg_slot(rseg->id));
ut_ad(is_temp == !rseg->is_persistent());
mutex_enter(&rseg->mutex);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment