summaryrefslogtreecommitdiff
path: root/sql/rpl_mi.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/rpl_mi.cc')
-rw-r--r--sql/rpl_mi.cc272
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;