/* Copyright (c) 2014, 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 */ #include "sql/rpl_msr.h" #include #include "my_dbug.h" #include "my_inttypes.h" #include "my_sys.h" #include "mysql/components/services/log_builtins.h" #include "mysqld_error.h" #include "sql/current_thd.h" #include "sql/rpl_mi.h" #include "sql/rpl_rli.h" // Relay_log_info const char *Multisource_info::default_channel = ""; const char *Multisource_info::group_replication_channel_names[] = { "group_replication_applier", "group_replication_recovery"}; bool Multisource_info::add_mi(const char *channel_name, Master_info *mi) { DBUG_TRACE; m_channel_map_lock->assert_some_wrlock(); mi_map::const_iterator it; std::pair ret; bool res = false; /* The check of mi exceeding MAX_CHANNELS shall be done in the caller */ assert(current_mi_count < MAX_CHANNELS); replication_channel_map::iterator map_it; enum_channel_type type = is_group_replication_channel_name(channel_name) ? GROUP_REPLICATION_CHANNEL : SLAVE_REPLICATION_CHANNEL; map_it = rep_channel_map.find(type); if (map_it == rep_channel_map.end()) { std::pair map_ret = rep_channel_map.insert( replication_channel_map::value_type(type, mi_map())); if (!map_ret.second) return true; map_it = rep_channel_map.find(type); } ret = map_it->second.insert(mi_map::value_type(channel_name, mi)); /* If a map insert fails, ret.second is false */ if (!ret.second) return true; /* Save the pointer for the default_channel to avoid searching it */ if (!strcmp(channel_name, get_default_channel())) default_channel_mi = mi; #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE res = add_mi_to_rpl_pfs_mi(mi); #endif current_mi_count++; return res; } Master_info *Multisource_info::get_mi(const char *channel_name) { DBUG_TRACE; m_channel_map_lock->assert_some_lock(); assert(channel_name != nullptr); mi_map::iterator it; replication_channel_map::iterator map_it; map_it = rep_channel_map.find(SLAVE_REPLICATION_CHANNEL); if (map_it != rep_channel_map.end()) { it = map_it->second.find(channel_name); } if (map_it == rep_channel_map.end() || // If not a slave channel, maybe a group one it == map_it->second.end()) { map_it = rep_channel_map.find(GROUP_REPLICATION_CHANNEL); if (map_it == rep_channel_map.end()) { return nullptr; } it = map_it->second.find(channel_name); if (it == map_it->second.end()) { return nullptr; } } return it->second; } bool Multisource_info::delete_mi(const char *channel_name) { DBUG_TRACE; m_channel_map_lock->assert_some_wrlock(); Master_info *mi = nullptr; mi_map::iterator it; assert(channel_name != nullptr); replication_channel_map::iterator map_it; map_it = rep_channel_map.find(SLAVE_REPLICATION_CHANNEL); if (map_it != rep_channel_map.end()) { it = map_it->second.find(channel_name); } if (map_it == rep_channel_map.end() || // If not a slave channel, maybe a group one it == map_it->second.end()) { map_it = rep_channel_map.find(GROUP_REPLICATION_CHANNEL); assert(map_it != rep_channel_map.end()); if (map_it != rep_channel_map.end()) { it = map_it->second.find(channel_name); assert(it != map_it->second.end()); } } if (map_it == rep_channel_map.end() || it == map_it->second.end()) { // the channel identified by channel_name could not be found return true; } #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE int index = -1; /* get the index of mi from rpl_pfs_mi */ index = get_index_from_rpl_pfs_mi(channel_name); assert(index != -1); /* set the current index to 0 and decrease current_mi_count */ rpl_pfs_mi[index] = nullptr; #endif current_mi_count--; mi = it->second; it->second = nullptr; /* erase from the map */ map_it->second.erase(it); if (default_channel_mi == mi) default_channel_mi = nullptr; /* delete the master info */ if (mi) { mi->channel_assert_some_wrlock(); mi->wait_until_no_reference(current_thd); if (mi->rli) { delete mi->rli; } delete mi; } return false; } bool Multisource_info::is_group_replication_applier_channel_name( const char *channel) { return !strcmp(channel, group_replication_channel_names[0]); } bool Multisource_info::is_group_replication_recovery_channel_name( const char *channel) { return !strcmp(channel, group_replication_channel_names[1]); } bool Multisource_info::is_group_replication_channel_name(const char *channel) { return is_group_replication_applier_channel_name(channel) || is_group_replication_recovery_channel_name(channel); } #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE bool Multisource_info::add_mi_to_rpl_pfs_mi(Master_info *mi) { DBUG_TRACE; m_channel_map_lock->assert_some_wrlock(); bool res = true; // not added /* Point to this added mi in the rpl_pfs_mi*/ for (uint i = 0; i < MAX_CHANNELS; i++) { if (rpl_pfs_mi[i] == nullptr) { rpl_pfs_mi[i] = mi; res = false; // success break; } } return res; } int Multisource_info::get_index_from_rpl_pfs_mi(const char *channel_name) { m_channel_map_lock->assert_some_lock(); Master_info *mi = nullptr; for (uint i = 0; i < MAX_CHANNELS; i++) { mi = rpl_pfs_mi[i]; if (mi) { if (!strcmp(mi->get_channel(), channel_name)) return i; } } return -1; } Master_info *Multisource_info::get_mi_at_pos(uint pos) { DBUG_TRACE; m_channel_map_lock->assert_some_lock(); if (pos < MAX_CHANNELS) return rpl_pfs_mi[pos]; return nullptr; } Rpl_filter *Rpl_channel_filters::create_filter(const char *channel_name) { DBUG_TRACE; Rpl_filter *rpl_filter; filter_map::iterator it; std::pair ret; rpl_filter = new Rpl_filter; m_channel_to_filter_lock->wrlock(); it = channel_to_filter.find(channel_name); assert(it == channel_to_filter.end()); ret = channel_to_filter.insert( std::pair(channel_name, rpl_filter)); #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE reset_pfs_view(); #endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */ m_channel_to_filter_lock->unlock(); if (DBUG_EVALUATE_IF("simulate_out_of_memory_on_create_filter", 1, 0) || !ret.second) { LogErr(ERROR_LEVEL, ER_FAILED_TO_ADD_RPL_FILTER, channel_name); my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 0); return nullptr; } return rpl_filter; } void Rpl_channel_filters::delete_filter(Rpl_filter *rpl_filter) { DBUG_TRACE; /* Traverse the filter map. */ m_channel_to_filter_lock->wrlock(); for (filter_map::iterator it = channel_to_filter.begin(); it != channel_to_filter.end(); it++) { if (it->second == rpl_filter) { /* Find the replication filter and delete it. */ delete it->second; channel_to_filter.erase(it); #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE reset_pfs_view(); #endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */ m_channel_to_filter_lock->unlock(); return; } } m_channel_to_filter_lock->unlock(); } void Rpl_channel_filters::discard_group_replication_filters() { /* Traverse the filter map. */ m_channel_to_filter_lock->wrlock(); filter_map::iterator it = channel_to_filter.begin(); while (it != channel_to_filter.end()) { if (channel_map.is_group_replication_channel_name(it->first.c_str())) { LogErr(WARNING_LEVEL, ER_PER_CHANNEL_RPL_FILTER_CONF_FOR_GRP_RPL, it->first.c_str()); delete it->second; it->second = nullptr; it = channel_to_filter.erase(it); } else it++; } m_channel_to_filter_lock->unlock(); } void Rpl_channel_filters::discard_all_unattached_filters() { DBUG_TRACE; /* Traverse the filter map. */ m_channel_to_filter_lock->wrlock(); filter_map::iterator it = channel_to_filter.begin(); while (it != channel_to_filter.end()) { if (it->second->is_attached()) { /* Do not discard the replication filter if it is attached to a channel. */ it++; continue; } /* Discard the replication filter with a warning if it is not attached to a channel. */ delete it->second; it->second = nullptr; LogErr(WARNING_LEVEL, ER_RPL_FILTERS_NOT_ATTACHED_TO_CHANNEL, it->first.c_str()); it = channel_to_filter.erase(it); } /* Reset the P_S view at the end of server startup */ #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE reset_pfs_view(); #endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */ m_channel_to_filter_lock->unlock(); } Rpl_filter *Rpl_channel_filters::get_channel_filter(const char *channel_name) { DBUG_TRACE; filter_map::iterator it; Rpl_filter *rpl_filter = nullptr; assert(channel_name != nullptr); m_channel_to_filter_lock->rdlock(); it = channel_to_filter.find(channel_name); if (it == channel_to_filter.end()) { m_channel_to_filter_lock->unlock(); rpl_filter = create_filter(channel_name); } else { rpl_filter = it->second; m_channel_to_filter_lock->unlock(); } return rpl_filter; } #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE void Rpl_channel_filters::reset_pfs_view() { DBUG_TRACE; m_channel_to_filter_lock->assert_some_wrlock(); rpl_pfs_filter_vec.clear(); // Traverse the filter map. for (filter_map::iterator it = channel_to_filter.begin(); it != channel_to_filter.end(); it++) { it->second->rdlock(); it->second->put_filters_into_vector(rpl_pfs_filter_vec, it->first.c_str()); it->second->unlock(); } } Rpl_pfs_filter *Rpl_channel_filters::get_filter_at_pos(uint pos) { DBUG_TRACE; m_channel_to_filter_lock->assert_some_rdlock(); Rpl_pfs_filter *res = nullptr; if (pos < rpl_pfs_filter_vec.size()) res = &rpl_pfs_filter_vec[pos]; return res; } uint Rpl_channel_filters::get_filter_count() { DBUG_TRACE; m_channel_to_filter_lock->assert_some_rdlock(); return rpl_pfs_filter_vec.size(); } #endif /*WITH_PERFSCHEMA_STORAGE_ENGINE */ bool Rpl_channel_filters::build_do_and_ignore_table_hashes() { DBUG_TRACE; /* Traverse the filter map. */ m_channel_to_filter_lock->rdlock(); for (filter_map::iterator it = channel_to_filter.begin(); it != channel_to_filter.end(); it++) { if (it->second->build_do_table_hash() || it->second->build_ignore_table_hash()) { LogErr(ERROR_LEVEL, ER_FAILED_TO_BUILD_DO_AND_IGNORE_TABLE_HASHES); return true; } } m_channel_to_filter_lock->unlock(); return false; } #endif /*WITH_PERFSCHEMA_STORAGE_ENGINE */ /* There is only one channel_map for the whole server */ Multisource_info channel_map; /* There is only one rpl_channel_filters for the whole server */ Rpl_channel_filters rpl_channel_filters;