summaryrefslogtreecommitdiffstats
path: root/sql/semisync_slave.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/semisync_slave.cc')
-rw-r--r--sql/semisync_slave.cc275
1 files changed, 275 insertions, 0 deletions
diff --git a/sql/semisync_slave.cc b/sql/semisync_slave.cc
new file mode 100644
index 00000000..788aab78
--- /dev/null
+++ b/sql/semisync_slave.cc
@@ -0,0 +1,275 @@
+/* Copyright (c) 2008 MySQL AB, 2009 Sun Microsystems, Inc.
+ Use is subject to license terms.
+
+ 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
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+#include <my_global.h>
+#include "semisync_slave.h"
+
+Repl_semi_sync_slave repl_semisync_slave;
+
+my_bool rpl_semi_sync_slave_enabled= 0;
+
+char rpl_semi_sync_slave_delay_master;
+my_bool rpl_semi_sync_slave_status= 0;
+ulong rpl_semi_sync_slave_trace_level;
+
+/*
+ indicate whether or not the slave should send a reply to the master.
+
+ This is set to true in repl_semi_slave_read_event if the current
+ event read is the last event of a transaction. And the value is
+ checked in repl_semi_slave_queue_event.
+*/
+bool semi_sync_need_reply= false;
+unsigned int rpl_semi_sync_slave_kill_conn_timeout;
+unsigned long long rpl_semi_sync_slave_send_ack = 0;
+
+int Repl_semi_sync_slave::init_object()
+{
+ int result= 0;
+
+ m_init_done = true;
+
+ /* References to the parameter works after set_options(). */
+ set_slave_enabled(rpl_semi_sync_slave_enabled);
+ set_trace_level(rpl_semi_sync_slave_trace_level);
+ set_delay_master(rpl_semi_sync_slave_delay_master);
+ set_kill_conn_timeout(rpl_semi_sync_slave_kill_conn_timeout);
+
+ return result;
+}
+
+int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header,
+ unsigned long total_len,
+ int *semi_flags,
+ const uchar **payload,
+ unsigned long *payload_len)
+{
+ int read_res = 0;
+ DBUG_ENTER("Repl_semi_sync_slave::slave_read_sync_header");
+
+ if (rpl_semi_sync_slave_status)
+ {
+ if (!DBUG_IF("semislave_corrupt_log")
+ && header[0] == k_packet_magic_num)
+ {
+ semi_sync_need_reply = (header[1] & k_packet_flag_sync);
+ *payload_len = total_len - 2;
+ *payload = header + 2;
+
+ DBUG_PRINT("semisync", ("%s: reply - %d",
+ "Repl_semi_sync_slave::slave_read_sync_header",
+ semi_sync_need_reply));
+
+ if (semi_sync_need_reply)
+ *semi_flags |= SEMI_SYNC_NEED_ACK;
+ if (is_delay_master())
+ *semi_flags |= SEMI_SYNC_SLAVE_DELAY_SYNC;
+ }
+ else
+ {
+ sql_print_error("Missing magic number for semi-sync packet, packet "
+ "len: %lu", total_len);
+ read_res = -1;
+ }
+ } else {
+ *payload= header;
+ *payload_len= total_len;
+ }
+
+ DBUG_RETURN(read_res);
+}
+
+int Repl_semi_sync_slave::slave_start(Master_info *mi)
+{
+ bool semi_sync= get_slave_enabled();
+
+ sql_print_information("Slave I/O thread: Start %s replication to\
+ master '%s@%s:%d' in log '%s' at position %lu",
+ semi_sync ? "semi-sync" : "asynchronous",
+ const_cast<char *>(mi->user), mi->host, mi->port,
+ const_cast<char *>(mi->master_log_name),
+ (unsigned long)(mi->master_log_pos));
+
+ if (semi_sync && !rpl_semi_sync_slave_status)
+ rpl_semi_sync_slave_status= 1;
+
+ /*clear the counter*/
+ rpl_semi_sync_slave_send_ack= 0;
+ return 0;
+}
+
+int Repl_semi_sync_slave::slave_stop(Master_info *mi)
+{
+ if (get_slave_enabled())
+ kill_connection(mi->mysql);
+
+ if (rpl_semi_sync_slave_status)
+ rpl_semi_sync_slave_status= 0;
+
+ return 0;
+}
+
+int Repl_semi_sync_slave::reset_slave(Master_info *mi)
+{
+ return 0;
+}
+
+void Repl_semi_sync_slave::kill_connection(MYSQL *mysql)
+{
+ if (!mysql)
+ return;
+
+ char kill_buffer[30];
+ MYSQL *kill_mysql = NULL;
+ size_t kill_buffer_length;
+
+ kill_mysql = mysql_init(kill_mysql);
+ mysql_options(kill_mysql, MYSQL_OPT_CONNECT_TIMEOUT, &m_kill_conn_timeout);
+ mysql_options(kill_mysql, MYSQL_OPT_READ_TIMEOUT, &m_kill_conn_timeout);
+ mysql_options(kill_mysql, MYSQL_OPT_WRITE_TIMEOUT, &m_kill_conn_timeout);
+
+ bool ret= (!mysql_real_connect(kill_mysql, mysql->host,
+ mysql->user, mysql->passwd,0, mysql->port, mysql->unix_socket, 0));
+ if (DBUG_IF("semisync_slave_failed_kill") || ret)
+ {
+ sql_print_information("cannot connect to master to kill slave io_thread's "
+ "connection");
+ goto failed_graceful_kill;
+ }
+
+ DBUG_EXECUTE_IF("slave_delay_killing_semisync_connection", my_sleep(400000););
+
+ kill_buffer_length= my_snprintf(kill_buffer, 30, "KILL %lu",
+ mysql->thread_id);
+ if (mysql_real_query(kill_mysql, kill_buffer, (ulong)kill_buffer_length))
+ {
+ sql_print_information(
+ "Failed to gracefully kill our active semi-sync connection with "
+ "primary. Silently closing the connection.");
+ goto failed_graceful_kill;
+ }
+
+end:
+ mysql_close(kill_mysql);
+ return;
+
+failed_graceful_kill:
+ /*
+ If we fail to issue `KILL` on the primary to kill the active semi-sync
+ connection; we need to locally clean up our side of the connection. This
+ is because mysql_close will send COM_QUIT on the active semi-sync
+ connection, causing the primary to error.
+ */
+ net_clear(&(mysql->net), 0);
+ end_server(mysql);
+ goto end;
+}
+
+int Repl_semi_sync_slave::request_transmit(Master_info *mi)
+{
+ MYSQL *mysql= mi->mysql;
+ MYSQL_RES *res= 0;
+ MYSQL_ROW row;
+ const char *query;
+
+ if (!get_slave_enabled())
+ return 0;
+
+ query= "SHOW VARIABLES LIKE 'rpl_semi_sync_master_enabled'";
+ if (mysql_real_query(mysql, query, (ulong)strlen(query)) ||
+ !(res= mysql_store_result(mysql)))
+ {
+ sql_print_error("Execution failed on master: %s, error :%s", query, mysql_error(mysql));
+ return 1;
+ }
+
+ row= mysql_fetch_row(res);
+ if (DBUG_IF("master_not_support_semisync") || !row)
+ {
+ /* Master does not support semi-sync */
+ sql_print_warning("Master server does not support semi-sync, "
+ "fallback to asynchronous replication");
+ rpl_semi_sync_slave_status= 0;
+ mysql_free_result(res);
+ return 0;
+ }
+ mysql_free_result(res);
+
+ /*
+ Tell master dump thread that we want to do semi-sync
+ replication
+ */
+ query= "SET @rpl_semi_sync_slave= 1";
+ if (mysql_real_query(mysql, query, (ulong)strlen(query)))
+ {
+ sql_print_error("Set 'rpl_semi_sync_slave=1' on master failed");
+ return 1;
+ }
+ mysql_free_result(mysql_store_result(mysql));
+ rpl_semi_sync_slave_status= 1;
+
+ return 0;
+}
+
+int Repl_semi_sync_slave::slave_reply(Master_info *mi)
+{
+ MYSQL* mysql= mi->mysql;
+ const char *binlog_filename= const_cast<char *>(mi->master_log_name);
+ my_off_t binlog_filepos= mi->master_log_pos;
+
+ NET *net= &mysql->net;
+ uchar reply_buffer[REPLY_MAGIC_NUM_LEN
+ + REPLY_BINLOG_POS_LEN
+ + REPLY_BINLOG_NAME_LEN];
+ int reply_res = 0;
+ size_t name_len = strlen(binlog_filename);
+
+ DBUG_ENTER("Repl_semi_sync_slave::slave_reply");
+
+ if (rpl_semi_sync_slave_status && semi_sync_need_reply)
+ {
+ /* Prepare the buffer of the reply. */
+ reply_buffer[REPLY_MAGIC_NUM_OFFSET] = k_packet_magic_num;
+ int8store(reply_buffer + REPLY_BINLOG_POS_OFFSET, binlog_filepos);
+ memcpy(reply_buffer + REPLY_BINLOG_NAME_OFFSET,
+ binlog_filename,
+ name_len + 1 /* including trailing '\0' */);
+
+ DBUG_PRINT("semisync", ("%s: reply (%s, %lu)",
+ "Repl_semi_sync_slave::slave_reply",
+ binlog_filename, (ulong)binlog_filepos));
+
+ net_clear(net, 0);
+ /* Send the reply. */
+ reply_res = my_net_write(net, reply_buffer,
+ name_len + REPLY_BINLOG_NAME_OFFSET);
+ if (!reply_res)
+ {
+ reply_res = (DBUG_IF("semislave_failed_net_flush") || net_flush(net));
+ if (reply_res)
+ sql_print_error("Semi-sync slave net_flush() reply failed");
+ rpl_semi_sync_slave_send_ack++;
+ }
+ else
+ {
+ sql_print_error("Semi-sync slave send reply failed: %s (%d)",
+ net->last_error, net->last_errno);
+ }
+ }
+
+ DBUG_RETURN(reply_res);
+}