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,21 @@ +[set up circular replication on two servers] +include/rpl_init.inc [topology=1->2->1] + +[preparing data on server1] +CREATE TABLE IF NOT EXISTS t (a VARCHAR(100)) ENGINE=InnoDB; +[skip create table events on server1] +call mtr.add_suppression("Detect dual master circular topological structure *"); +STOP SLAVE; +STOP SLAVE; +INSERT INTO t values('abc'); +SET GLOBAL SERVER_ID = 3; +START SLAVE; +START SLAVE; + +*** detect event with circle A->B->A *** + +[clean up] +SET GLOBAL SERVER_ID = 1; +DROP TABLE IF EXISTS t; +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,86 @@ +# ==== 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 ==== +# + +# 1. setup environment: +# server1(server_id=1) <---(dual master)--> server2(server_id=2) +# 2. create table t on server1, and it should be replicated to server2 +# 3. stop slave on server1 +# 4. insert one record on server1 +# 5. change server_id from 1 to 3 on server1 +# 6. start slave on server1 +# 7. expected record more than 1 on server1 (also warning of dual master for event) + + +--source include/have_innodb.inc +# Use wait_for_slave_to_(start|stop) for current connections +let $keep_connection= 1; + +# 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 + +# Preparing data on server1. +--echo [preparing data on server1] +--connection server_1 + +# make sure log_slave_updates is on +#SET session log_slave_updates=on; + +#CREATE TABLE IF NOT EXISTS t (a INT NOT NULL AUTO_INCREMENT, b VARCHAR(100), PRIMARY KEY(a)) ENGINE=InnoDB; +CREATE TABLE IF NOT EXISTS t (a VARCHAR(100)) ENGINE=InnoDB; + +--echo [skip create table events on server1] +#CHANGE MASTER TO master_log_pos=276; + +call mtr.add_suppression("Detect dual master circular topological structure *"); + +--real_sleep 1 +STOP SLAVE; + +--connection server_2 +STOP SLAVE; + +--connection server_1 +INSERT INTO t values('abc'); + +SET GLOBAL SERVER_ID = 3; +START SLAVE; + +--connection server_2 +START SLAVE; + + +#--real_sleep 3 + +--connection server_1 +let $wait_condition= SELECT COUNT(*) > 10 FROM t; +--source include/wait_condition.inc +#select * from t; + +--connection server_2 +#select * from t; + +--echo +--echo *** detect event with circle A->B->A *** +--echo + +--disable_warnings + +# Clean up environment +--echo [clean up] + +--connection server_1 +SET GLOBAL SERVER_ID = 1; +DROP TABLE IF EXISTS t; + +--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() */ Index: Percona-Server-5.5.18-rel23.0/mysql-test/suite/rpl/t/rpl_circular_event_detect.cnf =================================================================== --- /dev/null +++ Percona-Server-5.5.18-rel23.0/mysql-test/suite/rpl/t/rpl_circular_event_detect.cnf @@ -0,0 +1,10 @@ +!include ../my.cnf + +[mysqld.1] +log-slave-updates +innodb + +[mysqld.2] +log-slave-updates +innodb + Index: Percona-Server-5.5.18-rel23.0/sql/sql_repl.h =================================================================== --- Percona-Server-5.5.18-rel23.0.orig/sql/sql_repl.h +++ Percona-Server-5.5.18-rel23.0/sql/sql_repl.h @@ -32,6 +32,14 @@ typedef struct st_slave_info THD* thd; } SLAVE_INFO; +typedef enum en_list_op { OP_INSERT=0, OP_DELETE } list_op; + +extern LIST *slave_id_list; +extern int set_warning; +extern pthread_mutex_t slave_id_list_mutex; + +void update_slave_id_list(uint32 server_id, list_op op); + extern my_bool opt_show_slave_auth_info; extern char *master_host, *master_info_file; extern bool server_id_supplied;