/* Copyright (c) 2016, 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 */ #ifndef DEFINED_RPL_REPLICA_UNTIL_OPTIONS_H #define DEFINED_RPL_REPLICA_UNTIL_OPTIONS_H #include #include #include "my_dbug.h" #include "my_inttypes.h" #include "my_io.h" #include "sql/rpl_gtid.h" class Log_event; class Relay_log_info; /** @class Until_option This is the abstract base class for slave start until options. It defines the public interfaces called by slave coordinator. This class publics three interfaces: - is_satisfied_at_start_slave It is called just when starting slave coordinator to check if the until condition is already satisfied. - is_satisfied_before_dispatching_event It is called just after coordinator reads a events from relay log but before it is applied or dispatched. - is_satisfied_after_dispatching_event It is called just after a event is applied or dispatched by the coordinator. These three interfaces cover all until options's check requirements. */ class Until_option { public: virtual ~Until_option() = default; /** Check if the until option is already satisfied at coordinator starting. @return bool @retval true if it is satisfied. @retval false if it is not satisfied. */ bool is_satisfied_at_start_slave() { DBUG_TRACE; bool ret = check_at_start_slave(); return ret; } /** check if the until option is satisfied before applying or dispatching a event. @param[in] ev point to the event which will be applied or dispatched. @return bool @retval true if it is satisfied. @retval false if it is not satisfied. */ bool is_satisfied_before_dispatching_event(const Log_event *ev) { DBUG_TRACE; bool ret = check_before_dispatching_event(ev); return ret; } /** check if the until option is satisfied after applied or dispatched a event. @return bool @retval true if it is satisfied. @retval false if it is not satisfied. */ bool is_satisfied_after_dispatching_event() { DBUG_TRACE; bool ret = check_after_dispatching_event(); return ret; } /** check if the until option is waiting for more transactions to be read from the relay log. @return bool @retval true all log events where read. @retval false waiting for more log events to be read. */ bool is_satisfied_all_transactions_read_from_relay_log() { DBUG_TRACE; return check_all_transactions_read_from_relay_log(); } protected: Relay_log_info *m_rli; Until_option(Relay_log_info *rli) : m_rli(rli) {} private: /* The virtual functions called by the interface functions. They are implemented in sub-classes. */ virtual bool check_at_start_slave() = 0; virtual bool check_before_dispatching_event(const Log_event *ev) = 0; virtual bool check_after_dispatching_event() = 0; virtual bool check_all_transactions_read_from_relay_log() = 0; }; /** @class Until_position It is an abstract class for until master position and until relay log position. It ecapsulates the variables and functions shared by the two sort of until options. To avoid comparing log name in every call of interface functions. An optimized comparing logic is implemented. - Log name is just compared once for each master log and relay log. - coodinator should notify Until_option object if master log or relay log is switched by calling notify_log_name_change() function. */ class Until_position : public Until_option { public: ~Until_position() override = default; /** Initialize the until position when starting the slave. @param[in] log_name the log name in START REPLICA UNTIL option. @param[in] log_pos the log position in START REPLICA UNTIL option. @return int @retval 0 if succeeds. @retval a defined mysql error number if error happens. */ int init(const char *log_name, my_off_t log_pos); /** Coordinator calls this function to notify that master log switch or relay log switch. */ void notify_log_name_change() { DBUG_TRACE; m_log_names_cmp_result = LOG_NAMES_CMP_UNKNOWN; return; } const char *get_until_log_name() { return (const char *)m_until_log_name; } my_off_t get_until_log_pos() { return m_until_log_pos; } protected: /** Log name is compared only once for each master log or relay log. And the comparison result is stored in m_log_names_cmp_result. */ enum { LOG_NAMES_CMP_UNKNOWN = -2, LOG_NAMES_CMP_LESS = -1, LOG_NAMES_CMP_EQUAL = 0, LOG_NAMES_CMP_GREATER = 1 } m_log_names_cmp_result; Until_position(Relay_log_info *rli) : Until_option(rli) {} /** Check if until position is satisfied. - If m_log_names_cmp_result is LOG_NAMES_CMP_UNKNOWN, then it first compares log_name to m_until_log_name. - If the comparison result is LOG_NAMES_CMP_LESS, it will return false directly. If it is LOG_NAMES_CMP_EQUAL, it then compares log_pos to m_until_log_pos. - If the comparison result is LOG_NAMES_CMP_GREATER, it will return true directly. */ bool check_position(const char *log_name, my_off_t log_pos); private: /* They store the log name and log position in START REPLICA UNTIL option */ char m_until_log_name[FN_REFLEN]; ulonglong m_until_log_pos; /* It stores the suffix number of m_until_log_name. Suffix number is compared as a number but not a string. Otherwise it will cause trouble for the below log names. master-bin.999999 and master-bin.1234567. */ ulong m_until_log_name_extension; }; /** @class Until_master_position It is for UNTIL SOURCE_LOG_FILE and SOURCE_LOG_POS */ class Until_master_position : public Until_position { public: Until_master_position(Relay_log_info *rli) : Until_position(rli) {} private: /* Current event's master log name and position. They are set in is_satisfied_before_dispatching_event and checked in is_satisfied_after_dispatching_event. */ char m_current_log_name[FN_REFLEN]; my_off_t m_current_log_pos; bool check_at_start_slave() override; bool check_before_dispatching_event(const Log_event *ev) override; bool check_after_dispatching_event() override; bool check_all_transactions_read_from_relay_log() override; }; /** @class Until_relay_position It is for UNTIL relay_log_file and relay_log_pos */ class Until_relay_position : public Until_position { public: Until_relay_position(Relay_log_info *rli) : Until_position(rli) {} private: bool check_at_start_slave() override; bool check_before_dispatching_event(const Log_event *ev) override; bool check_after_dispatching_event() override; bool check_all_transactions_read_from_relay_log() override; }; /** @class Until_gtids It is an abstract class for UNTIL SQL_BEFORE_GTIDS and SQL_AFTER_GTIDS. It encapsulates the variables and functions shared between the two options. */ class Until_gtids : public Until_option { public: ~Until_gtids() override = default; /** Initialize the until gtids when starting the slave. @param[in] gtid_set_str the gtid set in START REPLICA UNTIL option. @return int @retval 0 if succeeds. @retval a defined mysql error number if error happens. */ int init(const char *gtid_set_str); protected: /** Stores the gtids of START REPLICA UNTIL SQL_*_GTIDS. Each time a gtid is about to be processed, we check if it is in the set. Depending on until_condition, SQL thread is stopped before or after applying the gtid. */ Gtid_set m_gtids; Until_gtids(Relay_log_info *rli) : Until_option(rli), m_gtids(global_tsid_map) {} }; /** @class Until_before_gtids It implements the logic of UNTIL SQL_BEFORE_GTIDS */ class Until_before_gtids : public Until_gtids { public: Until_before_gtids(Relay_log_info *rli) : Until_gtids(rli) {} private: bool check_at_start_slave() override; bool check_before_dispatching_event(const Log_event *ev) override; bool check_after_dispatching_event() override; bool check_all_transactions_read_from_relay_log() override; }; /** @class Until_after_gtids It implements the logic of UNTIL SQL_AFTER_GTIDS */ class Until_after_gtids : public Until_gtids { public: Until_after_gtids(Relay_log_info *rli) : Until_gtids(rli) {} ~Until_after_gtids() override; private: bool check_at_start_slave() override; bool check_before_dispatching_event(const Log_event *ev) override; bool check_after_dispatching_event() override; bool check_all_transactions_read_from_relay_log() override; /** Checks if all the customer specified transactions have been executed or not. @return bool @retval true all customer specified transactions have been executed @retval false customer specified all transactions have not been executed yet */ bool check_all_transactions_executed(); /** If last transaction has ben completely received wait for the worker thread to finish executing the transactions. @return bool @retval true error happened in wait like worker error out, shutdown etc. @retval false all specified transactions have been executed */ bool wait_for_gtid_set(); /** Print message to the error log for the last message. Caller of the function should hold a read or write lock on global_tsid_lock. */ void last_transaction_executed_message(); /* Gtid_set of transactions read from the Relay Log for application in this channel and known exectuted GTID in the server, transactions may still be in execution in worker threads. */ std::unique_ptr m_gtids_known_to_channel{nullptr}; /* Last transaction has been received and dispatched to worker. */ bool m_last_transaction_in_execution{false}; }; /** @class Until_view_id It implements the logic for UNTIL VIEW_ID. */ class Until_view_id : public Until_option { public: Until_view_id(Relay_log_info *rli) : Until_option(rli), until_view_id_found(false), until_view_id_commit_found(false) {} /** Initialize the view_id when starting the slave. @param[in] view_id the view_id in START REPLICA UNTIO option. @return int @retval 0 if succeeds. @retval a defined mysql error number if error happens. */ int init(const char *view_id); private: std::string m_view_id; /* Flag used to indicate that view_id identified by 'until_view_id' was found on the current UNTIL_SQL_VIEW_ID condition. It is set to false on the beginning of the UNTIL_SQL_VIEW_ID condition, and set to true when view_id is found. */ bool until_view_id_found; /* Flag used to indicate that commit event after view_id identified by 'until_view_id' was found on the current UNTIL_SQL_VIEW_ID condition. It is set to false on the beginning of the UNTIL_SQL_VIEW_ID condition, and set to true when commit event after view_id is found. */ bool until_view_id_commit_found; bool check_at_start_slave() override; bool check_before_dispatching_event(const Log_event *ev) override; bool check_after_dispatching_event() override; bool check_all_transactions_read_from_relay_log() override; }; /** @class Until_mts_gap It implements the logic of UNTIL SQL_AFTER_MTS_GAP */ class Until_mts_gap : public Until_option { public: Until_mts_gap(Relay_log_info *rli) : Until_option(rli) {} /** Initialize it when starting the slave. */ void init(); private: bool check_at_start_slave() override; bool check_before_dispatching_event(const Log_event *ev) override; bool check_after_dispatching_event() override; bool check_all_transactions_read_from_relay_log() override; }; #endif // DEFINED_RPL_REPLICA_UNTIL_OPTIONS_H