diff options
Diffstat (limited to 'storage/innobase/trx/trx0rseg.cc')
-rw-r--r-- | storage/innobase/trx/trx0rseg.cc | 768 |
1 files changed, 768 insertions, 0 deletions
diff --git a/storage/innobase/trx/trx0rseg.cc b/storage/innobase/trx/trx0rseg.cc new file mode 100644 index 00000000..307f8757 --- /dev/null +++ b/storage/innobase/trx/trx0rseg.cc @@ -0,0 +1,768 @@ +/***************************************************************************** + +Copyright (c) 1996, 2016, 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/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" + +#ifdef WITH_WSREP +#include <mysql/service_wsrep.h> + +#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->frame, + uint32_t(xid->formatID)); + + mtr->write<4,mtr_t::MAYBE_NOP>(*rseg_header, + TRX_RSEG + TRX_RSEG_WSREP_XID_GTRID_LEN + + rseg_header->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->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->frame, + xid->data, xid_length); + if (xid_length < XIDDATASIZE + && memcmp(TRX_RSEG + TRX_RSEG_WSREP_XID_DATA + + rseg_header->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); +} + +/** Clear the WSREP XID information from rollback segment header. +@param[in,out] block rollback segment header +@param[in,out] mtr mini-transaction */ +static void trx_rseg_clear_wsrep_checkpoint(buf_block_t *block, mtr_t *mtr) +{ + mtr->memset(block, TRX_RSEG + TRX_RSEG_WSREP_XID_INFO, + TRX_RSEG_WSREP_XID_DATA + XIDDATASIZE - TRX_RSEG_WSREP_XID_INFO, + 0); +} + +static void +trx_rseg_update_wsrep_checkpoint(const XID* xid, mtr_t* mtr) +{ + const byte* xid_uuid = wsrep_xid_uuid(xid); + /* 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, xid_uuid, + sizeof wsrep_uuid); + const trx_rseg_t* rseg = trx_sys.rseg_array[0]; + + buf_block_t* rseg_header = trx_rsegf_get(rseg->space, rseg->page_no, + mtr); + if (UNIV_UNLIKELY(mach_read_from_4(TRX_RSEG + TRX_RSEG_FORMAT + + rseg_header->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 (const trx_rseg_t* rseg = + trx_sys.rseg_array[rseg_id]) { + trx_rseg_clear_wsrep_checkpoint( + trx_rsegf_get(rseg->space, + rseg->page_no, mtr), + mtr); + } + } + } +} + +/** 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->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->frame)); + + xid.bqual_length = static_cast<int>( + mach_read_from_4(TRX_RSEG + TRX_RSEG_WSREP_XID_BQUAL_LEN + + rseg_header->frame)); + + memcpy(xid.data, TRX_RSEG + TRX_RSEG_WSREP_XID_DATA + + rseg_header->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); + 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 = trx_rsegf_get_new( + trx_sysf_rseg_get_space(sys, rseg_id), page_no, &mtr); + + if (mach_read_from_4(TRX_RSEG + TRX_RSEG_FORMAT + + rseg_header->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 */ + +/** 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,out] sys_header the TRX_SYS page (NULL for temporary rseg) +@param[in,out] mtr mini-transaction +@return the created rollback segment +@retval NULL on failure */ +buf_block_t* +trx_rseg_header_create( + fil_space_t* space, + ulint rseg_id, + buf_block_t* sys_header, + mtr_t* mtr) +{ + buf_block_t* block; + + ut_ad(mtr->memo_contains(*space)); + ut_ad(!sys_header == (space == fil_system.temp_space)); + + /* Allocate a new file segment for the rollback segment */ + block = fseg_create(space, TRX_RSEG + TRX_RSEG_FSEG_HEADER, mtr); + + if (block == NULL) { + /* No space left */ + return block; + } + + buf_block_dbg_add_level(block, SYNC_RSEG_HEADER_NEW); + + ut_ad(0 == mach_read_from_4(TRX_RSEG_FORMAT + TRX_RSEG + + block->frame)); + ut_ad(0 == mach_read_from_4(TRX_RSEG_HISTORY_SIZE + TRX_RSEG + + block->frame)); + + /* Initialize the history list */ + flst_init(block, TRX_RSEG_HISTORY + TRX_RSEG, mtr); + + /* Reset the undo log slots */ + mtr->memset(block, TRX_RSEG_UNDO_SLOTS + TRX_RSEG, + TRX_RSEG_N_SLOTS * 4, 0xff); + + if (sys_header) { + /* Add the rollback segment info to the free slot in + the trx system header */ + + mtr->write<4,mtr_t::MAYBE_NOP>( + *sys_header, + TRX_SYS + TRX_SYS_RSEGS + TRX_SYS_RSEG_SPACE + + rseg_id * TRX_SYS_RSEG_SLOT_SIZE + + sys_header->frame, space->id); + mtr->write<4,mtr_t::MAYBE_NOP>( + *sys_header, + TRX_SYS + TRX_SYS_RSEGS + TRX_SYS_RSEG_PAGE_NO + + rseg_id * TRX_SYS_RSEG_SLOT_SIZE + + sys_header->frame, block->page.id().page_no()); + } + + return block; +} + +/** Free a rollback segment in memory. */ +void +trx_rseg_mem_free(trx_rseg_t* rseg) +{ + trx_undo_t* undo; + trx_undo_t* next_undo; + + mutex_free(&rseg->mutex); + + /* There can't be any active transactions. */ + ut_a(UT_LIST_GET_LEN(rseg->undo_list) == 0); + + for (undo = UT_LIST_GET_FIRST(rseg->undo_cached); + undo != NULL; + undo = next_undo) { + + next_undo = UT_LIST_GET_NEXT(undo_list, undo); + + UT_LIST_REMOVE(rseg->undo_cached, undo); + + MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED); + + ut_free(undo); + } + + ut_free(rseg); +} + +/** 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 */ +static +trx_rseg_t* +trx_rseg_mem_create(ulint id, fil_space_t* space, uint32_t page_no) +{ + 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->last_page_no = FIL_NULL; + rseg->curr_size = 1; + + mutex_create(rseg->is_persistent() + ? LATCH_ID_REDO_RSEG : LATCH_ID_NOREDO_RSEG, + &rseg->mutex); + + UT_LIST_INIT(rseg->undo_list, &trx_undo_t::undo_list); + UT_LIST_INIT(rseg->undo_cached, &trx_undo_t::undo_list); + + return(rseg); +} + +/** Read the undo log lists. +@param[in,out] rseg rollback segment +@param[in,out] max_trx_id maximum observed transaction identifier +@param[in] rseg_header rollback segment header +@return error code */ +static dberr_t trx_undo_lists_init(trx_rseg_t *rseg, trx_id_t &max_trx_id, + const buf_block_t *rseg_header) +{ + ut_ad(srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN); + + 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, + max_trx_id); + if (!undo) + return DB_CORRUPTION; + rseg->curr_size+= undo->size; + MONITOR_INC(MONITOR_NUM_UNDO_SLOT_USED); + } + } + + return DB_SUCCESS; +} + +/** Restore the state of a persistent rollback segment. +@param[in,out] rseg persistent rollback segment +@param[in,out] max_trx_id maximum observed transaction identifier +@param[in,out] mtr mini-transaction +@return error code */ +static dberr_t trx_rseg_mem_restore(trx_rseg_t *rseg, trx_id_t &max_trx_id, + mtr_t *mtr) +{ + buf_block_t* rseg_hdr = trx_rsegf_get_new( + rseg->space->id, rseg->page_no, mtr); + + if (!mach_read_from_4(TRX_RSEG + TRX_RSEG_FORMAT + rseg_hdr->frame)) { + trx_id_t id = mach_read_from_8(TRX_RSEG + TRX_RSEG_MAX_TRX_ID + + rseg_hdr->frame); + + if (id > max_trx_id) { + max_trx_id = id; + } + + const byte* binlog_name = TRX_RSEG + TRX_RSEG_BINLOG_NAME + + rseg_hdr->frame; + if (*binlog_name) { + lsn_t lsn = mach_read_from_8(my_assume_aligned<8>( + FIL_PAGE_LSN + + rseg_hdr + ->frame)); + compile_time_assert(TRX_RSEG_BINLOG_NAME_LEN == sizeof + trx_sys.recovered_binlog_filename); + 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->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->frame) + + 1; + if (dberr_t err = trx_undo_lists_init(rseg, max_trx_id, rseg_hdr)) { + return err; + } + + if (auto len = flst_get_len(TRX_RSEG + TRX_RSEG_HISTORY + + rseg_hdr->frame)) { + trx_sys.rseg_history_len += len; + + fil_addr_t node_addr = flst_get_last(TRX_RSEG + + TRX_RSEG_HISTORY + + rseg_hdr->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 = trx_undo_page_get( + page_id_t(rseg->space->id, node_addr.page), mtr); + + trx_id_t id = mach_read_from_8(block->frame + node_addr.boffset + + TRX_UNDO_TRX_ID); + if (id > max_trx_id) { + max_trx_id = id; + } + id = mach_read_from_8(block->frame + node_addr.boffset + + TRX_UNDO_TRX_NO); + if (id > max_trx_id) { + max_trx_id = id; + } + + rseg->set_last_commit(node_addr.boffset, id); + unsigned purge = mach_read_from_2(block->frame + + node_addr.boffset + + TRX_UNDO_NEEDS_PURGE); + ut_ad(purge <= 1); + rseg->needs_purge = purge != 0; + + 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); + } + } + + return DB_SUCCESS; +} + +/** 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->frame); + trx_rseg_init_binlog_info(sys->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_rseg_mem_create( + rseg_id, + fil_space_get(trx_sysf_rseg_get_space( + sys, rseg_id)), + page_no); + ut_ad(rseg->is_persistent()); + ut_ad(rseg->id == rseg_id); + ut_ad(!trx_sys.rseg_array[rseg_id]); + trx_sys.rseg_array[rseg_id] = rseg; + if ((err = trx_rseg_mem_restore( + rseg, max_trx_id, &mtr)) + != 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 (ulint rseg_id = 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) { + if (trx_rseg_t*& rseg = trx_sys.rseg_array[rseg_id]) { + while (trx_undo_t* u= UT_LIST_GET_FIRST( + rseg->undo_list)) { + UT_LIST_REMOVE(rseg->undo_list, u); + ut_free(u); + } + trx_rseg_mem_free(rseg); + rseg = NULL; + } + } + 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 a persistent rollback segment. +@param[in] space_id system or undo tablespace id +@return pointer to new rollback segment +@retval NULL on failure */ +trx_rseg_t* +trx_rseg_create(ulint space_id) +{ + trx_rseg_t* rseg = NULL; + mtr_t mtr; + + mtr.start(); + + fil_space_t* space = mtr_x_lock_space(space_id, &mtr); + ut_ad(space->purpose == FIL_TYPE_TABLESPACE); + + if (buf_block_t* sys_header = trx_sysf_get(&mtr)) { + ulint rseg_id = trx_sys_rseg_find_free(sys_header); + if (buf_block_t* rblock = rseg_id == ULINT_UNDEFINED + ? NULL + : trx_rseg_header_create(space, rseg_id, sys_header, + &mtr)) { + ut_ad(trx_sysf_rseg_get_space(sys_header, rseg_id) + == space_id); + rseg = trx_rseg_mem_create(rseg_id, space, + rblock->page.id(). + page_no()); + ut_ad(rseg->id == rseg_id); + ut_ad(rseg->is_persistent()); + ut_ad(!trx_sys.rseg_array[rseg->id]); + trx_sys.rseg_array[rseg->id] = rseg; + } + } + + mtr.commit(); + + 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); + mtr_x_lock_space(fil_system.temp_space, &mtr); + + buf_block_t* rblock = trx_rseg_header_create( + fil_system.temp_space, i, NULL, &mtr); + trx_rseg_t* rseg = trx_rseg_mem_create( + i, fil_system.temp_space, rblock->page.id().page_no()); + ut_ad(!rseg->is_persistent()); + ut_ad(!trx_sys.temp_rsegs[i]); + trx_sys.temp_rsegs[i] = rseg; + mtr.commit(); + } +} + +/** 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] trx committing transaction +@param[in,out] mtr mini-transaction */ +void trx_rseg_update_binlog_offset(buf_block_t *rseg_header, const trx_t *trx, + mtr_t *mtr) +{ + DBUG_LOG("trx", "trx_mysql_binlog_offset: " << trx->mysql_log_offset); + + const size_t len = strlen(trx->mysql_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->frame, + trx->mysql_log_offset); + + void* name = TRX_RSEG + TRX_RSEG_BINLOG_NAME + rseg_header->frame; + + if (memcmp(trx->mysql_log_file_name, name, len)) { + mtr->memcpy(*rseg_header, name, trx->mysql_log_file_name, len); + } +} |