Hi Andrei and Daogang,
The patch has been committed, please review it, thanks.
Only the comments are changed.
On Wed, 2010-03-10 at 17:41 +0200, Andrei Elkin wrote:
> Li-Bing, hello.
>
> The latest patch is good.
> Thanks for addressing those small suggestions!
>
> I have the last thing to ask to consider a small change in the heading comments
> of init_server_auto_options().
> Also one remark is inlined.
>
> > #At file:///home/anders/work/bzrwork/worktree1/mysql-5.1-rep%2B2/ based on
> revid:sven.sandberg@stripped
> >
> > 3182 Li-Bing.Song@stripped 2010-03-10
> > WL#4677: Unique Server Ids for Replication Topology (UUIDs)
> >
>
> > +
> > +/**
>
> I suggest to replace the following paragraph
>
> - > + File 'auto.cnf' in data direcory is used to save all options which was
> - > + generated by servers themselves. This options are needed by servers.
> - > + These options, however, need not be provided by users. So they are stored
> - > + independent in 'auto.cnf'.
>
> with the combination of mine and yours
>
> + File 'auto.cnf' resides in the data directory to hold values of options
> + that server evaluates itself and that needs to be durable to sustain
> + the server restart.
> + Note, the user may not supply any literal value to these auto-options, and
> + only allowed to trigger (re)evaluation.
> + For instance, `server_uuid' value will be evaluated and stored if
> + there is no corresponding line in the file.
> + Because of the specifics of the auto-options they need a separate storage.
> + Meanwhile it's 'auto.cnf' that has the same structure as 'my.cnf'.
>
> > + There is a section '[auto]' in the file. All these options are in the
> section.
> > + Now, Only one option exists, it is server_uuid.
>
> + (todo: consider to implement sql-query-able persistent storage by WL#5279).
>
> Yes, I think we are better to "adveratize" to delevopers a possibility to convert
> this extra cnf file into a table. We can't solve this task now "only" because
> there is no notion of the read-only table.
>
> - > +
> - > + The options needed by servers will be generated and than the file will be
> - > + refreshed if they did not exist in the file.
>
> The last two lines are unnecesary because a protocol of updating a line in
> the file is the option-specific and the earlier paragraph explains how
> `server_uuid' is (re)evaluated.
>
>
>
> > +
> > + @return Return 0 or 1 if an error occurred.
> > + */
> > +static int init_server_auto_options()
> > +{
> > + bool flush= false;
> > + char fname[FN_REFLEN];
> > + char name[]= "auto";
> > + const char *groups[]= {"auto", NULL};
> > + char *uuid= 0;
> > + my_option auto_options[]= {
> > + {"server-uuid", 0, "", (uchar**) &uuid, (uchar **) &uuid,
> > + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
> > + {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
> > + };
> > +
> > + DBUG_ENTER("init_server_auto_options");
> > +
> > + if (NULL == fn_format(fname, "auto.cnf", mysql_data_home, "",
> > + MY_UNPACK_FILENAME | MY_SAFE_PATH))
> > + DBUG_RETURN(1);
> > +
> > + /* load_defaults require argv[0] is not null */
> > + char **argv= (char **)(&name);
> > + int argc= 1;
> > + /* load all options in 'auto.cnf'. */
> > + if (my_load_defaults(fname, groups, &argc, &argv, NULL))
> > + DBUG_RETURN(1);
> > +
> > + /*
> > + Record the origial pointer allocated by my_load_defaults for free,
> > + because argv will be changed by handle_options
> > + */
> > + char **old_argv= argv;
> > + if (handle_options(&argc, &argv, auto_options,
> mysqld_get_one_option))
> > + DBUG_RETURN(1);
> > +
> > + if (uuid)
> > + {
> > + if (strlen(uuid) != UUID_LENGTH)
> > + {
> > + sql_print_error("Invalid UUID in the auto.cnf file");
> > + goto err;
> > + }
> > + strcpy(server_uuid, uuid);
> > + }
> > + else
> > + {
> > + flush= TRUE;
> > + /* server_uuid will be set in the function */
> > + if (generate_server_uuid())
> > + goto err;
> > + }
> > + /*
> > + The uuid has been copied to server_uuid, so the memory allocated by
> > + my_load_defaults can be freed now.
> > + */
> > + free_defaults(old_argv);
> > +
> > + if (flush)
> > + DBUG_RETURN(flush_auto_options(fname));
> > + DBUG_RETURN(0);
> > +err:
> > + free_defaults(argv);
> > + DBUG_RETURN(1);
> > +}
> >
> > static int init_server_components()
> > {
> > @@ -4002,6 +4152,16 @@ a file name for --log-bin-index option",
> > }
> > }
> >
> > + /*
> > + Each server should have one UUID. We will create it automatically, if it
> > + does not exist.
> > + */
> > + if (!opt_bootstrap && init_server_auto_options())
> > + {
> > + sql_print_error("Initializing server's UUID failed");
> > + unireg_abort(1);
> > + }
> > +
> > /* if the errmsg.sys is not loaded, terminate to maintain behaviour */
> > if (!errmesg[0][0])
> > unireg_abort(1);
> > @@ -7759,6 +7919,7 @@ static int mysql_init_variables(void)
> > opt_debug_sync_timeout= 0;
> > #endif /* defined(ENABLED_DEBUG_SYNC) */
> > key_map_full.set_all();
> > + server_uuid[0]= 0;
> >
> > /* Character sets */
> > system_charset_info= &my_charset_utf8_general_ci;
> >
> > === modified file 'sql/repl_failsafe.cc'
> > --- a/sql/repl_failsafe.cc 2010-02-12 23:30:44 +0000
> > +++ b/sql/repl_failsafe.cc 2010-03-09 16:47:38 +0000
> > @@ -684,6 +684,7 @@ bool show_slave_hosts(THD* thd)
> > field_list.push_back(new Item_return_int("Port", 7, MYSQL_TYPE_LONG));
> > field_list.push_back(new Item_return_int("Master_id", 10,
> > MYSQL_TYPE_LONG));
> > + field_list.push_back(new Item_empty_string("Slave_UUID", UUID_LENGTH));
> >
> > if (protocol->send_fields(&field_list,
> > Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
> > @@ -704,6 +705,11 @@ bool show_slave_hosts(THD* thd)
> > }
> > protocol->store((uint32) si->port);
> > protocol->store((uint32) si->master_id);
> > +
> > + /* get slave's UUID */
> > + String slave_uuid;
> > + if (get_slave_uuid(si->thd, &slave_uuid));
> > + protocol->store(slave_uuid.c_ptr_safe(), &my_charset_bin);
> > if (protocol->write())
> > {
> > pthread_mutex_unlock(&LOCK_slave_list);
> >
> > === modified file 'sql/rpl_mi.cc'
> > --- a/sql/rpl_mi.cc 2009-11-04 12:28:20 +0000
> > +++ b/sql/rpl_mi.cc 2010-03-09 16:47:38 +0000
> > @@ -41,6 +41,7 @@ Master_info::Master_info(bool is_slave_r
> > host[0] = 0; user[0] = 0; password[0] = 0;
> > ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0;
> > ssl_cipher[0]= 0; ssl_key[0]= 0;
> > + master_uuid[0]=0;
> >
> > my_init_dynamic_array(&ignore_server_ids, sizeof(::server_id), 16, 16);
> > bzero((char*) &file, sizeof(file));
> > @@ -132,8 +133,9 @@ enum {
> > LINE_FOR_MASTER_HEARTBEAT_PERIOD= 16,
> > /* 6.0 added value of master_ignore_server_id */
> > LINE_FOR_REPLICATE_IGNORE_SERVER_IDS= 17,
> > + LINE_FOR_MASTER_UUID= 18,
> > /* Number of lines currently used when saving master info file */
> > - LINES_IN_MASTER_INFO= LINE_FOR_REPLICATE_IGNORE_SERVER_IDS
> > + LINES_IN_MASTER_INFO= LINE_FOR_MASTER_UUID
> > };
> >
> > int init_master_info(Master_info* mi, const char* master_info_fname,
> > @@ -335,6 +337,9 @@ file '%s')", fname);
> > sql_print_error("Failed to initialize master info ignore_server_ids");
> > goto errwithmsg;
> > }
> > + if (lines >= LINE_FOR_MASTER_UUID &&
> > + init_strvar_from_file(mi->master_uuid, sizeof(mi->master_uuid),
> &mi->file, 0))
> > + goto errwithmsg;
> > }
> >
> > #ifndef HAVE_OPENSSL
> > @@ -464,14 +469,14 @@ int flush_master_info(Master_info* mi, b
> > my_sprintf(heartbeat_buf, (heartbeat_buf, "%.3f", mi->heartbeat_period));
> > my_b_seek(file, 0L);
> > my_b_printf(file,
> > -
> "%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n",
> > +
> "%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n%s\n",
> > LINES_IN_MASTER_INFO,
> > mi->master_log_name, llstr(mi->master_log_pos, lbuf),
> > mi->host, mi->user,
> > mi->password, mi->port, mi->connect_retry,
> > (int)(mi->ssl), mi->ssl_ca, mi->ssl_capath,
> mi->ssl_cert,
> > mi->ssl_cipher, mi->ssl_key,
> mi->ssl_verify_server_cert,
> > - heartbeat_buf, ignore_server_ids_buf);
> > + heartbeat_buf, ignore_server_ids_buf, mi->master_uuid);
> > my_free(ignore_server_ids_buf, MYF(0));
> > err= flush_io_cache(file);
> > if (sync_masterinfo_period && !err &&
> >
> > === modified file 'sql/rpl_mi.h'
> > --- a/sql/rpl_mi.h 2009-11-04 12:28:20 +0000
> > +++ b/sql/rpl_mi.h 2010-03-09 16:47:38 +0000
> > @@ -112,6 +112,7 @@ class Master_info : public Slave_reporti
> > ulonglong received_heartbeats; // counter of received heartbeat events
> > DYNAMIC_ARRAY ignore_server_ids;
> > ulong master_id;
> > + char master_uuid[UUID_LENGTH+1];
> > };
> > void init_master_log_pos(Master_info* mi);
> > int init_master_info(Master_info* mi, const char* master_info_fname,
> >
> > === modified file 'sql/set_var.cc'
> > --- a/sql/set_var.cc 2010-02-17 07:58:43 +0000
> > +++ b/sql/set_var.cc 2010-03-09 16:47:38 +0000
> > @@ -532,6 +532,10 @@ static sys_var_thd_ulong sys_query_alloc
> > static sys_var_thd_ulong sys_query_prealloc_size(&vars,
> "query_prealloc_size",
> > &SV::query_prealloc_size,
> > 0, fix_thd_mem_root);
> > +
> > +static sys_var_const sys_server_uuid(&vars, "server_uuid",
> > + OPT_GLOBAL, SHOW_CHAR,
> > + (uchar *) server_uuid);
> > #ifdef HAVE_SMEM
> > /* purecov: begin tested */
> > static sys_var_const sys_shared_memory(&vars, "shared_memory",
> >
> > === modified file 'sql/slave.cc'
> > --- a/sql/slave.cc 2010-02-12 23:30:44 +0000
> > +++ b/sql/slave.cc 2010-03-09 16:47:38 +0000
> > @@ -145,6 +145,7 @@ static int connect_to_master(THD* thd, M
> > static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed,
> > void* thread_killed_arg);
> > static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi);
> > +static int get_master_uuid(MYSQL *mysql, Master_info *mi);
> > static Log_event* next_event(Relay_log_info* rli);
> > static int queue_event(Master_info* mi,const char* buf,ulong event_len);
> > static int terminate_slave_thread(THD *thd,
> > @@ -1131,6 +1132,95 @@ bool is_network_error(uint errorno)
> > return FALSE;
> > }
> >
> > +/**
> > + An auxiliary function extracts slave UUID.
> > +
> > + @param[in] thd THD to access a user variable
> > + @param[out] value String to return UUID value.
> > +
> > + @return if success value is returned else NULL is returned.
> > +*/
> > +String *get_slave_uuid(THD *thd, String *value)
> > +{
> > + uchar name[]= "slave_uuid";
> > + my_bool null_value;
> > +
> > + if (value == NULL)
> > + return NULL;
> > + user_var_entry *entry=
> > + (user_var_entry*) hash_search(&thd->user_vars, name,
> sizeof(name)-1);
> > + if (entry && entry->length > 0)
> > + {
> > + value->copy(entry->value, entry->length, NULL);
> > + return value;
> > + }
> > + else
> > + return NULL;
> > +}
> > +
> > +/**
> > + Get master's uuid on connecting.
> > +
> > + @param mysql MYSQL to request uuid from master.
> > + @param mi Master_info to set master_uuid
> > +
> > + @return 0: Success, 1: Fatal error, 2: Network error.
> > +*/
> > +static int get_master_uuid(MYSQL *mysql, Master_info *mi)
> > +{
> > + const char *errmsg;
> > + MYSQL_RES *master_res= NULL;
> > + MYSQL_ROW master_row= NULL;
> > + int ret= 0;
> > +
> > + if (!mysql_real_query(mysql,
> > + STRING_WITH_LEN("SHOW VARIABLES LIKE 'SERVER_UUID'"))
> &&
> > + (master_res= mysql_store_result(mysql)) &&
> > + (master_row= mysql_fetch_row(master_res)))
> > + {
> > + if (!strcmp(::server_uuid, master_row[1]) &&
> > + !mi->rli.replicate_same_server_id)
> > + {
> > + errmsg= "The slave I/O thread stops because master and slave have equal
> "
> > + "MySQL server UUIDs; these UUIDs must be different for
> replication to work.";
> > + mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
> ER(ER_SLAVE_FATAL_ERROR), errmsg);
> > + // Fatal error
> > + ret= 1;
> > + }
> > + else
> > + {
> > + if (mi->master_uuid[0] != 0 && strcmp(mi->master_uuid,
> master_row[1]))
> > + sql_print_warning("Master's UUID has changed, its old UUID is %s, "
> > + "the new one is %s", mi->master_uuid,
> master_row[1]);
> > + strncpy(mi->master_uuid, master_row[1], UUID_LENGTH);
> > + mi->master_uuid[UUID_LENGTH]= 0;
> > + }
> > + }
> > + else if (mysql_errno(mysql))
> > + {
> > + if (is_network_error(mysql_errno(mysql)))
> > + {
> > + mi->report(WARNING_LEVEL, mysql_errno(mysql),
> > + "Get master SERVER_UUID failed with error: %s",
> mysql_error(mysql));
> > + ret= 2;
> > + }
> > + /* Fatal error */
> > + errmsg= "The slave I/O thread stops because a fatal error is encountered "
> > + "when it try to get the value of SERVER_UUID variable from
> master.";
> > + mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, ER(ER_SLAVE_FATAL_ERROR),
> errmsg);
> > + ret= 1;
> > + }
> > + else if (!master_row && master_res)
> > + {
> > + mi->report(WARNING_LEVEL, ER_UNKNOWN_SYSTEM_VARIABLE,
> > + "Unknown system variable 'SERVER_UUID' on master, "
> > + "maybe it is a *VERY OLD MASTER*.");
> > + }
> > +
> > + if (master_res)
> > + mysql_free_result(master_res);
> > + return ret;
> > +}
> >
> > /*
> > Note that we rely on the master's version (3.23, 4.0.14 etc) instead of
> > @@ -1723,6 +1813,7 @@ bool show_master_info(THD* thd, Master_i
> > FN_REFLEN));
> > field_list.push_back(new Item_return_int("Master_Server_Id", sizeof(ulong),
> > MYSQL_TYPE_LONG));
> > + field_list.push_back(new Item_empty_string("Master_UUID", UUID_LENGTH));
> >
> > if (protocol->send_fields(&field_list,
> > Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
> > @@ -1871,6 +1962,7 @@ bool show_master_info(THD* thd, Master_i
> > }
> > // Master_Server_id
> > protocol->store((uint32) mi->master_id);
> > + protocol->store(mi->master_uuid, &my_charset_bin);
> >
> > pthread_mutex_unlock(&mi->rli.err_lock);
> > pthread_mutex_unlock(&mi->err_lock);
> > @@ -2712,6 +2804,9 @@ connected:
> > thd->slave_net = &mysql->net;
> > thd_proc_info(thd, "Checking master version");
> > ret= get_master_version_and_clock(mysql, mi);
> > + if (!ret)
> > + ret= get_master_uuid(mysql, mi);
> > +
> > if (ret == 1)
> > /* Fatal error */
> > goto err;
> > @@ -4049,6 +4144,8 @@ static int connect_to_master(THD* thd, M
> > int last_errno= -2; // impossible error
> > ulong err_count=0;
> > char llbuff[22];
> > + /* if your query is larger than 128, you must increase its size */
> > + char init_cmd[128];
> > DBUG_ENTER("connect_to_master");
> >
> > #ifndef DBUG_OFF
> > @@ -4079,6 +4176,9 @@ static int connect_to_master(THD* thd, M
> > /* This one is not strictly needed but we have it here for completeness */
> > mysql_options(mysql, MYSQL_SET_CHARSET_DIR, (char *) charsets_dir);
> >
> > + sprintf(init_cmd, "SET @SLAVE_UUID= '%s'", server_uuid);
> > + mysql_options(mysql, MYSQL_INIT_COMMAND, init_cmd);
> > +
> > while (!(slave_was_killed = io_slave_killed(thd,mi)) &&
> > (reconnect ? mysql_reconnect(mysql) != 0 :
> > mysql_real_connect(mysql, mi->host, mi->user, mi->password,
> 0,
> >
> > === modified file 'sql/slave.h'
> > --- a/sql/slave.h 2010-02-12 23:30:44 +0000
> > +++ b/sql/slave.h 2010-03-09 16:47:38 +0000
> > @@ -224,6 +224,7 @@ extern char *master_ssl_cipher, *master_
> >
> > extern I_List<THD> threads;
> >
> > +extern String *get_slave_uuid(THD *thd, String *value);
> > #endif /* HAVE_REPLICATION */
> >
> > /* masks for start/stop operations on io and sql slave threads */
> >
> > === modified file 'sql/sql_parse.cc'
> > --- a/sql/sql_parse.cc 2010-03-03 14:43:35 +0000
> > +++ b/sql/sql_parse.cc 2010-03-09 16:47:38 +0000
> > @@ -1304,7 +1304,7 @@ bool dispatch_command(enum enum_server_c
> > {
> > ulong pos;
> > ushort flags;
> > - uint32 slave_server_id;
> > + String slave_uuid;
> >
> > status_var_increment(thd->status_var.com_other);
> > thd->enable_slow_log= opt_log_slow_admin_statements;
> > @@ -1314,10 +1314,10 @@ bool dispatch_command(enum enum_server_c
> > /* TODO: The following has to be changed to an 8 byte integer */
> > pos = uint4korr(packet);
> > flags = uint2korr(packet + 4);
> > - thd->server_id=0; /* avoid suicide */
> > - if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
> > - kill_zombie_dump_threads(slave_server_id);
> > - thd->server_id = slave_server_id;
> > + thd->server_id= uint4korr(packet+6);
> > +
> > + get_slave_uuid(thd, &slave_uuid);
>
> > + kill_zombie_dump_threads(&slave_uuid);
>
> Just to repeat that we agreed on earlier.
> Killing the old server_uuid-unaware slaves, in pretty rare
> New master and Old slave setup, can be issue only if heartbeat is disabled/unaware.
> Otherwise the dump thread to the Old slave's IO thread ceases in the hb period.
>
> >
> > general_log_print(thd, command, "Log: '%s' Pos: %ld", packet+10,
> > (long) pos);
> >
> > === modified file 'sql/sql_repl.cc'
> > --- a/sql/sql_repl.cc 2010-03-03 14:43:35 +0000
> > +++ b/sql/sql_repl.cc 2010-03-09 16:47:38 +0000
> > @@ -1281,24 +1281,32 @@ err:
> >
> > SYNOPSIS
> > kill_zombie_dump_threads()
> > - slave_server_id the slave's server id
> > + slave_uuid the slave's UUID
> >
> > */
> >
> >
> > -void kill_zombie_dump_threads(uint32 slave_server_id)
> > +void kill_zombie_dump_threads(String *slave_uuid)
> > {
> > + if (slave_uuid->length() == 0)
> > + return;
> > + DBUG_ASSERT(slave_uuid->length() == UUID_LENGTH);
> > +
> > pthread_mutex_lock(&LOCK_thread_count);
> > I_List_iterator<THD> it(threads);
> > THD *tmp;
> >
> > while ((tmp=it++))
> > {
> > - if (tmp->command == COM_BINLOG_DUMP &&
> > - tmp->server_id == slave_server_id)
> > + if (tmp != current_thd && tmp->command == COM_BINLOG_DUMP)
> > {
> > - pthread_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
> > - break;
> > + String tmp_uuid;
> > + if (get_slave_uuid(tmp, &tmp_uuid) != NULL &&
> > + !strncmp(slave_uuid->c_ptr(), tmp_uuid.c_ptr(), UUID_LENGTH))
> > + {
> > + pthread_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
> > + break;
> > + }
> > }
> > }
> > pthread_mutex_unlock(&LOCK_thread_count);
> > @@ -1379,6 +1387,13 @@ bool change_master(THD* thd, Master_info
> > reset binlog's name to FIRST and position to 4.
> > */
> >
> > + if ((lex_mi->host && strcmp(lex_mi->host, mi->host)) ||
> > + (lex_mi->port && lex_mi->port != mi->port))
> > + {
> > + mi->master_uuid[0]= 0;
> > + mi->master_id= 0;
> > + }
> > +
> > if ((lex_mi->host || lex_mi->port) && !lex_mi->log_file_name
> && !lex_mi->pos)
> > {
> > mi->master_log_name[0] = 0;
> >
> > === modified file 'sql/sql_repl.h'
> > --- a/sql/sql_repl.h 2009-09-23 21:32:31 +0000
> > +++ b/sql/sql_repl.h 2010-03-09 16:47:38 +0000
> > @@ -53,7 +53,7 @@ bool log_in_use(const char* log_name);
> > void adjust_linfo_offsets(my_off_t purge_offset);
> > bool show_binlogs(THD* thd);
> > extern int init_master_info(Master_info* mi);
> > -void kill_zombie_dump_threads(uint32 slave_server_id);
> > +void kill_zombie_dump_threads(String *slave_uuid);
> > int check_binlog_magic(IO_CACHE* log, const char** errmsg);
> >
> > typedef struct st_load_file_info
>
>
> cheers,
>
> Andrei
--
Your Sincerely,
Libing Song
==================================
MySQL Replication Team
Software Engineer
Email : Li-Bing.Song@stripped
Skype : libing.song
MSN : slb_database@stripped
Phone : +86 010-6505-4020 ext. 319
Mobile: +86 138-1144-2038
==================================