summaryrefslogtreecommitdiffstats
path: root/storage/innobase/trx/trx0rseg.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/trx/trx0rseg.cc')
-rw-r--r--storage/innobase/trx/trx0rseg.cc727
1 files changed, 727 insertions, 0 deletions
diff --git a/storage/innobase/trx/trx0rseg.cc b/storage/innobase/trx/trx0rseg.cc
new file mode 100644
index 00000000..8d1a381c
--- /dev/null
+++ b/storage/innobase/trx/trx0rseg.cc
@@ -0,0 +1,727 @@
+/*****************************************************************************
+
+Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2017, 2022, 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/trx0rseg.cc
+Rollback segment
+
+Created 3/26/1996 Heikki Tuuri
+*******************************************************/
+
+#include "trx0rseg.h"
+#include "trx0undo.h"
+#include "fut0lst.h"
+#include "srv0srv.h"
+#include "trx0purge.h"
+#include "srv0mon.h"
+#include "log.h"
+
+#ifdef WITH_WSREP
+# include <mysql/service_wsrep.h>
+
+/** The offset to WSREP XID headers, after TRX_RSEG */
+# define TRX_RSEG_WSREP_XID_INFO TRX_RSEG_MAX_TRX_ID + 16 + 512
+
+/** WSREP XID format (1 if present and valid, 0 if not present) */
+# define TRX_RSEG_WSREP_XID_FORMAT TRX_RSEG_WSREP_XID_INFO
+/** WSREP XID GTRID length */
+# define TRX_RSEG_WSREP_XID_GTRID_LEN TRX_RSEG_WSREP_XID_INFO + 4
+/** WSREP XID bqual length */
+# define TRX_RSEG_WSREP_XID_BQUAL_LEN TRX_RSEG_WSREP_XID_INFO + 8
+/** WSREP XID data (XIDDATASIZE bytes) */
+# define TRX_RSEG_WSREP_XID_DATA TRX_RSEG_WSREP_XID_INFO + 12
+
+# ifdef UNIV_DEBUG
+/** The latest known WSREP XID sequence number */
+static long long wsrep_seqno = -1;
+# endif /* UNIV_DEBUG */
+/** The latest known WSREP XID UUID */
+static unsigned char wsrep_uuid[16];
+
+/** Write the WSREP XID information into rollback segment header.
+@param[in,out] rseg_header rollback segment header
+@param[in] xid WSREP XID
+@param[in,out] mtr mini transaction */
+static void
+trx_rseg_write_wsrep_checkpoint(
+ buf_block_t* rseg_header,
+ const XID* xid,
+ mtr_t* mtr)
+{
+ DBUG_ASSERT(xid->gtrid_length >= 0);
+ DBUG_ASSERT(xid->bqual_length >= 0);
+ DBUG_ASSERT(xid->gtrid_length + xid->bqual_length < XIDDATASIZE);
+
+ mtr->write<4,mtr_t::MAYBE_NOP>(*rseg_header,
+ TRX_RSEG + TRX_RSEG_WSREP_XID_FORMAT
+ + rseg_header->page.frame,
+ uint32_t(xid->formatID));
+
+ mtr->write<4,mtr_t::MAYBE_NOP>(*rseg_header,
+ TRX_RSEG + TRX_RSEG_WSREP_XID_GTRID_LEN
+ + rseg_header->page.frame,
+ uint32_t(xid->gtrid_length));
+
+ mtr->write<4,mtr_t::MAYBE_NOP>(*rseg_header,
+ TRX_RSEG + TRX_RSEG_WSREP_XID_BQUAL_LEN
+ + rseg_header->page.frame,
+ uint32_t(xid->bqual_length));
+
+ const ulint xid_length = static_cast<ulint>(xid->gtrid_length
+ + xid->bqual_length);
+ mtr->memcpy<mtr_t::MAYBE_NOP>(*rseg_header,
+ TRX_RSEG + TRX_RSEG_WSREP_XID_DATA
+ + rseg_header->page.frame,
+ xid->data, xid_length);
+ if (xid_length < XIDDATASIZE
+ && memcmp(TRX_RSEG + TRX_RSEG_WSREP_XID_DATA
+ + rseg_header->page.frame, field_ref_zero,
+ XIDDATASIZE - xid_length)) {
+ mtr->memset(rseg_header,
+ TRX_RSEG + TRX_RSEG_WSREP_XID_DATA + xid_length,
+ XIDDATASIZE - xid_length, 0);
+ }
+}
+
+/** Update the WSREP XID information in rollback segment header.
+@param[in,out] rseg_header rollback segment header
+@param[in] xid WSREP XID
+@param[in,out] mtr mini-transaction */
+void
+trx_rseg_update_wsrep_checkpoint(
+ buf_block_t* rseg_header,
+ const XID* xid,
+ mtr_t* mtr)
+{
+ ut_ad(wsrep_is_wsrep_xid(xid));
+
+#ifdef UNIV_DEBUG
+ /* Check that seqno is monotonically increasing */
+ long long xid_seqno = wsrep_xid_seqno(xid);
+ const byte* xid_uuid = wsrep_xid_uuid(xid);
+
+ if (xid_seqno != -1
+ && !memcmp(xid_uuid, wsrep_uuid, sizeof wsrep_uuid)) {
+ ut_ad(xid_seqno > wsrep_seqno);
+ } else {
+ memcpy(wsrep_uuid, xid_uuid, sizeof wsrep_uuid);
+ }
+ wsrep_seqno = xid_seqno;
+#endif /* UNIV_DEBUG */
+ trx_rseg_write_wsrep_checkpoint(rseg_header, xid, mtr);
+}
+
+static dberr_t trx_rseg_update_wsrep_checkpoint(const XID* xid, mtr_t* mtr)
+{
+ dberr_t err;
+ buf_block_t *rseg_header = trx_sys.rseg_array[0].get(mtr, &err);
+
+ if (UNIV_UNLIKELY(!rseg_header))
+ return err;
+
+ /* We must make check against wsrep_uuid here, the
+ trx_rseg_update_wsrep_checkpoint() writes over wsrep_uuid with xid
+ contents in debug mode and the memcmp() will never give nonzero
+ result. */
+ const bool must_clear_rsegs=
+ memcmp(wsrep_uuid, wsrep_xid_uuid(xid), sizeof wsrep_uuid);
+
+ if (UNIV_UNLIKELY(mach_read_from_4(TRX_RSEG + TRX_RSEG_FORMAT +
+ rseg_header->page.frame)))
+ trx_rseg_format_upgrade(rseg_header, mtr);
+
+ trx_rseg_update_wsrep_checkpoint(rseg_header, xid, mtr);
+
+ if (must_clear_rsegs)
+ /* Because the UUID part of the WSREP XID differed from
+ current_xid_uuid, the WSREP group UUID was changed, and we must
+ reset the XID in all rollback segment headers. */
+ for (ulint rseg_id= 1; rseg_id < TRX_SYS_N_RSEGS; ++rseg_id)
+ if (buf_block_t* block= trx_sys.rseg_array[rseg_id].get(mtr, &err))
+ mtr->memset(block, TRX_RSEG + TRX_RSEG_WSREP_XID_INFO,
+ TRX_RSEG_WSREP_XID_DATA + XIDDATASIZE -
+ TRX_RSEG_WSREP_XID_INFO, 0);
+ return err;
+}
+
+/** Update WSREP checkpoint XID in first rollback segment header
+as part of wsrep_set_SE_checkpoint() when it is guaranteed that there
+are no wsrep transactions committing.
+If the UUID part of the WSREP XID does not match to the UUIDs of XIDs already
+stored into rollback segments, the WSREP XID in all the remaining rollback
+segments will be reset.
+@param[in] xid WSREP XID */
+void trx_rseg_update_wsrep_checkpoint(const XID* xid)
+{
+ mtr_t mtr;
+ mtr.start();
+ trx_rseg_update_wsrep_checkpoint(xid, &mtr);
+ mtr.commit();
+}
+
+/** Read the WSREP XID information in rollback segment header.
+@param[in] rseg_header Rollback segment header
+@param[out] xid Transaction XID
+@return whether the WSREP XID was present */
+static
+bool trx_rseg_read_wsrep_checkpoint(const buf_block_t *rseg_header, XID &xid)
+{
+ int formatID = static_cast<int>(
+ mach_read_from_4(TRX_RSEG + TRX_RSEG_WSREP_XID_FORMAT
+ + rseg_header->page.frame));
+ if (formatID == 0) {
+ return false;
+ }
+
+ xid.formatID = formatID;
+ xid.gtrid_length = static_cast<int>(
+ mach_read_from_4(TRX_RSEG + TRX_RSEG_WSREP_XID_GTRID_LEN
+ + rseg_header->page.frame));
+
+ xid.bqual_length = static_cast<int>(
+ mach_read_from_4(TRX_RSEG + TRX_RSEG_WSREP_XID_BQUAL_LEN
+ + rseg_header->page.frame));
+
+ memcpy(xid.data, TRX_RSEG + TRX_RSEG_WSREP_XID_DATA
+ + rseg_header->page.frame, XIDDATASIZE);
+
+ return true;
+}
+
+/** Read the WSREP XID from the TRX_SYS page (in case of upgrade).
+@param[in] page TRX_SYS page
+@param[out] xid WSREP XID (if present)
+@return whether the WSREP XID is present */
+static bool trx_rseg_init_wsrep_xid(const page_t* page, XID& xid)
+{
+ if (mach_read_from_4(TRX_SYS + TRX_SYS_WSREP_XID_INFO
+ + TRX_SYS_WSREP_XID_MAGIC_N_FLD
+ + page)
+ != TRX_SYS_WSREP_XID_MAGIC_N) {
+ return false;
+ }
+
+ xid.formatID = static_cast<int>(
+ mach_read_from_4(
+ TRX_SYS + TRX_SYS_WSREP_XID_INFO
+ + TRX_SYS_WSREP_XID_FORMAT + page));
+ xid.gtrid_length = static_cast<int>(
+ mach_read_from_4(
+ TRX_SYS + TRX_SYS_WSREP_XID_INFO
+ + TRX_SYS_WSREP_XID_GTRID_LEN + page));
+ xid.bqual_length = static_cast<int>(
+ mach_read_from_4(
+ TRX_SYS + TRX_SYS_WSREP_XID_INFO
+ + TRX_SYS_WSREP_XID_BQUAL_LEN + page));
+ memcpy(xid.data,
+ TRX_SYS + TRX_SYS_WSREP_XID_INFO
+ + TRX_SYS_WSREP_XID_DATA + page, XIDDATASIZE);
+ return true;
+}
+
+/** Recover the latest WSREP checkpoint XID.
+@param[out] xid WSREP XID
+@return whether the WSREP XID was found */
+bool trx_rseg_read_wsrep_checkpoint(XID& xid)
+{
+ mtr_t mtr;
+ long long max_xid_seqno = -1;
+ bool found = false;
+
+ for (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS;
+ rseg_id++, mtr.commit()) {
+ mtr.start();
+ const buf_block_t* sys = trx_sysf_get(&mtr, false);
+ if (UNIV_UNLIKELY(!sys)) {
+ continue;
+ }
+ const uint32_t page_no = trx_sysf_rseg_get_page_no(
+ sys, rseg_id);
+
+ if (page_no == FIL_NULL) {
+ continue;
+ }
+
+ const buf_block_t* rseg_header = buf_page_get_gen(
+ page_id_t(trx_sysf_rseg_get_space(sys, rseg_id),
+ page_no),
+ 0, RW_S_LATCH, nullptr, BUF_GET, &mtr);
+
+ if (!rseg_header) {
+ continue;
+ }
+
+ if (mach_read_from_4(TRX_RSEG + TRX_RSEG_FORMAT
+ + rseg_header->page.frame)) {
+ continue;
+ }
+
+ XID tmp_xid;
+ long long tmp_seqno = 0;
+ if (trx_rseg_read_wsrep_checkpoint(rseg_header, tmp_xid)
+ && (tmp_seqno = wsrep_xid_seqno(&tmp_xid))
+ > max_xid_seqno) {
+ found = true;
+ max_xid_seqno = tmp_seqno;
+ xid = tmp_xid;
+ memcpy(wsrep_uuid, wsrep_xid_uuid(&tmp_xid),
+ sizeof wsrep_uuid);
+ }
+ }
+
+ return found;
+}
+#endif /* WITH_WSREP */
+
+buf_block_t *trx_rseg_t::get(mtr_t *mtr, dberr_t *err) const
+{
+ if (!space)
+ {
+ if (err) *err= DB_TABLESPACE_NOT_FOUND;
+ return nullptr;
+ }
+ return buf_page_get_gen(page_id(), 0, RW_X_LATCH, nullptr,
+ BUF_GET, mtr, err);
+}
+
+/** Upgrade a rollback segment header page to MariaDB 10.3 format.
+@param[in,out] rseg_header rollback segment header page
+@param[in,out] mtr mini-transaction */
+void trx_rseg_format_upgrade(buf_block_t *rseg_header, mtr_t *mtr)
+{
+ mtr->memset(rseg_header, TRX_RSEG + TRX_RSEG_FORMAT, 4, 0);
+ /* Clear also possible garbage at the end of the page. Old
+ InnoDB versions did not initialize unused parts of pages. */
+ mtr->memset(rseg_header, TRX_RSEG + TRX_RSEG_MAX_TRX_ID + 8,
+ srv_page_size
+ - (FIL_PAGE_DATA_END + TRX_RSEG + TRX_RSEG_MAX_TRX_ID + 8),
+ 0);
+}
+
+/** Create a rollback segment header.
+@param[in,out] space system, undo, or temporary tablespace
+@param[in] rseg_id rollback segment identifier
+@param[in] max_trx_id new value of TRX_RSEG_MAX_TRX_ID
+@param[in,out] mtr mini-transaction
+@param[out] err error code
+@return the created rollback segment
+@retval nullptr on failure */
+buf_block_t *trx_rseg_header_create(fil_space_t *space, ulint rseg_id,
+ trx_id_t max_trx_id, mtr_t *mtr,
+ dberr_t *err)
+{
+ ut_ad(mtr->memo_contains(*space));
+ buf_block_t *block=
+ fseg_create(space, TRX_RSEG + TRX_RSEG_FSEG_HEADER, mtr, err);
+ if (block)
+ {
+ ut_ad(0 == mach_read_from_4(TRX_RSEG_FORMAT + TRX_RSEG +
+ block->page.frame));
+ ut_ad(0 == mach_read_from_4(TRX_RSEG_HISTORY_SIZE + TRX_RSEG +
+ block->page.frame));
+ ut_ad(0 == mach_read_from_4(TRX_RSEG_MAX_TRX_ID + TRX_RSEG +
+ block->page.frame));
+
+ /* Initialize the history list */
+ flst_init(block, TRX_RSEG_HISTORY + TRX_RSEG, mtr);
+
+ mtr->write<8,mtr_t::MAYBE_NOP>(*block, TRX_RSEG + TRX_RSEG_MAX_TRX_ID +
+ block->page.frame, max_trx_id);
+
+ /* Reset the undo log slots */
+ mtr->memset(block, TRX_RSEG_UNDO_SLOTS + TRX_RSEG, TRX_RSEG_N_SLOTS * 4,
+ 0xff);
+ }
+ return block;
+}
+
+void trx_rseg_t::destroy()
+{
+ latch.destroy();
+
+ /* There can't be any active transactions. */
+ ut_a(!UT_LIST_GET_LEN(undo_list));
+
+ for (trx_undo_t *next, *undo= UT_LIST_GET_FIRST(undo_cached); undo;
+ undo= next)
+ {
+ next= UT_LIST_GET_NEXT(undo_list, undo);
+ UT_LIST_REMOVE(undo_cached, undo);
+ ut_free(undo);
+ }
+}
+
+void trx_rseg_t::init(fil_space_t *space, uint32_t page)
+{
+ latch.SRW_LOCK_INIT(trx_rseg_latch_key);
+ ut_ad(!this->space || this->space != space);
+ this->space= space;
+ page_no= page;
+ last_page_no= FIL_NULL;
+ curr_size= 1;
+
+ UT_LIST_INIT(undo_list, &trx_undo_t::undo_list);
+ UT_LIST_INIT(undo_cached, &trx_undo_t::undo_list);
+}
+
+void trx_rseg_t::reinit(uint32_t page)
+{
+ ut_ad(is_persistent());
+ ut_ad(page_no == page);
+ ut_a(!UT_LIST_GET_LEN(undo_list));
+ ut_ad(!history_size || UT_LIST_GET_FIRST(undo_cached));
+
+ history_size= 0;
+ page_no= page;
+
+ for (trx_undo_t *next, *undo= UT_LIST_GET_FIRST(undo_cached); undo;
+ undo= next)
+ {
+ next= UT_LIST_GET_NEXT(undo_list, undo);
+ UT_LIST_REMOVE(undo_cached, undo);
+ ut_free(undo);
+ }
+
+ ut_ad(!is_referenced());
+ needs_purge= 0;
+ last_commit_and_offset= 0;
+ last_page_no= FIL_NULL;
+ curr_size= 1;
+ ref.store(0, std::memory_order_release);
+}
+
+/** Read the undo log lists.
+@param[in,out] rseg rollback segment
+@param[in] rseg_header rollback segment header
+@return error code */
+static dberr_t trx_undo_lists_init(trx_rseg_t *rseg,
+ const buf_block_t *rseg_header)
+{
+ ut_ad(srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN);
+ bool is_undo_empty= true;
+
+ for (ulint i= 0; i < TRX_RSEG_N_SLOTS; i++)
+ {
+ uint32_t page_no= trx_rsegf_get_nth_undo(rseg_header, i);
+ if (page_no != FIL_NULL)
+ {
+ const trx_undo_t *undo=
+ trx_undo_mem_create_at_db_start(rseg, i, page_no);
+ if (!undo)
+ return DB_CORRUPTION;
+ if (is_undo_empty)
+ is_undo_empty= !undo->size || undo->state == TRX_UNDO_CACHED;
+ rseg->curr_size+= undo->size;
+ }
+ }
+
+ trx_sys.set_undo_non_empty(!is_undo_empty);
+ return DB_SUCCESS;
+}
+
+/** Restore the state of a persistent rollback segment.
+@param[in,out] rseg persistent rollback segment
+@param[in,out] mtr mini-transaction
+@return error code */
+static dberr_t trx_rseg_mem_restore(trx_rseg_t *rseg, mtr_t *mtr)
+{
+ if (!rseg->space)
+ return DB_TABLESPACE_NOT_FOUND;
+ dberr_t err;
+ const buf_block_t *rseg_hdr=
+ buf_page_get_gen(rseg->page_id(), 0, RW_S_LATCH, nullptr, BUF_GET, mtr,
+ &err);
+ if (!rseg_hdr)
+ return err;
+
+ if (!mach_read_from_4(TRX_RSEG + TRX_RSEG_FORMAT + rseg_hdr->page.frame))
+ {
+ trx_id_t id= mach_read_from_8(TRX_RSEG + TRX_RSEG_MAX_TRX_ID +
+ rseg_hdr->page.frame);
+
+ if (id > rseg->needs_purge)
+ rseg->needs_purge= id;
+
+ const byte *binlog_name=
+ TRX_RSEG + TRX_RSEG_BINLOG_NAME + rseg_hdr->page.frame;
+ if (*binlog_name)
+ {
+ lsn_t lsn= mach_read_from_8(my_assume_aligned<8>
+ (FIL_PAGE_LSN + rseg_hdr->page.frame));
+ static_assert(TRX_RSEG_BINLOG_NAME_LEN ==
+ sizeof trx_sys.recovered_binlog_filename, "compatibility");
+ if (lsn > trx_sys.recovered_binlog_lsn)
+ {
+ trx_sys.recovered_binlog_lsn= lsn;
+ trx_sys.recovered_binlog_offset=
+ mach_read_from_8(TRX_RSEG + TRX_RSEG_BINLOG_OFFSET +
+ rseg_hdr->page.frame);
+ memcpy(trx_sys.recovered_binlog_filename, binlog_name,
+ TRX_RSEG_BINLOG_NAME_LEN);
+ }
+
+#ifdef WITH_WSREP
+ trx_rseg_read_wsrep_checkpoint(rseg_hdr, trx_sys.recovered_wsrep_xid);
+#endif
+ }
+ }
+
+ if (srv_operation == SRV_OPERATION_RESTORE)
+ /* mariabackup --prepare only deals with
+ the redo log and the data files, not with
+ transactions or the data dictionary. */
+ return DB_SUCCESS;
+
+ /* Initialize the undo log lists according to the rseg header */
+
+ rseg->curr_size = mach_read_from_4(TRX_RSEG + TRX_RSEG_HISTORY_SIZE +
+ rseg_hdr->page.frame) + 1;
+ err= trx_undo_lists_init(rseg, rseg_hdr);
+ if (err != DB_SUCCESS);
+ else if (auto len= flst_get_len(TRX_RSEG + TRX_RSEG_HISTORY +
+ rseg_hdr->page.frame))
+ {
+ rseg->history_size+= len;
+
+ fil_addr_t node_addr= flst_get_last(TRX_RSEG + TRX_RSEG_HISTORY +
+ rseg_hdr->page.frame);
+ node_addr.boffset= static_cast<uint16_t>(node_addr.boffset -
+ TRX_UNDO_HISTORY_NODE);
+ rseg->last_page_no= node_addr.page;
+
+ const buf_block_t* block=
+ buf_page_get_gen(page_id_t(rseg->space->id, node_addr.page),
+ 0, RW_S_LATCH, nullptr, BUF_GET, mtr, &err);
+ if (!block)
+ return err;
+
+ trx_id_t id= mach_read_from_8(block->page.frame + node_addr.boffset +
+ TRX_UNDO_TRX_ID);
+ if (id > rseg->needs_purge)
+ rseg->needs_purge= id;
+ id= mach_read_from_8(block->page.frame + node_addr.boffset +
+ TRX_UNDO_TRX_NO);
+ if (id > rseg->needs_purge)
+ rseg->needs_purge= id;
+
+ rseg->set_last_commit(node_addr.boffset, id);
+ ut_ad(mach_read_from_2(block->page.frame + node_addr.boffset +
+ TRX_UNDO_NEEDS_PURGE) <= 1);
+
+ if (rseg->last_page_no != FIL_NULL)
+ /* There is no need to cover this operation by the purge
+ mutex because we are still bootstrapping. */
+ purge_sys.purge_queue.push(*rseg);
+ }
+
+ trx_sys.set_undo_non_empty(rseg->history_size > 0);
+ return err;
+}
+
+/** Read binlog metadata from the TRX_SYS page, in case we are upgrading
+from MySQL or a MariaDB version older than 10.3.5. */
+static void trx_rseg_init_binlog_info(const page_t* page)
+{
+ if (mach_read_from_4(TRX_SYS + TRX_SYS_MYSQL_LOG_INFO
+ + TRX_SYS_MYSQL_LOG_MAGIC_N_FLD
+ + page)
+ == TRX_SYS_MYSQL_LOG_MAGIC_N) {
+ memcpy(trx_sys.recovered_binlog_filename,
+ TRX_SYS_MYSQL_LOG_INFO + TRX_SYS_MYSQL_LOG_NAME
+ + TRX_SYS + page, TRX_SYS_MYSQL_LOG_NAME_LEN);
+ trx_sys.recovered_binlog_offset = mach_read_from_8(
+ TRX_SYS_MYSQL_LOG_INFO + TRX_SYS_MYSQL_LOG_OFFSET
+ + TRX_SYS + page);
+ }
+
+#ifdef WITH_WSREP
+ trx_rseg_init_wsrep_xid(page, trx_sys.recovered_wsrep_xid);
+#endif
+}
+
+/** Initialize or recover the rollback segments at startup. */
+dberr_t trx_rseg_array_init()
+{
+ trx_id_t max_trx_id = 0;
+
+ *trx_sys.recovered_binlog_filename = '\0';
+ trx_sys.recovered_binlog_offset = 0;
+#ifdef WITH_WSREP
+ trx_sys.recovered_wsrep_xid.null();
+ XID wsrep_sys_xid;
+ wsrep_sys_xid.null();
+ bool wsrep_xid_in_rseg_found = false;
+#endif
+ mtr_t mtr;
+ dberr_t err = DB_SUCCESS;
+
+ for (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) {
+ mtr.start();
+ if (const buf_block_t* sys = trx_sysf_get(&mtr, false)) {
+ if (rseg_id == 0) {
+ /* In case this is an upgrade from
+ before MariaDB 10.3.5, fetch the base
+ information from the TRX_SYS page. */
+ max_trx_id = mach_read_from_8(
+ TRX_SYS + TRX_SYS_TRX_ID_STORE
+ + sys->page.frame);
+ trx_rseg_init_binlog_info(sys->page.frame);
+#ifdef WITH_WSREP
+ wsrep_sys_xid.set(&trx_sys.recovered_wsrep_xid);
+#endif
+ }
+
+ const uint32_t page_no = trx_sysf_rseg_get_page_no(
+ sys, rseg_id);
+ if (page_no != FIL_NULL) {
+ trx_rseg_t& rseg = trx_sys.rseg_array[rseg_id];
+ uint32_t space_id=
+ trx_sysf_rseg_get_space(
+ sys, rseg_id);
+
+ fil_space_t *rseg_space =
+ fil_space_get(space_id);
+ if (!rseg_space) {
+ mtr.commit();
+ err = DB_ERROR;
+ sql_print_error(
+ "InnoDB: Failed to open the undo "
+ "tablespace undo%03" PRIu32,
+ (space_id -
+ srv_undo_space_id_start + 1));
+ break;
+ }
+
+ rseg.init(rseg_space, page_no);
+ ut_ad(rseg.is_persistent());
+ err = trx_rseg_mem_restore(&rseg, &mtr);
+ if (rseg.needs_purge > max_trx_id) {
+ max_trx_id = rseg.needs_purge;
+ }
+ if (err != DB_SUCCESS) {
+ mtr.commit();
+ break;
+ }
+#ifdef WITH_WSREP
+ if (!wsrep_sys_xid.is_null() &&
+ !wsrep_sys_xid.eq(&trx_sys.recovered_wsrep_xid)) {
+ wsrep_xid_in_rseg_found = true;
+ ut_ad(memcmp(wsrep_xid_uuid(&wsrep_sys_xid),
+ wsrep_xid_uuid(&trx_sys.recovered_wsrep_xid),
+ sizeof wsrep_uuid)
+ || wsrep_xid_seqno(
+ &wsrep_sys_xid)
+ <= wsrep_xid_seqno(
+ &trx_sys.recovered_wsrep_xid));
+ }
+#endif
+ }
+ }
+
+ mtr.commit();
+ }
+
+ if (err != DB_SUCCESS) {
+ for (auto& rseg : trx_sys.rseg_array) {
+ while (auto u = UT_LIST_GET_FIRST(rseg.undo_list)) {
+ UT_LIST_REMOVE(rseg.undo_list, u);
+ ut_free(u);
+ }
+ }
+ return err;
+ }
+
+#ifdef WITH_WSREP
+ if (!wsrep_sys_xid.is_null()) {
+ /* Upgrade from a version prior to 10.3.5,
+ where WSREP XID was stored in TRX_SYS page.
+ If no rollback segment has a WSREP XID set,
+ we must copy the XID found in TRX_SYS page
+ to rollback segments. */
+ mtr.start();
+
+ if (!wsrep_xid_in_rseg_found) {
+ trx_rseg_update_wsrep_checkpoint(&wsrep_sys_xid, &mtr);
+ }
+
+ /* Finally, clear WSREP XID in TRX_SYS page. */
+ mtr.memset(trx_sysf_get(&mtr),
+ TRX_SYS + TRX_SYS_WSREP_XID_INFO,
+ TRX_SYS_WSREP_XID_LEN, 0);
+ mtr.commit();
+ }
+#endif
+
+ trx_sys.init_max_trx_id(max_trx_id + 1);
+ return DB_SUCCESS;
+}
+
+/** Create the temporary rollback segments. */
+dberr_t trx_temp_rseg_create(mtr_t *mtr)
+{
+ for (ulong i= 0; i < array_elements(trx_sys.temp_rsegs); i++)
+ {
+ mtr->start();
+ mtr->set_log_mode(MTR_LOG_NO_REDO);
+ mtr->x_lock_space(fil_system.temp_space);
+ dberr_t err;
+ buf_block_t *rblock=
+ trx_rseg_header_create(fil_system.temp_space, i, 0, mtr, &err);
+ if (UNIV_UNLIKELY(!rblock))
+ {
+ mtr->commit();
+ return err;
+ }
+ trx_sys.temp_rsegs[i].init(fil_system.temp_space,
+ rblock->page.id().page_no());
+ mtr->commit();
+ }
+ return DB_SUCCESS;
+}
+
+/** Update the offset information about the end of the binlog entry
+which corresponds to the transaction just being committed.
+In a replication slave, this updates the master binlog position
+up to which replication has proceeded.
+@param[in,out] rseg_header rollback segment header
+@param[in] log_file_name binlog file name
+@param[in] log_offset binlog file offset
+@param[in,out] mtr mini-transaction */
+void trx_rseg_update_binlog_offset(buf_block_t *rseg_header,
+ const char *log_file_name,
+ ulonglong log_offset,
+ mtr_t *mtr)
+{
+ DBUG_PRINT("trx", ("trx_mysql_binlog_offset %llu", log_offset));
+ const size_t len= strlen(log_file_name) + 1;
+ ut_ad(len > 1);
+
+ if (UNIV_UNLIKELY(len > TRX_RSEG_BINLOG_NAME_LEN))
+ return;
+
+ mtr->write<8,mtr_t::MAYBE_NOP>(
+ *rseg_header,
+ TRX_RSEG + TRX_RSEG_BINLOG_OFFSET + rseg_header->page.frame,
+ log_offset);
+
+ byte *name= TRX_RSEG + TRX_RSEG_BINLOG_NAME + rseg_header->page.frame;
+
+ if (memcmp(log_file_name, name, len))
+ mtr->memcpy(*rseg_header, name, log_file_name, len);
+}