@ -495,19 +495,23 @@ void MySQL_Monitor_Connection_Pool::put_connection(char *hostname, int port, MYS
/**
* @ brief MySQL 8 status query for Group Replication members .
* @ details Since ' MySQL 8 ' we rely on ' COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE ' , deprecating the previously
* required ' sys . gr_member_routing_candidate_status ' view .
* required ' sys . gr_member_routing_candidate_status ' view . Another additions :
* - A new field ' members ' has been added to the query , containing the current cluster members as seen by the
* queried node . This field is used for auto discovery .
* - Server state ' RECOVERING ' is now also considered when detecting if a member is a ' viable ' candidate .
*/
const char MYSQL_8_GR_QUERY [ ] {
" SELECT (SELECT IF ( "
" MEMBER_STATE='ONLINE' AND ( "
" (SELECT COUNT(*) FROM performance_schema.replication_group_members WHERE MEMBER_STATE != 'ONLINE' ) >="
" (SELECT COUNT(*) FROM performance_schema.replication_group_members WHERE MEMBER_STATE NOT IN ('ONLINE', 'RECOVERING') ) >="
" ((SELECT COUNT(*) FROM performance_schema.replication_group_members)/2) = 0) "
" , 'YES', 'NO')) AS viable_candidate, "
" (SELECT IF (@@read_only, 'YES', 'NO')) as read_only, "
" COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE AS transactions_behind "
" COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE AS transactions_behind, "
" (SELECT GROUP_CONCAT(CONCAT(member_host, \" : \" , member_port)) FROM performance_schema.replication_group_members) AS members "
" FROM "
" performance_schema.replication_group_members "
" JOIN performance_schema.replication_group_member_stats rgms USING(member_id) "
" JOIN performance_schema.replication_group_member_stats rgms USING(member_id) "
" WHERE rgms.MEMBER_ID=@@SERVER_UUID "
} ;
@ -616,7 +620,7 @@ void MySQL_Monitor_State_Data::init_async() {
async_state_machine_ = ASYNC_QUERY_START ;
# ifdef TEST_GROUPREP
{
query_ = " SELECT viable_candidate,read_only,transactions_behind FROM GR_MEMBER_ROUTING_CANDIDATE_STATUS " ;
query_ = " SELECT viable_candidate,read_only,transactions_behind ,members FROM GR_MEMBER_ROUTING_CANDIDATE_STATUS " ;
query_ + = std : : string ( hostname ) + " : " + std : : to_string ( port ) ;
}
# else
@ -1867,7 +1871,7 @@ void * monitor_group_replication_thread(void *arg) {
mmsd - > interr = 0 ; // reset the value
# ifdef TEST_GROUPREP
{
std : : string s { " SELECT viable_candidate,read_only,transactions_behind FROM GR_MEMBER_ROUTING_CANDIDATE_STATUS" } ;
std : : string s { " SELECT viable_candidate,read_only,transactions_behind ,members FROM GR_MEMBER_ROUTING_CANDIDATE_STATUS" } ;
s + = " " + std : : string ( mmsd - > hostname ) + " : " + std : : to_string ( mmsd - > port ) ;
mmsd - > async_exit_status = mysql_query_start ( & mmsd - > interr , mmsd - > mysql , s . c_str ( ) ) ;
}
@ -3373,18 +3377,6 @@ set<uint32_t> extract_writer_hgs(SQLite3_result* Group_Replication_Hosts_results
return writer_hgs ;
}
/**
* @ brief Holds the info from a GR server definition .
*/
typedef struct _gr_host_def_t {
string host ;
int port ;
int use_ssl ;
bool writer_is_also_reader ;
int max_transactions_behind ;
int max_transactions_behind_count ;
} gr_host_def_t ;
/**
* @ brief Extracts a ' MySQL_Monitor_State_Data ' from the provided ' SQLite3_result * ' .
* @ details The expected contents of the provided ' SQLite3_result * ' are the ones generated by
@ -3505,13 +3497,67 @@ unique_ptr<MySQL_Monitor_State_Data> init_mmsd_with_conn(
return mmsd ;
}
using gr_srv_addr_t = pair < string , int32_t > ;
struct gr_srv_st_t {
bool viable_candidate = false ;
bool read_only = true ;
int64_t transactions_behind = - 1 ;
bool inv_srv_state = false ;
vector < gr_srv_addr_t > gr_members { } ;
} ;
# define GR_MEMBER_ENTRY_ERR "%s '%s' in 'members' field from GR query to server '%s:%d'. Autodiscovery action aborted.\n"
vector < pair < string , int32_t > > parse_gr_members_addrs (
const MySQL_Monitor_State_Data * mmsd , const vector < string > & gr_cluster_members
) {
# ifdef DEBUG
nlohmann : : ordered_json members { gr_cluster_members } ;
proxy_debug (
PROXY_DEBUG_MONITOR , 7 , " Received 'members' field '%s' from GR query to server '%s:%d' \n " , members . dump ( ) . c_str ( ) ,
mmsd - > hostname , mmsd - > port
) ;
# endif
vector < pair < string , int32_t > > result { } ;
for ( const auto & cluster_member : gr_cluster_members ) {
const vector < string > gr_member_host_port { split_str ( cluster_member , ' : ' ) } ;
if ( gr_member_host_port . size ( ) ! = 2 ) {
proxy_error ( GR_MEMBER_ENTRY_ERR , " Invalid server entry " , cluster_member . c_str ( ) , mmsd - > hostname , mmsd - > port ) ;
break ;
}
const string srv_host { gr_member_host_port [ 0 ] } ;
const char * c_str_port { gr_member_host_port [ 1 ] . c_str ( ) } ;
int32_t srv_port = - 1 ;
{
char * p_end = nullptr ;
long port = std : : strtol ( c_str_port , & p_end , 10 ) ;
if ( c_str_port = = p_end ) {
proxy_error (
GR_MEMBER_ENTRY_ERR , " Failed to parse port for server entry " , cluster_member . c_str ( ) , mmsd - > hostname , mmsd - > port
) ;
break ;
} else {
srv_port = port ;
}
}
result . push_back ( { srv_host , srv_port } ) ;
}
// If any entry fails to parse, we invalidate the whole action
if ( gr_cluster_members . size ( ) ! = result . size ( ) ) {
return { } ;
} else {
return result ;
}
}
gr_srv_st_t extract_gr_srv_st ( MySQL_Monitor_State_Data * mmsd ) {
gr_srv_st_t gr_srv_st { } ;
@ -3522,7 +3568,7 @@ gr_srv_st_t extract_gr_srv_st(MySQL_Monitor_State_Data* mmsd) {
num_fields = mysql_num_fields ( mmsd - > result ) ;
num_rows = mysql_num_rows ( mmsd - > result ) ;
if ( fields = = NULL | | num_fields ! = 3 | | num_rows ! = 1 ) {
if ( fields = = NULL | | num_fields ! = 4 | | num_rows ! = 1 ) {
proxy_error (
" 'mysql_fetch_fields' returns 'NULL', or 'mysql_num_fields(%d)', or 'mysql_num_rows(%d)' are incorrect. "
" Server %s:%d. See bug #1994 \n " ,
@ -3543,11 +3589,17 @@ gr_srv_st_t extract_gr_srv_st(MySQL_Monitor_State_Data* mmsd) {
if ( row [ 2 ] ) {
gr_srv_st . transactions_behind = atol ( row [ 2 ] ) ;
}
if ( mmsd - > cur_monitored_gr_srvs & & row [ 3 ] ) {
const string str_members_addrs { row [ 3 ] } ;
const vector < string > members_addrs { split_str ( str_members_addrs , ' , ' ) } ;
gr_srv_st . gr_members = parse_gr_members_addrs ( mmsd , members_addrs ) ;
}
}
}
proxy_debug (
PROXY_DEBUG_MONITOR , 4 ,
PROXY_DEBUG_MONITOR , 7 ,
" Fetched %u:%s:%d info - interr: %d, error: %s, viable_candidate:'%d', read_only:'%d', "
" transactions_behind:'%ld' \n " ,
mmsd - > hostgroup_id , mmsd - > hostname , mmsd - > port , mmsd - > interr , mmsd - > mysql_error_msg ,
@ -3578,7 +3630,6 @@ gr_node_info_t gr_update_hosts_map(
// NOTE: This isn't specified in the initializer list due to current standard limitations
gr_node_info_t node_info { } ;
node_info . srv_st = gr_srv_st ;
MySQL_Monitor_State_Data_Task_Result task_result = mmsd - > get_task_result ( ) ;
// Consider 'time_now' to be 'now - fetch_duration'
unsigned long long time_now = realtime_time ( ) ;
@ -3671,6 +3722,24 @@ void gr_mon_action_over_resp_srv(MySQL_Monitor_State_Data* mmsd, const gr_node_i
MyHGM - > group_replication_lag_action (
mmsd - > writer_hostgroup , mmsd - > hostname , mmsd - > port , node_info . lag_counts , node_info . srv_st . read_only , enable
) ;
if ( mmsd - > cur_monitored_gr_srvs & & node_info . srv_st . gr_members . empty ( ) = = false ) {
for ( const gr_srv_addr_t & gr_member : node_info . srv_st . gr_members ) {
const string & srv_host { gr_member . first } ;
const int32_t srv_port { gr_member . second } ;
bool found = false ;
for ( const gr_host_def_t & host_def : * mmsd - > cur_monitored_gr_srvs ) {
if ( srv_host = = host_def . host & & srv_port = = host_def . port ) {
found = true ;
}
}
if ( found = = false ) {
MyHGM - > update_group_replication_add_autodiscovered ( srv_host , srv_port , mmsd - > writer_hostgroup ) ;
}
}
}
}
}
}
@ -3905,6 +3974,11 @@ void* monitor_GR_thread_HG(void *arg) {
}
}
int rnd_discoverer = conn_mmsds . size ( ) = = 0 ? - 1 : rand ( ) % conn_mmsds . size ( ) ;
if ( rnd_discoverer ! = - 1 ) {
conn_mmsds [ rnd_discoverer ] - > cur_monitored_gr_srvs = & hosts_defs ;
}
// TODO: This needs to be reworked once we change the way monitoring actions work on clusters, taking
// the full cluster fetch data to avoid transient states. For now, since we perform the monitoring
// actions independently, we workaround the limitation of 'Monitor_Poll' of only handling
@ -3934,6 +4008,10 @@ void* monitor_GR_thread_HG(void *arg) {
///////////////////////////////////////////////////////////////////////////////////////
if ( rnd_discoverer ! = - 1 ) {
conn_mmsds [ rnd_discoverer ] - > cur_monitored_gr_srvs = nullptr ;
}
// Set the time for the next iteration
next_check_time = curtime + mysql_thread___monitor_groupreplication_healthcheck_interval * 1000 ;
}