Index: Percona-Server-5.5.18-rel23.0/mysql-test/suite/rpl/r/rpl_circular_event_detect.result =================================================================== --- /dev/null +++ Percona-Server-5.5.18-rel23.0/mysql-test/suite/rpl/r/rpl_circular_event_detect.result @@ -0,0 +1,16 @@ +*** Set up circular replication on two servers *** +include/rpl_init.inc [topology=1->2->1] + +STOP SLAVE; +CREATE TABLE IF NOT EXISTS t2 (a INT NOT NULL AUTO_INCREMENT, b VARCHAR(100), c INT NOT NULL, PRIMARY KEY(a)) ENGINE=InnoDB; +call mtr.add_suppression("Detect *"); +SET GLOBAL SERVER_ID = 11; +START SLAVE; + +*** detect event with circle A->B->A *** + +*** Clean up *** +SET GLOBAL SERVER_ID = 1; +DROP TABLE IF EXISTS t2; +include/rpl_reset.inc +include/rpl_end.inc Index: Percona-Server-5.5.18-rel23.0/mysql-test/suite/rpl/t/rpl_circular_event_detect.test =================================================================== --- /dev/null +++ Percona-Server-5.5.18-rel23.0/mysql-test/suite/rpl/t/rpl_circular_event_detect.test @@ -0,0 +1,59 @@ +# ==== Purpose ==== +# +# Confirm abnormal event with another server_id would be passed +# among the circular replacted hosts, and it should be detected +# with warning information. +# +# +# ==== Related bugs and worklogs ==== +# + +--source include/have_innodb.inc + + +# server1(server_id=1) <---(dual master)--> server2(server_id=2) +# 1. stop slave on server1 +# 2. insert one record on server1 +# 3. change server_id from 1 to 11 on server1 +# 4. start slave on server1 +# 5. expected waring of dual master for event + +# Use wait_for_slave_to_(start|stop) for current connections +let $keep_connection= 0; + +# Set up circular ring and new names for servers +--echo *** Set up circular replication on two servers *** +--let $rpl_topology= 1->2->1 +--source include/rpl_init.inc +--echo + +--connection server_1 +STOP SLAVE; + +# Preparing data on server1. +--connection server_1 +CREATE TABLE IF NOT EXISTS t2 (a INT NOT NULL AUTO_INCREMENT, b VARCHAR(100), c INT NOT NULL, PRIMARY KEY(a)) ENGINE=InnoDB; + +call mtr.add_suppression("Detect *"); +#call mtr.add_suppression("Slave SQL: Error *"); + +SET GLOBAL SERVER_ID = 11; +START SLAVE; + +--real_sleep 1 + +--echo +--echo *** detect event with circle A->B->A *** +--echo + +--disable_warnings + +# Clean up +--echo *** Clean up *** + +--connection server_1 +SET GLOBAL SERVER_ID = 1; +DROP TABLE IF EXISTS t2; + +--source include/rpl_reset.inc +--source include/rpl_end.inc Index: Percona-Server-5.5.18-rel23.0/sql/mysqld.cc =================================================================== --- Percona-Server-5.5.18-rel23.0.orig/sql/mysqld.cc +++ Percona-Server-5.5.18-rel23.0/sql/mysqld.cc @@ -374,6 +374,10 @@ my_bool locked_in_memory; bool opt_using_transactions; bool volatile abort_loop; bool volatile shutdown_in_progress; +LIST *slave_id_list=NULL; +int set_warning; +pthread_mutex_t slave_id_list_mutex; + /* True if the bootstrap thread is running. Protected by LOCK_thread_count, just like thread_count. @@ -1550,6 +1554,7 @@ void clean_up(bool print_message) mysql_cond_broadcast(&COND_thread_count); mysql_mutex_unlock(&LOCK_thread_count); sys_var_end(); + list_free(slave_id_list, 1); /* The following lines may never be executed as the main thread may have @@ -1583,6 +1588,7 @@ static void wait_for_signal_thread_to_en static void clean_up_mutexes() { + (void)pthread_mutex_destroy(&slave_id_list_mutex); mysql_rwlock_destroy(&LOCK_grant); mysql_mutex_destroy(&LOCK_thread_count); mysql_mutex_destroy(&LOCK_status); @@ -3632,6 +3638,7 @@ You should consider changing lower_case_ static int init_thread_environment() { + pthread_mutex_init(&slave_id_list_mutex, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_thread_count, &LOCK_thread_count, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_status, &LOCK_status, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_delayed_insert, Index: Percona-Server-5.5.18-rel23.0/sql/slave.cc =================================================================== --- Percona-Server-5.5.18-rel23.0.orig/sql/slave.cc +++ Percona-Server-5.5.18-rel23.0/sql/slave.cc @@ -3848,6 +3848,35 @@ static int queue_old_event(Master_info * } } +/* loop server's slave id list to check whether exists the server_id. + * return index if found, else return -1 + */ +static int find_in_slave_id_list(uint32 server_id) +{ + if (!slave_id_list) + return -1; + + int index=0; + int found=0; + + pthread_mutex_lock(&slave_id_list_mutex); + LIST *list = slave_id_list; + for (index=0; list ; list=list->next, index++) { + if (*(uint32*)list->data == server_id) { + found = 1; + break; + } + } + + pthread_mutex_unlock(&slave_id_list_mutex); + + if (found) + return index; + else + return -1; +} + + /* queue_event() @@ -4060,6 +4089,22 @@ static int queue_event(Master_info* mi,c } else { + if (s_id != ::server_id && + -1 == find_in_slave_id_list(s_id) && + -1 != find_in_slave_id_list(mi->master_id)) { + /* if replicated event's server_id is neither the same with current + * instance's id, nor current instance's id, a warning should be given + * for dual mastser topological structure. + */ + if (set_warning) { + sql_print_warning( + "Detect dual master circular topological structure: " + "event_sid=%ld, server id=%ld, master(slave) id=%ld ", + s_id, ::server_id, mi->master_id); + set_warning = 0; + } + } + /* write the event to the relay log */ if (likely(!(rli->relay_log.appendv(buf,event_len,0)))) { Index: Percona-Server-5.5.18-rel23.0/sql/sql_parse.cc =================================================================== --- Percona-Server-5.5.18-rel23.0.orig/sql/sql_parse.cc +++ Percona-Server-5.5.18-rel23.0/sql/sql_parse.cc @@ -1235,10 +1235,12 @@ bool dispatch_command(enum enum_server_c kill_zombie_dump_threads(slave_server_id); thd->server_id = slave_server_id; + update_slave_id_list(slave_server_id, OP_INSERT); general_log_print(thd, command, "Log: '%s' Pos: %ld", packet+10, (long) pos); mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags); unregister_slave(thd,1,1); + update_slave_id_list(slave_server_id, OP_DELETE); /* fake COM_QUIT -- if we get here, the thread needs to terminate */ error = TRUE; break; Index: Percona-Server-5.5.18-rel23.0/sql/sql_repl.cc =================================================================== --- Percona-Server-5.5.18-rel23.0.orig/sql/sql_repl.cc +++ Percona-Server-5.5.18-rel23.0/sql/sql_repl.cc @@ -429,6 +429,37 @@ static int send_heartbeat_event(NET* net DBUG_RETURN(0); } +/* update global slave id list when binlog dump thread is started or exited */ +void update_slave_id_list(uint32 server_id, list_op op) +{ + pthread_mutex_lock(&slave_id_list_mutex); + LIST *list = slave_id_list; + //fprintf(stderr, "op=%d id=%d len=%d\n", op, server_id, list_length(list)); + + set_warning = 1; + + if (op == OP_INSERT) { + LIST *element = (LIST*)my_malloc(sizeof(LIST*), MYF(MY_FAE)); + element->data = (void*)my_malloc(sizeof(void*), MYF(MY_FAE|MY_ZEROFILL)); + memcpy(element->data, &server_id, sizeof(uint32)); + slave_id_list = list_add(slave_id_list, element); + } else if (op == OP_DELETE) { + while (list) { + if (*(uint32*)list->data == server_id) { + //fprintf(stderr, "\t found it !\n"); + slave_id_list = list_delete(slave_id_list, list); + my_free(list->data); + my_free(list); + break; + } + list=list->next; + } + } + + pthread_mutex_unlock(&slave_id_list_mutex); +} + + /* TODO: Clean up loop to only have one call to send_file() */