diff options
Diffstat (limited to 'sql/rpl_mi.cc')
-rw-r--r-- | sql/rpl_mi.cc | 272 |
1 files changed, 196 insertions, 76 deletions
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index 63f1f21c957..cda876ee764 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright (C) 2000-2003 MySQL AB, 2008-2009 Sun Microsystems, Inc This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,76 +14,112 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <my_global.h> // For HAVE_REPLICATION -#include "mysql_priv.h" +#include "sql_priv.h" #include <my_dir.h> - +#include "unireg.h" // REQUIRED by other includes #include "rpl_mi.h" +#include "slave.h" // SLAVE_MAX_HEARTBEAT_PERIOD #ifdef HAVE_REPLICATION +#define DEFAULT_CONNECT_RETRY 60 // Defined in slave.cc int init_intvar_from_file(int* var, IO_CACHE* f, int default_val); int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, const char *default_val); +int init_floatvar_from_file(float* var, IO_CACHE* f, float default_val); +int init_dynarray_intvar_from_file(DYNAMIC_ARRAY* arr, IO_CACHE* f); -Master_info::Master_info() +Master_info::Master_info(bool is_slave_recovery) :Slave_reporting_capability("I/O"), - ssl(0), ssl_verify_server_cert(0), fd(-1), io_thd(0), inited(0), - abort_slave(0),slave_running(0), - slave_run_id(0) + ssl(0), ssl_verify_server_cert(0), fd(-1), io_thd(0), + rli(is_slave_recovery), port(MYSQL_PORT), + connect_retry(DEFAULT_CONNECT_RETRY), inited(0), abort_slave(0), + slave_running(0), slave_run_id(0), sync_counter(0), + heartbeat_period(0), received_heartbeats(0), master_id(0) { 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; + my_init_dynamic_array(&ignore_server_ids, sizeof(::server_id), 16, 16); bzero((char*) &file, sizeof(file)); - pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); - pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); - pthread_cond_init(&data_cond, NULL); - pthread_cond_init(&start_cond, NULL); - pthread_cond_init(&stop_cond, NULL); + mysql_mutex_init(key_master_info_run_lock, &run_lock, MY_MUTEX_INIT_FAST); + mysql_mutex_init(key_master_info_data_lock, &data_lock, MY_MUTEX_INIT_FAST); + mysql_cond_init(key_master_info_data_cond, &data_cond, NULL); + mysql_cond_init(key_master_info_start_cond, &start_cond, NULL); + mysql_cond_init(key_master_info_stop_cond, &stop_cond, NULL); } Master_info::~Master_info() { - pthread_mutex_destroy(&run_lock); - pthread_mutex_destroy(&data_lock); - pthread_cond_destroy(&data_cond); - pthread_cond_destroy(&start_cond); - pthread_cond_destroy(&stop_cond); + delete_dynamic(&ignore_server_ids); + mysql_mutex_destroy(&run_lock); + mysql_mutex_destroy(&data_lock); + mysql_cond_destroy(&data_cond); + mysql_cond_destroy(&start_cond); + mysql_cond_destroy(&stop_cond); +} + +/** + A comparison function to be supplied as argument to @c sort_dynamic() + and @c bsearch() + + @return -1 if first argument is less, 0 if it equal to, 1 if it is greater + than the second +*/ +int change_master_server_id_cmp(ulong *id1, ulong *id2) +{ + return *id1 < *id2? -1 : (*id1 > *id2? 1 : 0); } -void init_master_info_with_options(Master_info* mi) +/** + Reports if the s_id server has been configured to ignore events + it generates with + + CHANGE MASTER IGNORE_SERVER_IDS= ( list of server ids ) + + Method is called from the io thread event receiver filtering. + + @param s_id the master server identifier + + @retval TRUE if s_id is in the list of ignored master servers, + @retval FALSE otherwise. + */ +bool Master_info::shall_ignore_server_id(ulong s_id) +{ + if (likely(ignore_server_ids.elements == 1)) + return (* (ulong*) dynamic_array_ptr(&ignore_server_ids, 0)) == s_id; + else + return bsearch((const ulong *) &s_id, + ignore_server_ids.buffer, + ignore_server_ids.elements, sizeof(ulong), + (int (*) (const void*, const void*)) change_master_server_id_cmp) + != NULL; +} + +void init_master_log_pos(Master_info* mi) { - DBUG_ENTER("init_master_info_with_options"); + DBUG_ENTER("init_master_log_pos"); mi->master_log_name[0] = 0; mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number - if (master_host) - strmake(mi->host, master_host, sizeof(mi->host) - 1); - if (master_user) - strmake(mi->user, master_user, sizeof(mi->user) - 1); - if (master_password) - strmake(mi->password, master_password, MAX_PASSWORD_LENGTH); - mi->port = master_port; - mi->connect_retry = master_connect_retry; - - mi->ssl= master_ssl; - if (master_ssl_ca) - strmake(mi->ssl_ca, master_ssl_ca, sizeof(mi->ssl_ca)-1); - if (master_ssl_capath) - strmake(mi->ssl_capath, master_ssl_capath, sizeof(mi->ssl_capath)-1); - if (master_ssl_cert) - strmake(mi->ssl_cert, master_ssl_cert, sizeof(mi->ssl_cert)-1); - if (master_ssl_cipher) - strmake(mi->ssl_cipher, master_ssl_cipher, sizeof(mi->ssl_cipher)-1); - if (master_ssl_key) - strmake(mi->ssl_key, master_ssl_key, sizeof(mi->ssl_key)-1); /* Intentionally init ssl_verify_server_cert to 0, no option available */ mi->ssl_verify_server_cert= 0; + /* + always request heartbeat unless master_heartbeat_period is set + explicitly zero. Here is the default value for heartbeat period + if CHANGE MASTER did not specify it. (no data loss in conversion + as hb period has a max) + */ + mi->heartbeat_period= (float) min(SLAVE_MAX_HEARTBEAT_PERIOD, + (slave_net_timeout/2.0)); + DBUG_ASSERT(mi->heartbeat_period > (float) 0.001 + || mi->heartbeat_period == 0); + DBUG_VOID_RETURN; } @@ -93,9 +129,14 @@ enum { /* 5.1.16 added value of master_ssl_verify_server_cert */ LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT= 15, - + /* 6.0 added value of master_heartbeat_period */ + LINE_FOR_MASTER_HEARTBEAT_PERIOD= 16, + /* MySQL Cluster 6.3 added master_bind */ + LINE_FOR_MASTER_BIND = 17, + /* 6.0 added value of master_ignore_server_id */ + LINE_FOR_REPLICATE_IGNORE_SERVER_IDS= 18, /* Number of lines currently used when saving master info file */ - LINES_IN_MASTER_INFO= LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT + LINES_IN_MASTER_INFO= LINE_FOR_REPLICATE_IGNORE_SERVER_IDS }; int init_master_info(Master_info* mi, const char* master_info_fname, @@ -123,7 +164,29 @@ int init_master_info(Master_info* mi, const char* master_info_fname, */ if (thread_mask & SLAVE_SQL) { + bool hot_log= FALSE; + /* + my_b_seek does an implicit flush_io_cache, so we need to: + + 1. check if this log is active (hot) + 2. if it is we keep log_lock until the seek ends, otherwise + release it right away. + + If we did not take log_lock, SQL thread might race with IO + thread for the IO_CACHE mutex. + + */ + mysql_mutex_t *log_lock= mi->rli.relay_log.get_log_lock(); + mysql_mutex_lock(log_lock); + hot_log= mi->rli.relay_log.is_active(mi->rli.linfo.log_file_name); + + if (!hot_log) + mysql_mutex_unlock(log_lock); + my_b_seek(mi->rli.cur_log, (my_off_t) 0); + + if (hot_log) + mysql_mutex_unlock(log_lock); } DBUG_RETURN(0); } @@ -137,7 +200,7 @@ int init_master_info(Master_info* mi, const char* master_info_fname, keep other threads from reading bogus info */ - pthread_mutex_lock(&mi->data_lock); + mysql_mutex_lock(&mi->data_lock); fd = mi->fd; /* does master.info exist ? */ @@ -146,7 +209,7 @@ int init_master_info(Master_info* mi, const char* master_info_fname, { if (abort_if_no_master_info_file) { - pthread_mutex_unlock(&mi->data_lock); + mysql_mutex_unlock(&mi->data_lock); DBUG_RETURN(0); } /* @@ -154,8 +217,9 @@ int init_master_info(Master_info* mi, const char* master_info_fname, the old descriptor and re-create the old file */ if (fd >= 0) - my_close(fd, MYF(MY_WME)); - if ((fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 ) + mysql_file_close(fd, MYF(MY_WME)); + if ((fd= mysql_file_open(key_file_master_info, + fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 ) { sql_print_error("Failed to create a new master info file (\ file '%s', errno %d)", fname, my_errno); @@ -170,7 +234,7 @@ file '%s')", fname); } mi->fd = fd; - init_master_info_with_options(mi); + init_master_log_pos(mi); } else // file exists @@ -179,7 +243,8 @@ file '%s')", fname); reinit_io_cache(&mi->file, READ_CACHE, 0L,0,0); else { - if ((fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 ) + if ((fd= mysql_file_open(key_file_master_info, + fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 ) { sql_print_error("Failed to open the existing master info file (\ file '%s', errno %d)", fname, my_errno); @@ -197,7 +262,9 @@ file '%s')", fname); mi->fd = fd; int port, connect_retry, master_log_pos, lines; int ssl= 0, ssl_verify_server_cert= 0; + float master_heartbeat_period= 0.0; char *first_non_digit; + char dummy_buf[HOSTNAME_LENGTH+1]; /* Starting from 4.1.x master.info has new format. Now its @@ -242,36 +309,34 @@ file '%s')", fname); lines= 7; if (init_intvar_from_file(&master_log_pos, &mi->file, 4) || - init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, - master_host) || - init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, - master_user) || + init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, 0) || + init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, "test") || init_strvar_from_file(mi->password, SCRAMBLED_PASSWORD_CHAR_LENGTH+1, - &mi->file, master_password) || - init_intvar_from_file(&port, &mi->file, master_port) || + &mi->file, 0) || + init_intvar_from_file(&port, &mi->file, MYSQL_PORT) || init_intvar_from_file(&connect_retry, &mi->file, - master_connect_retry)) + DEFAULT_CONNECT_RETRY)) goto errwithmsg; /* If file has ssl part use it even if we have server without - SSL support. But these option will be ignored later when + SSL support. But these options will be ignored later when slave will try connect to master, so in this case warning is printed. */ if (lines >= LINES_IN_MASTER_INFO_WITH_SSL) { - if (init_intvar_from_file(&ssl, &mi->file, master_ssl) || + if (init_intvar_from_file(&ssl, &mi->file, 0) || init_strvar_from_file(mi->ssl_ca, sizeof(mi->ssl_ca), - &mi->file, master_ssl_ca) || + &mi->file, 0) || init_strvar_from_file(mi->ssl_capath, sizeof(mi->ssl_capath), - &mi->file, master_ssl_capath) || + &mi->file, 0) || init_strvar_from_file(mi->ssl_cert, sizeof(mi->ssl_cert), - &mi->file, master_ssl_cert) || + &mi->file, 0) || init_strvar_from_file(mi->ssl_cipher, sizeof(mi->ssl_cipher), - &mi->file, master_ssl_cipher) || + &mi->file, 0) || init_strvar_from_file(mi->ssl_key, sizeof(mi->ssl_key), - &mi->file, master_ssl_key)) + &mi->file, 0)) goto errwithmsg; /* @@ -281,14 +346,37 @@ file '%s')", fname); if (lines >= LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT && init_intvar_from_file(&ssl_verify_server_cert, &mi->file, 0)) goto errwithmsg; - + /* + Starting from 6.0 master_heartbeat_period might be + in the file + */ + if (lines >= LINE_FOR_MASTER_HEARTBEAT_PERIOD && + init_floatvar_from_file(&master_heartbeat_period, &mi->file, 0.0)) + goto errwithmsg; + /* + Starting from MySQL Cluster 6.3 master_bind might be in the file + (this is just a reservation to avoid future upgrade problems) + */ + if (lines >= LINE_FOR_MASTER_BIND && + init_strvar_from_file(dummy_buf, sizeof(dummy_buf), &mi->file, "")) + goto errwithmsg; + /* + Starting from 6.0 list of server_id of ignorable servers might be + in the file + */ + if (lines >= LINE_FOR_REPLICATE_IGNORE_SERVER_IDS && + init_dynarray_intvar_from_file(&mi->ignore_server_ids, &mi->file)) + { + sql_print_error("Failed to initialize master info ignore_server_ids"); + goto errwithmsg; + } } #ifndef HAVE_OPENSSL if (ssl) sql_print_warning("SSL information in the master info file " - "('%s') are ignored because this MySQL slave was compiled " - "without SSL support.", fname); + "('%s') are ignored because this MySQL slave was " + "compiled without SSL support.", fname); #endif /* HAVE_OPENSSL */ /* @@ -300,6 +388,7 @@ file '%s')", fname); mi->connect_retry= (uint) connect_retry; mi->ssl= (my_bool) ssl; mi->ssl_verify_server_cert= ssl_verify_server_cert; + mi->heartbeat_period= master_heartbeat_period; } DBUG_PRINT("master_info",("log_file_name: %s position: %ld", mi->master_log_name, @@ -310,11 +399,12 @@ file '%s')", fname); goto err; mi->inited = 1; + mi->rli.is_relay_log_recovery= FALSE; // now change cache READ -> WRITE - must do this before flush_master_info reinit_io_cache(&mi->file, WRITE_CACHE, 0L, 0, 1); if ((error=test(flush_master_info(mi, TRUE, TRUE)))) sql_print_error("Failed to flush master info file"); - pthread_mutex_unlock(&mi->data_lock); + mysql_mutex_unlock(&mi->data_lock); DBUG_RETURN(error); errwithmsg: @@ -323,11 +413,11 @@ errwithmsg: err: if (fd >= 0) { - my_close(fd, MYF(0)); + mysql_file_close(fd, MYF(0)); end_io_cache(&mi->file); } mi->fd= -1; - pthread_mutex_unlock(&mi->data_lock); + mysql_mutex_unlock(&mi->data_lock); DBUG_RETURN(1); } @@ -363,21 +453,41 @@ int flush_master_info(Master_info* mi, */ if (flush_relay_log_cache) { - pthread_mutex_t *log_lock= mi->rli.relay_log.get_log_lock(); + mysql_mutex_t *log_lock= mi->rli.relay_log.get_log_lock(); IO_CACHE *log_file= mi->rli.relay_log.get_log_file(); if (need_lock_relay_log) - pthread_mutex_lock(log_lock); + mysql_mutex_lock(log_lock); - safe_mutex_assert_owner(log_lock); + mysql_mutex_assert_owner(log_lock); err= flush_io_cache(log_file); if (need_lock_relay_log) - pthread_mutex_unlock(log_lock); + mysql_mutex_unlock(log_lock); if (err) DBUG_RETURN(2); } + + /* + produce a line listing the total number and all the ignored server_id:s + */ + char* ignore_server_ids_buf; + { + ignore_server_ids_buf= + (char *) my_malloc((sizeof(::server_id) * 3 + 1) * + (1 + mi->ignore_server_ids.elements), MYF(MY_WME)); + if (!ignore_server_ids_buf) + DBUG_RETURN(1); + ulong cur_len= sprintf(ignore_server_ids_buf, "%u", + mi->ignore_server_ids.elements); + for (ulong i= 0; i < mi->ignore_server_ids.elements; i++) + { + ulong s_id; + get_dynamic(&mi->ignore_server_ids, (uchar*) &s_id, i); + cur_len+= sprintf(ignore_server_ids_buf + cur_len, " %lu", s_id); + } + } /* We flushed the relay log BEFORE the master.info file, because if we crash @@ -395,17 +505,27 @@ int flush_master_info(Master_info* mi, contents of file). But because of number of lines in the first line of file we don't care about this garbage. */ - + char heartbeat_buf[sizeof(mi->heartbeat_period) * 4]; // buffer to suffice always + sprintf(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", + "%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); - DBUG_RETURN(-flush_io_cache(file)); + mi->ssl_cipher, mi->ssl_key, mi->ssl_verify_server_cert, + heartbeat_buf, "", ignore_server_ids_buf); + my_free(ignore_server_ids_buf); + err= flush_io_cache(file); + if (sync_masterinfo_period && !err && + ++(mi->sync_counter) >= sync_masterinfo_period) + { + err= my_sync(mi->fd, MYF(MY_WME)); + mi->sync_counter= 0; + } + DBUG_RETURN(-err); } @@ -419,7 +539,7 @@ void end_master_info(Master_info* mi) if (mi->fd >= 0) { end_io_cache(&mi->file); - (void)my_close(mi->fd, MYF(MY_WME)); + mysql_file_close(mi->fd, MYF(MY_WME)); mi->fd = -1; } mi->inited = 0; |