summaryrefslogtreecommitdiffstats
path: root/storage/innobase/trx/trx0sys.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/trx/trx0sys.cc')
-rw-r--r--storage/innobase/trx/trx0sys.cc339
1 files changed, 339 insertions, 0 deletions
diff --git a/storage/innobase/trx/trx0sys.cc b/storage/innobase/trx/trx0sys.cc
new file mode 100644
index 00000000..3064645f
--- /dev/null
+++ b/storage/innobase/trx/trx0sys.cc
@@ -0,0 +1,339 @@
+/*****************************************************************************
+
+Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2017, 2021, MariaDB Corporation.
+
+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, Fifth Floor, Boston, MA 02110-1335 USA
+
+*****************************************************************************/
+
+/**************************************************//**
+@file trx/trx0sys.cc
+Transaction system
+
+Created 3/26/1996 Heikki Tuuri
+*******************************************************/
+
+#include "trx0sys.h"
+#include "mysqld.h"
+#include "sql_error.h"
+
+#include "fsp0fsp.h"
+#include "mtr0log.h"
+#include "mtr0log.h"
+#include "trx0trx.h"
+#include "trx0rseg.h"
+#include "trx0undo.h"
+#include "srv0srv.h"
+#include "srv0start.h"
+#include "trx0purge.h"
+#include "log0log.h"
+#include "log0recv.h"
+#include "os0file.h"
+
+/** The transaction system */
+trx_sys_t trx_sys;
+
+/** Check whether transaction id is valid.
+@param[in] id transaction id to check
+@param[in] name table name */
+void
+ReadViewBase::check_trx_id_sanity(
+ trx_id_t id,
+ const table_name_t& name)
+{
+ if (id >= trx_sys.get_max_trx_id()) {
+
+ ib::warn() << "A transaction id"
+ << " in a record of table "
+ << name
+ << " is newer than the"
+ << " system-wide maximum.";
+ ut_ad(0);
+ THD *thd = current_thd;
+ if (thd != NULL) {
+ char table_name[MAX_FULL_NAME_LEN + 1];
+
+ innobase_format_name(
+ table_name, sizeof(table_name),
+ name.m_name);
+
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_SIGNAL_WARN,
+ "InnoDB: Transaction id"
+ " in a record of table"
+ " %s is newer than system-wide"
+ " maximum.", table_name);
+ }
+ }
+}
+
+#ifdef UNIV_DEBUG
+/* Flag to control TRX_RSEG_N_SLOTS behavior debugging. */
+uint trx_rseg_n_slots_debug = 0;
+#endif
+
+/** Display the MySQL binlog offset info if it is present in the trx
+system header. */
+void
+trx_sys_print_mysql_binlog_offset()
+{
+ if (!*trx_sys.recovered_binlog_filename) {
+ return;
+ }
+
+ ib::info() << "Last binlog file '"
+ << trx_sys.recovered_binlog_filename
+ << "', position "
+ << trx_sys.recovered_binlog_offset;
+}
+
+/** Find an available rollback segment.
+@param[in] sys_header
+@return an unallocated rollback segment slot in the TRX_SYS header
+@retval ULINT_UNDEFINED if not found */
+ulint
+trx_sys_rseg_find_free(const buf_block_t* sys_header)
+{
+ for (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) {
+ if (trx_sysf_rseg_get_page_no(sys_header, rseg_id)
+ == FIL_NULL) {
+ return rseg_id;
+ }
+ }
+
+ return(ULINT_UNDEFINED);
+}
+
+/** Count the number of initialized persistent rollback segment slots. */
+static
+void
+trx_sysf_get_n_rseg_slots()
+{
+ mtr_t mtr;
+ mtr.start();
+
+ srv_available_undo_logs = 0;
+ if (const buf_block_t* sys_header = trx_sysf_get(&mtr, false)) {
+ for (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) {
+ srv_available_undo_logs
+ += trx_sysf_rseg_get_page_no(sys_header,
+ rseg_id)
+ != FIL_NULL;
+ }
+ }
+
+ mtr.commit();
+}
+
+/*****************************************************************//**
+Creates the file page for the transaction system. This function is called only
+at the database creation, before trx_sys_init. */
+static
+void
+trx_sysf_create(
+/*============*/
+ mtr_t* mtr) /*!< in: mtr */
+{
+ ulint slot_no;
+ buf_block_t* block;
+
+ ut_ad(mtr);
+
+ /* Note that below we first reserve the file space x-latch, and
+ then enter the kernel: we must do it in this order to conform
+ to the latching order rules. */
+
+ mtr_x_lock_space(fil_system.sys_space, mtr);
+ compile_time_assert(TRX_SYS_SPACE == 0);
+
+ /* Create the trx sys file block in a new allocated file segment */
+ block = fseg_create(fil_system.sys_space,
+ TRX_SYS + TRX_SYS_FSEG_HEADER,
+ mtr);
+ buf_block_dbg_add_level(block, SYNC_TRX_SYS_HEADER);
+
+ ut_a(block->page.id() == page_id_t(0, TRX_SYS_PAGE_NO));
+
+ mtr->write<2>(*block, FIL_PAGE_TYPE + block->frame,
+ FIL_PAGE_TYPE_TRX_SYS);
+
+ ut_ad(!mach_read_from_4(block->frame
+ + TRX_SYS_DOUBLEWRITE
+ + TRX_SYS_DOUBLEWRITE_MAGIC));
+
+ /* Reset the rollback segment slots. Old versions of InnoDB
+ (before MySQL 5.5) define TRX_SYS_N_RSEGS as 256 and expect
+ that the whole array is initialized. */
+ compile_time_assert(256 >= TRX_SYS_N_RSEGS);
+ compile_time_assert(TRX_SYS + TRX_SYS_RSEGS
+ + 256 * TRX_SYS_RSEG_SLOT_SIZE
+ <= UNIV_PAGE_SIZE_MIN - FIL_PAGE_DATA_END);
+ mtr->memset(block, TRX_SYS + TRX_SYS_RSEGS,
+ 256 * TRX_SYS_RSEG_SLOT_SIZE, 0xff);
+ /* Initialize all of the page. This part used to be uninitialized. */
+ mtr->memset(block, TRX_SYS + TRX_SYS_RSEGS
+ + 256 * TRX_SYS_RSEG_SLOT_SIZE,
+ srv_page_size
+ - (FIL_PAGE_DATA_END + TRX_SYS + TRX_SYS_RSEGS
+ + 256 * TRX_SYS_RSEG_SLOT_SIZE),
+ 0);
+
+ /* Create the first rollback segment in the SYSTEM tablespace */
+ slot_no = trx_sys_rseg_find_free(block);
+ buf_block_t* rblock = trx_rseg_header_create(fil_system.sys_space,
+ slot_no, block, mtr);
+
+ ut_a(slot_no == TRX_SYS_SYSTEM_RSEG_ID);
+ ut_a(rblock->page.id() == page_id_t(0, FSP_FIRST_RSEG_PAGE_NO));
+}
+
+/** Create the instance */
+void
+trx_sys_t::create()
+{
+ ut_ad(this == &trx_sys);
+ ut_ad(!is_initialised());
+ m_initialised = true;
+ trx_list.create();
+ rseg_history_len= 0;
+
+ rw_trx_hash.init();
+}
+
+/*****************************************************************//**
+Creates and initializes the transaction system at the database creation. */
+void
+trx_sys_create_sys_pages(void)
+/*==========================*/
+{
+ mtr_t mtr;
+
+ mtr_start(&mtr);
+
+ trx_sysf_create(&mtr);
+
+ mtr_commit(&mtr);
+}
+
+/** Create the rollback segments.
+@return whether the creation succeeded */
+bool
+trx_sys_create_rsegs()
+{
+ /* srv_available_undo_logs reflects the number of persistent
+ rollback segments that have been initialized in the
+ transaction system header page. */
+ ut_ad(srv_undo_tablespaces <= TRX_SYS_MAX_UNDO_SPACES);
+
+ if (high_level_read_only) {
+ srv_available_undo_logs = 0;
+ return(true);
+ }
+
+ /* 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. */
+ trx_sysf_get_n_rseg_slots();
+
+ ut_ad(srv_available_undo_logs <= TRX_SYS_N_RSEGS);
+
+ /* The first persistent rollback segment is always initialized
+ in the system tablespace. */
+ ut_a(srv_available_undo_logs > 0);
+
+ for (ulint i = 0; srv_available_undo_logs < TRX_SYS_N_RSEGS;
+ 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)
+ + srv_undo_space_id_start
+ : TRX_SYS_SPACE;
+
+ if (!trx_rseg_create(space)) {
+ ib::error() << "Unable to allocate the"
+ " requested innodb_undo_logs";
+ return(false);
+ }
+
+ /* Increase the number of active undo
+ tablespace in case new rollback segment
+ assigned to new undo tablespace. */
+ if (space > srv_undo_tablespaces_active) {
+ srv_undo_tablespaces_active++;
+
+ ut_ad(srv_undo_tablespaces_active == space);
+ }
+ }
+
+ ut_ad(srv_available_undo_logs == TRX_SYS_N_RSEGS);
+
+ ib::info info;
+ info << srv_available_undo_logs;
+ if (srv_undo_tablespaces_active) {
+ info << " rollback segments in " << srv_undo_tablespaces_active
+ << " undo tablespaces are active.";
+ } else {
+ info << " rollback segments are active.";
+ }
+
+ return(true);
+}
+
+/** Close the transaction system on shutdown */
+void
+trx_sys_t::close()
+{
+ ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS);
+ if (!is_initialised()) {
+ return;
+ }
+
+ if (size_t size = view_count()) {
+ ib::error() << "All read views were not closed before"
+ " shutdown: " << size << " read views open";
+ }
+
+ rw_trx_hash.destroy();
+
+ /* There can't be any active transactions. */
+
+ for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) {
+ if (trx_rseg_t* rseg = rseg_array[i]) {
+ trx_rseg_mem_free(rseg);
+ }
+
+ if (trx_rseg_t* rseg = temp_rsegs[i]) {
+ trx_rseg_mem_free(rseg);
+ }
+ }
+
+ ut_a(trx_list.empty());
+ trx_list.close();
+ m_initialised = false;
+}
+
+/** @return total number of active (non-prepared) transactions */
+ulint trx_sys_t::any_active_transactions()
+{
+ uint32_t total_trx= 0;
+
+ trx_sys.trx_list.for_each([&total_trx](const trx_t &trx) {
+ if (trx.state == TRX_STATE_COMMITTED_IN_MEMORY ||
+ (trx.state == TRX_STATE_ACTIVE && trx.id))
+ total_trx++;
+ });
+
+ return total_trx;
+}