diff --git a/storage/innobase/include/log0recv.h b/storage/innobase/include/log0recv.h index 3957c85593e..147df5b40a1 100644 --- a/storage/innobase/include/log0recv.h +++ b/storage/innobase/include/log0recv.h @@ -170,6 +170,8 @@ static inline void recv_recover_page(bool jri, buf_block_t *block) { recv_recover_page_func(jri, block); } +void recv_recover_page_in_bufferpool(buf_block_t *block, recv_addr_t *recv_addr); + #endif /* UNIV_HOTBACKUP */ /** Frees the recovery system. */ diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 2b277e5aace..136f776876f 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -1170,23 +1170,21 @@ static void recv_apply_log_rec(recv_addr_t *recv_addr) { } else if (recv_addr->state == RECV_NOT_PROCESSED) { mutex_exit(&recv_sys->mutex); - if (buf_page_peek(page_id)) { - mtr_t mtr; + mtr_t mtr; + mtr_start(&mtr); - mtr_start(&mtr); - - buf_block_t *block; - - block = - buf_page_get(page_id, page_size, RW_X_LATCH, UT_LOCATION_HERE, &mtr); + buf_block_t *block = buf_page_get_gen( + page_id, page_size, RW_X_LATCH, nullptr, Page_fetch::PEEK_IF_IN_POOL, + __FILE__, __LINE__, &mtr, false, true); + if (block) { buf_block_dbg_add_level(block, SYNC_NO_ORDER_CHECK); - recv_recover_page(false, block); + recv_recover_page_in_bufferpool(block, recv_addr, &mtr); + } else { mtr_commit(&mtr); - } else { recv_read_in_area(page_id); } @@ -2887,6 +2885,259 @@ void recv_recover_page_func( #endif /* UNIV_HOTBACKUP */ } +/** + * Applies the hashed log records to the page already in the buffer pool. +*/ +void recv_recover_page_in_bufferpool(buf_block_t *block, recv_addr_t *recv_addr, mtr_t *mtr) { + mutex_enter(&recv_sys->mutex); + + ut_a(recv_addr); + + if (recv_addr->state == RECV_BEING_PROCESSED || + recv_addr->state == RECV_PROCESSED) { +#ifndef UNIV_HOTBACKUP + ut_ad(recv_addr == nullptr || recv_needed_recovery || + recv_sys->scanned_lsn < recv_sys->checkpoint_lsn); +#endif /* !UNIV_HOTBACKUP */ + + mutex_exit(&recv_sys->mutex); + + return; + } + +#ifndef UNIV_HOTBACKUP + /* The following block is the scope of usage of the following bpage object + reference.*/ + { + buf_page_t &bpage = block->page; + + if (!fsp_is_system_temporary(bpage.id.space()) && + (arch_page_sys != nullptr && arch_page_sys->is_active())) { + page_t *frame; + lsn_t frame_lsn; + + frame = bpage.zip.data; + + if (!frame) { + frame = block->frame; + } + frame_lsn = mach_read_from_8(frame + FIL_PAGE_LSN); + + arch_page_sys->track_page(&bpage, LSN_MAX, frame_lsn, true); + } + } +#endif /* !UNIV_HOTBACKUP */ + +#ifndef UNIV_HOTBACKUP + /* this is explicitly false in case of meb, skip the assert */ + ut_ad(recv_needed_recovery || + recv_sys->scanned_lsn < recv_sys->checkpoint_lsn); + + DBUG_PRINT("ib_log", ("Applying log to page %u:%u", recv_addr->space, + recv_addr->page_no)); + +#ifdef UNIV_DEBUG + lsn_t max_lsn; + + ut_d(max_lsn = log_sys->m_scanned_lsn); +#endif /* UNIV_DEBUG */ +#else /* !UNIV_HOTBACKUP */ + ib::trace_2() << "Applying log to space_id " << recv_addr->space + << " page_nr " << recv_addr->page_no; +#endif /* !UNIV_HOTBACKUP */ + + recv_addr->state = RECV_BEING_PROCESSED; + + mutex_exit(&recv_sys->mutex); + + mtr_set_log_mode(mtr, MTR_LOG_NONE); + + page_t *page = block->frame; + + page_zip_des_t *page_zip = buf_block_get_page_zip(block); + +#ifndef UNIV_HOTBACKUP + + bool success = buf_page_get_known_nowait( + RW_X_LATCH, block, Cache_hint::KEEP_OLD, __FILE__, __LINE__, mtr); + ut_a(success); + + buf_block_dbg_add_level(block, SYNC_NO_ORDER_CHECK); +#endif /* !UNIV_HOTBACKUP */ + + /* Read the newest modification lsn from the page */ + lsn_t page_lsn = mach_read_from_8(page + FIL_PAGE_LSN); + +#ifndef UNIV_HOTBACKUP + + /* It may be that the page has been modified in the buffer + pool: read the newest modification LSN there */ + + lsn_t page_newest_lsn; + + page_newest_lsn = buf_page_get_newest_modification(&block->page); + + if (page_newest_lsn) { + page_lsn = page_newest_lsn; + } +#else /* !UNIV_HOTBACKUP */ + /* In recovery from a backup we do not really use the buffer pool */ + lsn_t page_newest_lsn = 0; + /* Count applied and skipped log records */ + size_t applied_recs = 0; + size_t skipped_recs = 0; +#endif /* !UNIV_HOTBACKUP */ + +#ifndef UNIV_HOTBACKUP + lsn_t end_lsn = 0; +#endif /* !UNIV_HOTBACKUP */ + lsn_t start_lsn = 0; + bool modification_to_page = false; + + for (auto recv : recv_addr->rec_list) { +#ifndef UNIV_HOTBACKUP + end_lsn = recv->end_lsn; + + ut_ad(end_lsn <= max_lsn); +#endif /* !UNIV_HOTBACKUP */ + + byte *buf = nullptr; + + if (recv->len > RECV_DATA_BLOCK_SIZE) { + /* We have to copy the record body to a separate + buffer */ + + buf = static_cast( + ut::malloc_withkey(UT_NEW_THIS_FILE_PSI_KEY, recv->len)); + + recv_data_copy_to_buf(buf, recv); + } else if (recv->data != nullptr) { + buf = ((byte *)(recv->data)) + sizeof(recv_data_t); + } else { + /* Redo record that does not have a payload, such as + MLOG_UNDO_ERASE_END, MLOG_COMP_PAGE_CREATE, MLOG_INIT_FILE_PAGE2 etc. + */ + ut_ad(recv->data == nullptr); + ut_ad(recv->len == 0); + } + + if (recv->type == MLOG_INIT_FILE_PAGE) { + page_lsn = page_newest_lsn; + + memset(FIL_PAGE_LSN + page, 0, 8); + memset(UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + page, 0, 8); + + if (page_zip) { + memset(FIL_PAGE_LSN + page_zip->data, 0, 8); + } + } + + /* Ignore applying the redo logs for tablespace that is + truncated. Truncated tablespaces are handled explicitly + post-recovery, where we will restore the tablespace back + to a normal state. + + Applying redo at this stage will cause problems because the + redo will have action recorded on page before tablespace + was re-inited and that would lead to a problem later. */ + + if (recv->start_lsn >= page_lsn +#ifndef UNIV_HOTBACKUP + && undo::is_active(recv_addr->space) +#endif /* !UNIV_HOTBACKUP */ + ) { + + lsn_t end_lsn; + + if (!modification_to_page) { +#ifndef UNIV_HOTBACKUP + ut_a(recv_needed_recovery); +#endif /* !UNIV_HOTBACKUP */ + modification_to_page = true; + start_lsn = recv->start_lsn; + } + + DBUG_PRINT("ib_log", ("apply " LSN_PF ":" + " %s len " ULINTPF " page %u:%u", + recv->start_lsn, get_mlog_string(recv->type), + recv->len, recv_addr->space, recv_addr->page_no)); + /* Since buf can be a nullptr for record types without a payload we can + end up with nullptr + 0 if we calc buf + recv->len. This is undefined + behaviour. Avoid this by only calculating the end_ptr when there's + actual data to work with, otherwise set it to nullptr. */ + unsigned char *buf_end = nullptr; + if (buf != nullptr) { + buf_end = buf + recv->len; + } + recv_parse_or_apply_log_rec_body(recv->type, buf, buf_end, + recv_addr->space, recv_addr->page_no, + block, mtr, ULINT_UNDEFINED, LSN_MAX); + + end_lsn = recv->start_lsn + recv->len; + + mach_write_to_8(FIL_PAGE_LSN + page, end_lsn); + + mach_write_to_8(UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + page, + end_lsn); + + if (page_zip) { + mach_write_to_8(FIL_PAGE_LSN + page_zip->data, end_lsn); + } +#ifdef UNIV_HOTBACKUP + ++applied_recs; + } else { + ++skipped_recs; +#endif /* UNIV_HOTBACKUP */ + } + + if (recv->len > RECV_DATA_BLOCK_SIZE) { + ut::free(buf); + } + } + +#ifdef UNIV_ZIP_DEBUG + if (fil_page_index_page_check(page)) { + page_zip_des_t *page_zip = buf_block_get_page_zip(block); + + ut_a(!page_zip || page_zip_validate_low(page_zip, page, nullptr, false)); + } +#endif /* UNIV_ZIP_DEBUG */ + +#ifndef UNIV_HOTBACKUP + if (modification_to_page) { + buf_flush_recv_note_modification(block, start_lsn, end_lsn); + } +#else /* !UNIV_HOTBACKUP */ + UT_NOT_USED(start_lsn); +#endif /* !UNIV_HOTBACKUP */ + + /* Make sure that committing mtr does not change the modification + LSN values of page */ + + mtr->discard_modifications(); + + mtr_commit(mtr); + + mutex_enter(&recv_sys->mutex); + + if (recv_max_page_lsn < page_lsn) { + recv_max_page_lsn = page_lsn; + } + + recv_addr->state = RECV_PROCESSED; + + ut_a(recv_sys->n_addrs > 0); + --recv_sys->n_addrs; + + mutex_exit(&recv_sys->mutex); + +#ifdef UNIV_HOTBACKUP + ib::trace_2() << "Applied " << applied_recs << " Skipped " << skipped_recs; +#endif /* UNIV_HOTBACKUP */ +} + + + /** Tries to parse a single log record. @param[out] type log record type @param[in] ptr pointer to a buffer