mirror of
https://github.com/MariaDB/server.git
synced 2025-07-27 05:35:00 +02:00
1045 lines
36 KiB
C++
1045 lines
36 KiB
C++
/*****************************************************************************
|
|
|
|
Copyright (c) 2017, 2024, Oracle and/or its affiliates.
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License, version 2.0, as published by the
|
|
Free Software Foundation.
|
|
|
|
This program is designed to work with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have either included with
|
|
the program or referenced in the documentation.
|
|
|
|
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, version 2.0,
|
|
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 St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
/** @file include/clone0snapshot.h
|
|
Database Physical Snapshot
|
|
|
|
*******************************************************/
|
|
|
|
#ifndef CLONE_SNAPSHOT_INCLUDE
|
|
#define CLONE_SNAPSHOT_INCLUDE
|
|
|
|
#include "univ.i"
|
|
|
|
#include "arch0log.h"
|
|
#include "arch0page.h"
|
|
#include "clone0api.h"
|
|
#include "clone0desc.h"
|
|
#include "clone0monitor.h"
|
|
#include "fil0fil.h"
|
|
#include "handler.h"
|
|
|
|
#include <map>
|
|
#include <vector>
|
|
#include <functional>
|
|
|
|
#define UNIV_PAGE_SIZE ((uint32_t)srv_page_size)
|
|
|
|
struct Clone_file_ctx {
|
|
/** File state:
|
|
[CREATED] -------------> [DROPPING] --> [DROPPED] --> [DROPPED_HANDLED]
|
|
| ^
|
|
| |
|
|
----> [RENAMING] -> [RENAMED]
|
|
| |
|
|
<------------
|
|
*/
|
|
enum class State {
|
|
/* Invalid state. */
|
|
NONE,
|
|
/* File is being dropped. */
|
|
DROPPING,
|
|
/* File is being renamed. */
|
|
RENAMING,
|
|
/* Newly created file or pre-existing before clone. */
|
|
CREATED,
|
|
/* File is renamed during clone. */
|
|
RENAMED,
|
|
/* File is deleted during clone. */
|
|
DROPPED,
|
|
/* File is deleted and chunk information is handled. */
|
|
DROPPED_HANDLED
|
|
};
|
|
|
|
/** File extension to use with name. */
|
|
enum class Extension {
|
|
/* No extension. */
|
|
NONE,
|
|
/* Replace extension - clone file to be replaced during recovery. */
|
|
REPLACE,
|
|
/* DDL extension - temporary extension used during rename. */
|
|
DDL
|
|
};
|
|
|
|
/** Initialize file state.
|
|
@param[in] extn file name extension */
|
|
void init(Extension extn) {
|
|
m_state.store(State::CREATED);
|
|
m_extension = extn;
|
|
|
|
m_pin.store(0);
|
|
m_modified_ddl = false;
|
|
m_waiting = 0;
|
|
|
|
m_next_state = CLONE_SNAPSHOT_NONE;
|
|
|
|
m_meta.init();
|
|
}
|
|
|
|
/** Get file name with extension.
|
|
@param[out] name file name. */
|
|
void get_file_name(std::string &name) const;
|
|
|
|
/** Mark file added by DDL.
|
|
@param[in] next_state next snapshot state */
|
|
void set_ddl(Snapshot_State next_state) {
|
|
m_modified_ddl = true;
|
|
m_next_state = next_state;
|
|
}
|
|
|
|
/** @return true iff added or modified by ddl in previous state.
|
|
@param[in] state current snapshot state */
|
|
bool by_ddl(Snapshot_State state) const {
|
|
return m_modified_ddl && (state <= m_next_state);
|
|
}
|
|
|
|
/** Start waiting for DDL */
|
|
void begin_wait() { ++m_waiting; }
|
|
|
|
/** Finish waiting for DDL */
|
|
void end_wait() {
|
|
ut_a(m_waiting > 0);
|
|
--m_waiting;
|
|
}
|
|
|
|
/** @return true, iff there are waiting clone tasks. */
|
|
bool is_waiting() const { return (m_waiting > 0); }
|
|
|
|
/** Pin the file. */
|
|
void pin() { ++m_pin; }
|
|
|
|
/** Unpin the file. */
|
|
void unpin() {
|
|
ut_a(m_pin > 0);
|
|
--m_pin;
|
|
}
|
|
|
|
/** @return true, iff clone tasks are using the file. */
|
|
bool is_pinned() const { return (m_pin.load() > 0); }
|
|
|
|
/** @return true, iff DDL is modifying file. */
|
|
bool modifying() const {
|
|
State state = m_state.load();
|
|
return (state == State::RENAMING || state == State::DROPPING);
|
|
}
|
|
|
|
/** @return true, iff DDL is deleting file. */
|
|
bool deleting() const {
|
|
State state = m_state.load();
|
|
return (state == State::DROPPING);
|
|
}
|
|
|
|
/** @return true, iff file is already deleted. */
|
|
bool deleted() const {
|
|
State state = m_state.load();
|
|
return (state == State::DROPPED || state == State::DROPPED_HANDLED);
|
|
}
|
|
|
|
/** @return true, iff file is already renamed. */
|
|
bool renamed() const {
|
|
State state = m_state.load();
|
|
return (state == State::RENAMED);
|
|
}
|
|
|
|
/** @return file metadata. */
|
|
Clone_File_Meta *get_file_meta() { return &m_meta; }
|
|
|
|
/** @return file metadata for read. */
|
|
const Clone_File_Meta *get_file_meta_read() const { return &m_meta; }
|
|
|
|
/** File metadata state. Modified by DDL commands. Protected by snapshot
|
|
mutex. Atomic operation helps clone to skip mutex when no ddl. */
|
|
std::atomic<State> m_state;
|
|
|
|
/** File name extension. */
|
|
Extension m_extension;
|
|
|
|
private:
|
|
/** Pin count incremented and decremented by clone tasks to synchronize with
|
|
concurrent DDL. Protected by snapshot mutex. */
|
|
std::atomic<uint32_t> m_pin;
|
|
|
|
/** Waiting count incremented and decremented by clone tasks while waiting
|
|
DDL file operation in progress. Protected by snapshot mutex. */
|
|
uint32_t m_waiting;
|
|
|
|
/** true, if file created or modified after clone is started. */
|
|
bool m_modified_ddl{false};
|
|
|
|
/** Next state when ddl last modified file. */
|
|
Snapshot_State m_next_state{CLONE_SNAPSHOT_DONE};
|
|
|
|
/** File metadata. */
|
|
Clone_File_Meta m_meta;
|
|
};
|
|
|
|
/** Vector type for storing clone files */
|
|
using Clone_File_Vec = std::vector<Clone_file_ctx *>;
|
|
|
|
/** Map type for mapping space ID to clone file index */
|
|
using Clone_File_Map = std::map<space_id_t, uint>;
|
|
|
|
/** Page identified by space and page number */
|
|
struct Clone_Page {
|
|
/** Tablespace ID */
|
|
uint32_t m_space_id;
|
|
|
|
/** Page number within tablespace */
|
|
uint32_t m_page_no;
|
|
};
|
|
|
|
/** Comparator for storing sorted page ID. */
|
|
struct Less_Clone_Page {
|
|
/** Less than operator for page ID.
|
|
@param[in] page1 first page
|
|
@param[in] page2 second page
|
|
@return true, if page1 is less than page2 */
|
|
inline bool operator()(const Clone_Page &page1,
|
|
const Clone_Page &page2) const {
|
|
if (page1.m_space_id < page2.m_space_id) {
|
|
return (true);
|
|
}
|
|
|
|
if (page1.m_space_id == page2.m_space_id &&
|
|
page1.m_page_no < page2.m_page_no) {
|
|
return (true);
|
|
}
|
|
return (false);
|
|
}
|
|
};
|
|
|
|
/** Vector type for storing clone page IDs */
|
|
using Clone_Page_Vec = std::vector<Clone_Page>;
|
|
|
|
/** Set for storing unique page IDs. */
|
|
using Clone_Page_Set = std::set<Clone_Page, Less_Clone_Page>;
|
|
|
|
/** Clone handle type */
|
|
enum Clone_Handle_Type {
|
|
/** Clone Handle for COPY */
|
|
CLONE_HDL_COPY = 1,
|
|
|
|
/** Clone Handle for APPLY */
|
|
CLONE_HDL_APPLY
|
|
};
|
|
|
|
/** Default chunk size in power of 2 in unit of pages.
|
|
Chunks are reserved by each thread for multi-threaded clone. For 16k page
|
|
size, chunk size is 64M. */
|
|
const uint SNAPSHOT_DEF_CHUNK_SIZE_POW2 = 12;
|
|
|
|
/** Default block size in power of 2 in unit of pages.
|
|
Data transfer callback is invoked once for each block. This is also
|
|
the maximum size of data that would be re-send if clone is stopped
|
|
and resumed. For 16k page size, block size is 1M. */
|
|
const uint SNAPSHOT_DEF_BLOCK_SIZE_POW2 = 6;
|
|
|
|
/** Maximum block size in power of 2 in unit of pages.
|
|
For 16k page size, maximum block size is 64M. */
|
|
const uint SNAPSHOT_MAX_BLOCK_SIZE_POW2 = 12;
|
|
|
|
/** Dynamic database snapshot: Holds metadata and handle to data */
|
|
class Clone_Snapshot {
|
|
public:
|
|
/** RAII style guard for begin & end of snapshot state transition. */
|
|
class State_transit {
|
|
public:
|
|
/** Constructor to begin state transition.
|
|
@param[in,out] snapshot Clone Snapshot
|
|
@param[in] new_state State to transit */
|
|
explicit State_transit(Clone_Snapshot *snapshot, Snapshot_State new_state);
|
|
|
|
/** Destructor to end state transition. */
|
|
~State_transit();
|
|
|
|
/** @return error code */
|
|
int get_error() const { return m_error; }
|
|
|
|
/** Disable copy construction */
|
|
State_transit(State_transit const &) = delete;
|
|
|
|
/** Disable assignment */
|
|
State_transit &operator=(State_transit const &) = delete;
|
|
|
|
private:
|
|
/** Clone Snapshot */
|
|
Clone_Snapshot *m_snapshot;
|
|
|
|
/** Saved error while beginning transition. */
|
|
int m_error;
|
|
};
|
|
|
|
/** Construct snapshot
|
|
@param[in] hdl_type copy, apply
|
|
@param[in] clone_type clone type
|
|
@param[in] arr_idx index in global array
|
|
@param[in] snap_id unique snapshot ID */
|
|
Clone_Snapshot(Clone_Handle_Type hdl_type, Ha_clone_type clone_type,
|
|
uint arr_idx, uint64_t snap_id);
|
|
|
|
/** Release contexts and free heap */
|
|
~Clone_Snapshot();
|
|
|
|
/** DDL notification before the operation.
|
|
@param[in] type type of DDL notification
|
|
@param[in] space space ID for the ddl operation
|
|
@param[in] no_wait return with error if needs to wait
|
|
@param[in] check_intr check for interrupt during wait
|
|
@param[out] error mysql error code
|
|
@return true iff clone state change is blocked. */
|
|
bool begin_ddl_state(Clone_notify::Type type, space_id_t space, bool no_wait,
|
|
bool check_intr, int &error);
|
|
|
|
/** DDL notification after the operation.
|
|
@param[in] type type of DDL notification
|
|
@param[in] space space ID for the ddl operation */
|
|
void end_ddl_state(Clone_notify::Type type, space_id_t space);
|
|
|
|
/** Wait for concurrent DDL file operation and pin file.
|
|
@param[in,out] file_ctx file context
|
|
@param[out] handle_delete if caller needs to handle deleted state
|
|
@return mysql error code. */
|
|
int pin_file(Clone_file_ctx *file_ctx, bool &handle_delete);
|
|
|
|
/** Unpin a file.
|
|
@param[in,out] file_ctx file context */
|
|
void unpin_file(Clone_file_ctx *file_ctx) { file_ctx->unpin(); }
|
|
|
|
/** Check if DDL needs to block clone operation.
|
|
@param[in] file_ctx file context
|
|
@return true iff clone operation needs to be blocked. */
|
|
bool blocks_clone(const Clone_file_ctx *file_ctx);
|
|
|
|
/** @return estimated bytes on disk */
|
|
uint64_t get_disk_estimate() const { return (m_data_bytes_disk); }
|
|
|
|
/** Get unique snapshot identifier
|
|
@return snapshot ID */
|
|
uint64_t get_id() { return (m_snapshot_id); }
|
|
|
|
/** Get snapshot index in global array
|
|
@return array index */
|
|
uint get_index() { return (m_snapshot_arr_idx); }
|
|
|
|
/** Get performance schema accounting object used to monitor stage
|
|
progress.
|
|
@return PFS stage object */
|
|
Clone_Monitor &get_clone_monitor() { return (m_monitor); }
|
|
|
|
/** Get snapshot heap used for allocation during clone.
|
|
@return heap */
|
|
mem_heap_t *lock_heap() {
|
|
mysql_mutex_lock(&m_snapshot_mutex);
|
|
return (m_snapshot_heap);
|
|
}
|
|
|
|
/* Release snapshot heap */
|
|
void release_heap(mem_heap_t *&heap) {
|
|
heap = nullptr;
|
|
mysql_mutex_unlock(&m_snapshot_mutex);
|
|
}
|
|
|
|
/** Get snapshot state
|
|
@return state */
|
|
Snapshot_State get_state() { return (m_snapshot_state); }
|
|
|
|
/** Get the redo file size for the snapshot
|
|
@return redo file size */
|
|
uint64_t get_redo_file_size() { return (m_redo_file_size); }
|
|
|
|
/** Get total number of chunks for current state
|
|
@return number of data chunks */
|
|
uint get_num_chunks() { return (m_num_current_chunks); }
|
|
|
|
/** Get maximum file length seen till now
|
|
@return file name length */
|
|
size_t get_max_file_name_length() { return (m_max_file_name_len); }
|
|
|
|
/** Get maximum buffer size required for clone
|
|
@return maximum dynamic buffer */
|
|
uint get_dyn_buffer_length() {
|
|
uint ret_len = 0;
|
|
|
|
if (is_copy() && m_snapshot_type != HA_CLONE_BLOCKING) {
|
|
ret_len = static_cast<uint>(2 * UNIV_PAGE_SIZE);
|
|
}
|
|
|
|
return (ret_len);
|
|
}
|
|
|
|
using File_Cbk_Func = std::function<int(Clone_file_ctx *)>;
|
|
|
|
/** Iterate through all files in current state
|
|
@param[in] func callback function
|
|
@return error code */
|
|
int iterate_files(File_Cbk_Func &&func);
|
|
|
|
/** Iterate through all data files
|
|
@param[in] func callback function
|
|
@return error code */
|
|
int iterate_data_files(File_Cbk_Func &&func);
|
|
|
|
/** Iterate through all redo files
|
|
@param[in] func callback function
|
|
@return error code */
|
|
int iterate_redo_files(File_Cbk_Func &&func);
|
|
|
|
/** Fill state descriptor from snapshot
|
|
@param[in] do_estimate estimate data bytes to transfer
|
|
@param[out] state_desc snapshot state descriptor */
|
|
void get_state_info(bool do_estimate, Clone_Desc_State *state_desc);
|
|
|
|
/** Set state information during apply
|
|
@param[in] state_desc snapshot state descriptor */
|
|
void set_state_info(Clone_Desc_State *state_desc);
|
|
|
|
/** Get next state based on snapshot type
|
|
@return next state */
|
|
Snapshot_State get_next_state();
|
|
|
|
/** Try to attach to snapshot
|
|
@param[in] hdl_type copy, apply
|
|
@param[in] pfs_monitor enable PFS monitoring
|
|
@return true if successfully attached */
|
|
bool attach(Clone_Handle_Type hdl_type, bool pfs_monitor);
|
|
|
|
/** Detach from snapshot. */
|
|
void detach();
|
|
|
|
/** Set current snapshot aborted state. Used in error cases before exiting
|
|
clone to make sure any DDL notifier exits waiting. */
|
|
void set_abort();
|
|
|
|
/** @return true, iff clone has aborted. */
|
|
bool is_aborted() const;
|
|
|
|
/** Start transition to new state
|
|
@param[in] state_desc descriptor for next state
|
|
@param[in] new_state state to move for apply
|
|
@param[in] temp_buffer buffer used for collecting page IDs
|
|
@param[in] temp_buffer_len buffer length
|
|
@param[in] cbk alter callback for long wait
|
|
@return error code */
|
|
int change_state(Clone_Desc_State *state_desc, Snapshot_State new_state,
|
|
byte *temp_buffer, uint temp_buffer_len,
|
|
Clone_Alert_Func cbk);
|
|
|
|
/** Add file metadata entry at destination
|
|
@param[in] file_meta file metadata from donor
|
|
@param[in] data_dir destination data directory
|
|
@param[in] desc_create create if doesn't exist
|
|
@param[out] desc_exists descriptor already exists
|
|
@param[out] file_ctx if there, set to current file context
|
|
@return error code */
|
|
int get_file_from_desc(const Clone_File_Meta *file_meta, const char *data_dir,
|
|
bool desc_create, bool &desc_exists,
|
|
Clone_file_ctx *&file_ctx);
|
|
|
|
/** Rename an existing file descriptor.
|
|
@param[in] file_meta renamed file metadata from donor
|
|
@param[in] data_dir destination data directory
|
|
@param[out] file_ctx if there, set to current file context
|
|
@return error code */
|
|
int rename_desc(const Clone_File_Meta *file_meta, const char *data_dir,
|
|
Clone_file_ctx *&file_ctx);
|
|
|
|
/** Fix files renamed with ddl extension. The file name is checked against
|
|
existing file and added to appropriate status file.
|
|
@param[in] data_dir destination data directory
|
|
@param[in,out] file_ctx Set to correct extension
|
|
@return error code */
|
|
int fix_ddl_extension(const char *data_dir, Clone_file_ctx *file_ctx);
|
|
|
|
/** Add file descriptor to file list
|
|
@param[in,out] file_ctx current file context
|
|
@param[in] ddl_create added by DDL concurrently
|
|
@return true, if it is the last file. */
|
|
bool add_file_from_desc(Clone_file_ctx *&file_ctx, bool ddl_create);
|
|
|
|
/** Extract file information from node and add to snapshot
|
|
@param[in] node file node
|
|
@param[in] by_ddl node is added concurrently by DDL
|
|
@return error code */
|
|
dberr_t add_node(fil_node_t *node, bool by_ddl);
|
|
|
|
/** Add page ID to to the set of pages in snapshot
|
|
@param[in] space_id page tablespace
|
|
@param[in] page_num page number within tablespace
|
|
@return error code */
|
|
int add_page(uint32_t space_id, uint32_t page_num);
|
|
|
|
/** Add redo file to snapshot
|
|
@param[in] file_name file name
|
|
@param[in] file_size file size in bytes
|
|
@param[in] file_offset start offset
|
|
@return error code. */
|
|
int add_redo_file(char *file_name, uint64_t file_size, uint64_t file_offset);
|
|
|
|
/** Get file metadata by index for current state
|
|
@param[in] index file index
|
|
@return file metadata entry */
|
|
Clone_File_Meta *get_file_by_index(uint index);
|
|
|
|
/** Get clone file context by index for current state
|
|
@param[in] index file index
|
|
@return file context */
|
|
Clone_file_ctx *get_file_ctx_by_index(uint index);
|
|
|
|
/** Get clone file context by chunk and block number.
|
|
@param[in] chunk_num chunk number
|
|
@param[in] block_num block number
|
|
@param[in] hint_index hint file index number to start search.
|
|
@return file context */
|
|
Clone_file_ctx *get_file_ctx(uint32_t chunk_num, uint32_t block_num,
|
|
uint32_t hint_index);
|
|
|
|
/** Get next block of data to transfer
|
|
@param[in] chunk_num current chunk
|
|
@param[in,out] block_num current/next block
|
|
@param[in,out] file_ctx current/next block file context
|
|
@param[out] data_offset block offset in file
|
|
@param[out] data_buf data buffer or NULL if transfer from file
|
|
@param[out] data_size size of data in bytes
|
|
@param[out] file_size updated file size if extended
|
|
@return error code */
|
|
int get_next_block(uint chunk_num, uint &block_num,
|
|
const Clone_file_ctx *&file_ctx, uint64_t &data_offset,
|
|
byte *&data_buf, uint32_t &data_size, uint64_t &file_size);
|
|
|
|
/** Update snapshot block size based on caller's buffer size
|
|
@param[in] buff_size buffer size for clone transfer */
|
|
void update_block_size(uint buff_size);
|
|
|
|
/** @return chunk size in bytes. */
|
|
inline uint32_t get_chunk_size() const {
|
|
return static_cast<uint32_t>(chunk_size() * UNIV_PAGE_SIZE);
|
|
}
|
|
|
|
/** @return number of blocks per chunk for different states. */
|
|
uint32_t get_blocks_per_chunk() const;
|
|
|
|
/** Check if copy snapshot
|
|
@return true if snapshot is for copy */
|
|
bool is_copy() const { return (m_snapshot_handle_type == CLONE_HDL_COPY); }
|
|
|
|
/** Update file size when file is extended during page copy
|
|
@param[in] file_index current file index
|
|
@param[in] file_size new file size */
|
|
void update_file_size(uint32_t file_index, uint64_t file_size);
|
|
|
|
/** @return maximum blocks to transfer with file pinned. */
|
|
uint32_t get_max_blocks_pin() const;
|
|
|
|
/** Skip all blocks belonging to currently deleted file context.
|
|
@param[in] chunk_num current chunk
|
|
@param[in,out] block_num current, next block */
|
|
void skip_deleted_blocks(uint32_t chunk_num, uint32_t &block_num);
|
|
|
|
private:
|
|
/** Allow DDL file operation after 64 pages. */
|
|
const static uint32_t S_MAX_PAGES_PIN = 64;
|
|
|
|
/** Allow DDL file operation after every block (1M data by default) */
|
|
const static uint32_t S_MAX_BLOCKS_PIN = 1;
|
|
|
|
/** File name allocation size base. */
|
|
const static size_t S_FILE_NAME_BASE_LEN = 256;
|
|
|
|
/** Various wait types related to snapshot state. */
|
|
enum class Wait_type {
|
|
/* DDL- limited wait if clone is waiting for another DDL. */
|
|
STATE_TRANSIT_WAIT,
|
|
/* DDL- Wait till snapshot state transition is over. */
|
|
STATE_TRANSIT,
|
|
/* DDL- Wait till PAGE COPY state is over. */
|
|
STATE_END_PAGE_COPY,
|
|
/* Clone - Wait till there are no blockers for state transition. */
|
|
STATE_BLOCKER,
|
|
/*DDL - Wait till the waiting clone threads are active. This are
|
|
clone threads from last DDL and useful to prevent starvation. */
|
|
DATA_FILE_WAIT,
|
|
/* DDL - Wait till all threads have closed active data files. */
|
|
DATA_FILE_CLOSE,
|
|
/* Clone - Wait till DDL file operation is complete. */
|
|
DDL_FILE_OPERATION
|
|
};
|
|
|
|
#ifdef UNIV_DEBUG
|
|
/** Debug sync Wait during state transition. */
|
|
void debug_wait_state_transit();
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
/** Update deleted state of a file if not yet done.
|
|
@param[in,out] file_ctx file context
|
|
@return true, if updated state */
|
|
bool update_deleted_state(Clone_file_ctx *file_ctx);
|
|
|
|
/** Get clone data file context by chunk number.
|
|
@param[in] chunk_num chunk number
|
|
@param[in] hint_index hint file index number to start search.
|
|
@return file context */
|
|
Clone_file_ctx *get_data_file_ctx(uint32_t chunk_num, uint32_t hint_index);
|
|
|
|
/** Get clone page file context by chunk number and block number.
|
|
@param[in] chunk_num chunk number
|
|
@param[in] block_num block number
|
|
@return file context */
|
|
Clone_file_ctx *get_page_file_ctx(uint32_t chunk_num, uint32_t block_num);
|
|
|
|
/** Get clone redo file context by chunk number.
|
|
@param[in] chunk_num chunk number
|
|
@param[in] hint_index hint file index number to start search.
|
|
@return file context */
|
|
Clone_file_ctx *get_redo_file_ctx(uint32_t chunk_num, uint32_t hint_index);
|
|
|
|
/** Get wait information string based on wait type.
|
|
@param[in] wait_type wait type
|
|
@return wait information string. */
|
|
const char *wait_string(Wait_type wait_type) const;
|
|
|
|
/** Wait for various operations based on type.
|
|
@param[in] type wait type
|
|
@param[in] ctx file context when relevant
|
|
@param[in] no_wait return with error if needs to wait
|
|
@param[in] check_intr check for interrupt during wait
|
|
@return mysql error code. */
|
|
int wait(Wait_type type, const Clone_file_ctx *ctx, bool no_wait,
|
|
bool check_intr);
|
|
|
|
/** During wait get relevant message string for logging.
|
|
@param[in] wait_type wait type
|
|
@param[out] info notification to log while waiting
|
|
@param[out] error error message to log on timeout */
|
|
void get_wait_mesg(Wait_type wait_type, std::string &info,
|
|
std::string &error);
|
|
|
|
/** Block clone state transition. Clone must wait.
|
|
@param[in] type type of DDL notification
|
|
@param[in] space space ID for the ddl operation
|
|
@param[in] no_wait return with error if needs to wait
|
|
@param[in] check_intr check for interrupt during wait`
|
|
@param[out] error mysql error code
|
|
@return true iff clone state change is blocked. */
|
|
bool block_state_change(Clone_notify::Type type, space_id_t space,
|
|
bool no_wait, bool check_intr, int &error);
|
|
|
|
/** Unblock clone state transition. */
|
|
void unblock_state_change();
|
|
|
|
/** Get next file state while being modified by ddl.
|
|
@param[in] type ddl notification type
|
|
@param[in] begin true, if DDL begin notification
|
|
false, if DDL end notification
|
|
@return target file state. */
|
|
Clone_file_ctx::State get_target_file_state(Clone_notify::Type type,
|
|
bool begin);
|
|
|
|
/** Handle files for DDL begin notification.
|
|
@param[in] type type of DDL notification
|
|
@param[in] space space ID for the ddl operation
|
|
@param[in] no_wait return with error if needs to wait
|
|
@param[in] check_intr check for interrupt during wait
|
|
@return mysql error code */
|
|
int begin_ddl_file(Clone_notify::Type type, space_id_t space, bool no_wait,
|
|
bool check_intr);
|
|
|
|
/** Handle files for DDL end notification.
|
|
@param[in] type type of DDL notification
|
|
@param[in] space space ID for the ddl operation */
|
|
void end_ddl_file(Clone_notify::Type type, space_id_t space);
|
|
|
|
/** Begin state transition before waiting for DDL. */
|
|
void begin_transit_ddl_wait() {
|
|
mysql_mutex_assert_owner(&m_snapshot_mutex);
|
|
/* Update number of clones to transit to new state. Set this prior to
|
|
waiting for DDLs blocking state transfer. This would help a new DDL to
|
|
find if clone is blocked by other DDL before state transition. */
|
|
m_num_clones_transit = m_num_clones;
|
|
}
|
|
|
|
/** Begin state transition.
|
|
@param[in] new_state state to transit to */
|
|
void begin_transit(Snapshot_State new_state) {
|
|
mysql_mutex_assert_owner(&m_snapshot_mutex);
|
|
m_snapshot_next_state = new_state;
|
|
/* Move to next state. This is ok as the snapshot
|
|
mutex is not released till transition is ended, This
|
|
could change later when we ideally should release
|
|
the snapshot mutex during transition. */
|
|
m_snapshot_state = m_snapshot_next_state;
|
|
}
|
|
|
|
/** End state transition. */
|
|
void end_transit() {
|
|
mysql_mutex_assert_owner(&m_snapshot_mutex);
|
|
m_num_clones_transit = 0;
|
|
m_snapshot_next_state = CLONE_SNAPSHOT_NONE;
|
|
}
|
|
|
|
/** Check if state transition is in progress
|
|
@return true during state transition */
|
|
bool in_transit_state() const {
|
|
mysql_mutex_assert_owner(&m_snapshot_mutex);
|
|
return (m_snapshot_next_state != CLONE_SNAPSHOT_NONE);
|
|
}
|
|
|
|
/** @return true, if waiting before starting transition. Generally the
|
|
case when some DDL blocks state transition. */
|
|
bool in_transit_wait() const {
|
|
mysql_mutex_assert_owner(&m_snapshot_mutex);
|
|
return (!in_transit_state() && m_num_clones_transit != 0);
|
|
}
|
|
|
|
/** Start redo archiving.
|
|
@return error code */
|
|
int init_redo_archiving();
|
|
|
|
/** Initialize snapshot state for file copy
|
|
@param[in] new_state state to move for apply
|
|
@return error code */
|
|
int init_file_copy(Snapshot_State new_state);
|
|
|
|
/** Initialize disk byte estimate. */
|
|
void init_disk_estimate() {
|
|
/* Initial size is set to the redo file size on disk. */
|
|
log_sys.latch.wr_lock(SRW_LOCK_CALL);
|
|
/* TODO: Get physical capacity. */
|
|
m_data_bytes_disk = log_sys.log_capacity;
|
|
log_sys.latch.wr_unlock();
|
|
}
|
|
|
|
/** Initialize snapshot state for page copy
|
|
@param[in] new_state state to move for apply
|
|
@param[in] page_buffer temporary buffer to copy page IDs
|
|
@param[in] page_buffer_len buffer length
|
|
@return error code */
|
|
int init_page_copy(Snapshot_State new_state, byte *page_buffer,
|
|
uint page_buffer_len);
|
|
|
|
/** Initialize snapshot state for redo copy
|
|
@param[in] new_state state to move for apply
|
|
@param[in] cbk alert callback for long wait
|
|
@return error code */
|
|
int init_redo_copy(Snapshot_State new_state, Clone_Alert_Func cbk);
|
|
|
|
/** Initialize state while applying cloned data
|
|
@param[in] state_desc snapshot state descriptor
|
|
@return error code */
|
|
int init_apply_state(Clone_Desc_State *state_desc);
|
|
|
|
/** Extend and flush files after copying data
|
|
@param[in] flush_redo if true flush redo, otherwise data
|
|
@return error code */
|
|
int extend_and_flush_files(bool flush_redo);
|
|
|
|
/** Create file descriptor and add to current file list
|
|
@param[in] data_dir destination data directory
|
|
@param[in] file_meta file metadata from donor
|
|
@param[in] is_ddl if ddl temporary file
|
|
@param[out] file_ctx file context
|
|
@return error code */
|
|
int create_desc(const char *data_dir, const Clone_File_Meta *file_meta,
|
|
bool is_ddl, Clone_file_ctx *&file_ctx);
|
|
|
|
/** Get file context for current chunk
|
|
@param[in] file_vector clone file vector
|
|
@param[in] chunk_num current chunk number
|
|
@param[in] start_index index for starting the search
|
|
@return file context */
|
|
Clone_file_ctx *get_file(Clone_File_Vec &file_vector, uint32_t chunk_num,
|
|
uint32_t start_index);
|
|
|
|
/** Get next page from buffer pool
|
|
@param[in] chunk_num current chunk
|
|
@param[in,out] block_num current, next block
|
|
@param[in,out] file_ctx current, next block file context
|
|
@param[out] data_offset offset in file
|
|
@param[out] data_buf page data
|
|
@param[out] data_size page data size
|
|
@param[out] file_size updated file size if extended
|
|
@return error code */
|
|
int get_next_page(uint chunk_num, uint &block_num,
|
|
const Clone_file_ctx *&file_ctx, uint64_t &data_offset,
|
|
byte *&data_buf, uint32_t &data_size, uint64_t &file_size);
|
|
|
|
/** Get page from buffer pool and make ready for write
|
|
@param[in] page_id page ID chunk
|
|
@param[in] page_size physical page size on disk
|
|
@param[in] file_ctx clone file context
|
|
@param[out] page_data data page
|
|
@param[out] data_size page size in bytes
|
|
@return error code */
|
|
int get_page_for_write(const page_id_t &page_id, uint32_t page_size,
|
|
const Clone_file_ctx *file_ctx, byte *&page_data,
|
|
uint &data_size);
|
|
|
|
/* Make page ready for flush by updating LSN anc checksum
|
|
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
|
|
@param[in,out] page_data data page
|
|
@param[in] full_crc32 whether to use full_crc32 algorithm */
|
|
void page_update_for_flush(ulint zip_size, byte *&page_data,
|
|
bool full_crc32);
|
|
|
|
/* Handle page compression and encryption. */
|
|
void page_compress_encrypt(
|
|
const Clone_File_Meta *file_meta, byte *&page_data, uint32_t data_size,
|
|
ulint zip_size, bool full_crc32, bool compress, bool encrypt,
|
|
uint32_t page_no);
|
|
|
|
/** Build file metadata entry
|
|
@param[in] file_name name of the file
|
|
@param[in] file_size file size in bytes
|
|
@param[in] file_offset start offset
|
|
@param[in] num_chunks total number of chunks in the file
|
|
@return file context */
|
|
Clone_file_ctx *build_file(const char *file_name, uint64_t file_size,
|
|
uint64_t file_offset, uint &num_chunks);
|
|
|
|
/** Allocate and set clone file name.
|
|
@param[in,out] file_meta file metadata
|
|
@param[in] file_name file name
|
|
@return true iff successful. */
|
|
bool build_file_name(Clone_File_Meta *file_meta, const char *file_name);
|
|
|
|
/** Add buffer pool dump file to the file list
|
|
@return error code */
|
|
int add_buf_pool_file();
|
|
|
|
/** Add file to snapshot
|
|
@param[in] name file name
|
|
@param[in] size_bytes file size in bytes
|
|
@param[in] alloc_bytes allocation size on disk for sparse file
|
|
@param[in] node file node
|
|
@param[in] by_ddl node is added concurrently by DDL
|
|
@return error code. */
|
|
int add_file(const char *name, uint64_t size_bytes, uint64_t alloc_bytes,
|
|
fil_node_t *node, bool by_ddl);
|
|
|
|
/** Check if file context has been changed by ddl.
|
|
@param[in] node tablespace file node
|
|
@param[out] file_ctx file context if exists
|
|
@return true iff file is created or modified by DDL. */
|
|
bool file_ctx_changed(const fil_node_t *node, Clone_file_ctx *&file_ctx);
|
|
|
|
/** Get chunk size
|
|
@return chunk size in pages */
|
|
inline uint32_t chunk_size() const {
|
|
auto size = static_cast<uint32_t>(1 << m_chunk_size_pow2);
|
|
return size;
|
|
}
|
|
|
|
/** Get block size for file copy
|
|
@return block size in pages */
|
|
uint32_t block_size() {
|
|
ut_a(m_block_size_pow2 <= SNAPSHOT_MAX_BLOCK_SIZE_POW2);
|
|
auto size = static_cast<uint32_t>(1 << m_block_size_pow2);
|
|
|
|
return size;
|
|
}
|
|
|
|
/** Get number of blocks per chunk for file copy
|
|
@return blocks per chunk */
|
|
inline uint32_t blocks_per_chunk() const {
|
|
ut_a(m_block_size_pow2 <= m_chunk_size_pow2);
|
|
return (1 << (m_chunk_size_pow2 - m_block_size_pow2));
|
|
}
|
|
|
|
/** Update system file name from configuration.
|
|
@param[in] replace if replacing current data directory
|
|
@param[in] file_meta file descriptor
|
|
@param[in,out] file_name file name to update
|
|
@return error code */
|
|
int update_sys_file_name(bool replace, const Clone_File_Meta *file_meta,
|
|
std::string &file_name);
|
|
|
|
/** Build file name along with path for cloned data files.
|
|
@param[in] data_dir clone data directory
|
|
@param[in] file_desc file descriptor
|
|
@param[out] file_path built file path if returned 0
|
|
@return error code (0 on success) */
|
|
int build_file_path(const char *data_dir, const Clone_File_Meta *file_desc,
|
|
std::string &file_path);
|
|
|
|
/** Build file context from file path.
|
|
@param[in] extn file extension type
|
|
@param[in] file_meta file descriptor
|
|
@param[in] file_path data file along with path
|
|
@param[out] file_ctx created file context
|
|
@return error code */
|
|
int build_file_ctx(Clone_file_ctx::Extension extn,
|
|
const Clone_File_Meta *file_meta,
|
|
const std::string &file_path, Clone_file_ctx *&file_ctx);
|
|
|
|
/** Check for existing file and if clone extension is needed. This function
|
|
has the side effect to add undo file indexes.
|
|
@param[in] replace if data directory is replaced
|
|
@param[in] undo_file if undo tablespace file
|
|
@param[in] redo_file if redo file
|
|
@param[in] data_file_index index of file
|
|
@param[in] data_file data file name
|
|
@param[out] extn file extension needs to be used
|
|
@return error code */
|
|
int handle_existing_file(bool replace, bool undo_file, bool redo_file,
|
|
uint32_t data_file_index,
|
|
const std::string &data_file,
|
|
Clone_file_ctx::Extension &extn);
|
|
|
|
/** @return number of data files to transfer. */
|
|
inline size_t num_data_files() const { return m_data_file_vector.size(); }
|
|
|
|
/** @return number of redo files to transfer. */
|
|
inline size_t num_redo_files() const { return m_redo_file_vector.size(); }
|
|
|
|
private:
|
|
/** @name Snapshot type and ID */
|
|
|
|
/** Snapshot handle type */
|
|
Clone_Handle_Type m_snapshot_handle_type;
|
|
|
|
/** Clone type */
|
|
Ha_clone_type m_snapshot_type;
|
|
|
|
/** Unique snapshot ID */
|
|
uint64_t m_snapshot_id;
|
|
|
|
/** Index in global snapshot array */
|
|
uint m_snapshot_arr_idx;
|
|
|
|
/** @name Snapshot State */
|
|
|
|
/** Mutex to handle access by concurrent clones */
|
|
mutable mysql_mutex_t m_snapshot_mutex;
|
|
|
|
/** Number of blockers for state change. Usually DDLs for short duration. */
|
|
uint32_t m_num_blockers;
|
|
|
|
/** Set to true only if clone is aborted after error. */
|
|
bool m_aborted;
|
|
|
|
/** Number of clones attached to this snapshot */
|
|
uint m_num_clones;
|
|
|
|
/** Number of clones in in state transition */
|
|
uint m_num_clones_transit;
|
|
|
|
/** Current state */
|
|
Snapshot_State m_snapshot_state;
|
|
|
|
/** Next state to move to. Set only during state transfer. */
|
|
Snapshot_State m_snapshot_next_state;
|
|
|
|
/** @name Snapshot data block */
|
|
|
|
/** Memory allocation heap */
|
|
mem_heap_t *m_snapshot_heap;
|
|
|
|
/** Chunk size in power of 2 */
|
|
uint m_chunk_size_pow2;
|
|
|
|
/** Block size in power of 2 */
|
|
uint m_block_size_pow2;
|
|
|
|
/** Number of chunks in current state */
|
|
uint m_num_current_chunks;
|
|
|
|
/** Maximum file name length observed till now. */
|
|
size_t m_max_file_name_len;
|
|
|
|
/** @name Snapshot file data */
|
|
|
|
/** All data files for transfer */
|
|
Clone_File_Vec m_data_file_vector;
|
|
|
|
/** Map space ID to file vector index */
|
|
Clone_File_Map m_data_file_map;
|
|
|
|
/** Total number of data chunks */
|
|
uint m_num_data_chunks;
|
|
|
|
/** Number of bytes on disk. */
|
|
uint64_t m_data_bytes_disk;
|
|
|
|
/** Index into m_data_file_vector for all undo files. */
|
|
std::vector<int> m_undo_file_indexes;
|
|
|
|
/** @name Snapshot page data */
|
|
|
|
/** Page archiver client */
|
|
Page_Arch_Client_Ctx m_page_ctx;
|
|
|
|
/** Set of unique page IDs */
|
|
Clone_Page_Set m_page_set;
|
|
|
|
/** Sorted page IDs to transfer */
|
|
Clone_Page_Vec m_page_vector;
|
|
|
|
/** Number of pages to transfer */
|
|
uint m_num_pages;
|
|
|
|
/** Number of duplicate pages found */
|
|
uint m_num_duplicate_pages;
|
|
|
|
/** @name Snapshot redo data */
|
|
|
|
/** redo log archiver client */
|
|
Log_Arch_Client_Ctx m_redo_ctx;
|
|
|
|
/** All archived redo files to transfer */
|
|
Clone_File_Vec m_redo_file_vector;
|
|
|
|
/** Start offset in first redo file */
|
|
uint64_t m_redo_start_offset;
|
|
|
|
/** Redo header block */
|
|
byte *m_redo_header;
|
|
|
|
/** Redo header size */
|
|
uint m_redo_header_size;
|
|
|
|
/** Redo trailer block */
|
|
byte *m_redo_trailer;
|
|
|
|
/** Redo trailer size */
|
|
uint m_redo_trailer_size;
|
|
|
|
/** Redo trailer block offset */
|
|
uint64_t m_redo_trailer_offset;
|
|
|
|
/** Archived redo file size */
|
|
uint64_t m_redo_file_size;
|
|
|
|
/** Total number of redo data chunks */
|
|
uint m_num_redo_chunks;
|
|
|
|
/** Enable PFS monitoring */
|
|
bool m_enable_pfs;
|
|
|
|
/** Performance Schema accounting object to monitor stage progress */
|
|
Clone_Monitor m_monitor;
|
|
};
|
|
|
|
#endif /* CLONE_SNAPSHOT_INCLUDE */
|