Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add semisync functionality to mysqlbinlog
Summary:
WebScaleSQL Feature: Semisync Log Tailers

Share the semisync_slave plugin code to use for semisync functionality
in mysqlbinlog.  mysqlbinlog sends acknowledgement for events which
require a semi sync reply.

Since mysqlbinlog now speaks semi-sync portocol, we force the semi-sync
plugin to be built regardless of the option WITHOUT_SERVER.

Test Plan:
mysqlbinlog with and without --semisync-debug
mtr test to verify mysqlbinlog slave is acknowledging
for events it received.

This has been used in production at Facebook for > 9 months.

Reviewers: darnaut, inaam-rana, pivanof, jeremycole

Reviewed By: pivanof, jeremycole

Subscribers: steaphan, jtolmer, CalvinSun

Differential Revision: https://reviews.facebook.net/D24171
Differential Revision: https://reviews.facebook.net/D32763
  • Loading branch information
santoshbanda authored and steaphangreene committed Feb 4, 2015
1 parent e7311f7 commit 60930e2
Show file tree
Hide file tree
Showing 15 changed files with 371 additions and 70 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Expand Up @@ -430,6 +430,8 @@ IF(NOT WITHOUT_SERVER)
ELSE()
# We may still want Cluster client libraries, use -DWITH_NDBCLUSTER=1
ADD_SUBDIRECTORY(storage/ndb)
# mysqlbinlog needs semi-sync
ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/plugin/semisync/)
ENDIF()

ADD_SUBDIRECTORY(include)
Expand Down
3 changes: 2 additions & 1 deletion client/CMakeLists.txt
Expand Up @@ -24,6 +24,7 @@ INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/strings
${EDITLINE_INCLUDE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/plugin/semisync
)


Expand Down Expand Up @@ -62,7 +63,7 @@ MYSQL_ADD_EXECUTABLE(mysql_plugin mysql_plugin.c)
TARGET_LINK_LIBRARIES(mysql_plugin mysqlclient)

MYSQL_ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc)
TARGET_LINK_LIBRARIES(mysqlbinlog mysqlclient)
TARGET_LINK_LIBRARIES(mysqlbinlog mysqlclient semisync_slave_client)

MYSQL_ADD_EXECUTABLE(mysqladmin mysqladmin.cc)
TARGET_LINK_LIBRARIES(mysqladmin mysqlclient)
Expand Down
2 changes: 2 additions & 0 deletions client/client_priv.h
Expand Up @@ -100,6 +100,8 @@ enum options_client
OPT_SERVER_PUBLIC_KEY,
OPT_ENABLE_CLEARTEXT_PLUGIN,
OPT_CONNECTION_SERVER_ID,
OPT_USE_SEMISYNC,
OPT_SEMISYNC_DEBUG,
OPT_MAX_CLIENT_OPTION
};

Expand Down
100 changes: 75 additions & 25 deletions client/mysqlbinlog.cc
Expand Up @@ -54,6 +54,7 @@ static void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
#include "sql_string.h"
#include "my_decimal.h"
#include "rpl_constants.h"
#include "semisync_slave_client.h"

#include <algorithm>

Expand Down Expand Up @@ -193,6 +194,9 @@ static bool filter_based_on_gtids= false;

static bool in_transaction= false;
static bool seen_gtids= false;
static bool opt_use_semisync = false;
static uint opt_semisync_debug = 0;
ReplSemiSyncSlave repl_semisync;

static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *print_event_info,
const char* logname);
Expand Down Expand Up @@ -1615,6 +1619,17 @@ static struct my_option my_long_options[] =
"Identifiers were provided.",
&opt_exclude_gtids_str, &opt_exclude_gtids_str, 0,
GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"use-semisync", OPT_USE_SEMISYNC,
"mysqlbinlog functions as a semisync slave sending acknowledgement "
"for received events.",
&opt_use_semisync, &opt_use_semisync, 0,
GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"semisync-debug", OPT_SEMISYNC_DEBUG,
"A value of 2 prints debug information of semi-sync. "
"A value of 1 prints function traces of semi-sync. "
"A value of of 0 doesn't print any debug information.",
&opt_semisync_debug, &opt_semisync_debug, 0,
GET_UINT, REQUIRED_ARG, 0, 0, 2, 1, 0, 0},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};

Expand Down Expand Up @@ -1651,21 +1666,6 @@ static void error(const char *format,...)
va_end(args);
}


/**
This function is used in log_event.cc to report errors.
@param format Printf-style format string, followed by printf
varargs.
*/
static void sql_print_error(const char *format,...)
{
va_list args;
va_start(args, format);
error_or_warning(format, args, "ERROR");
va_end(args);
}

/**
Prints a message to stderr, prefixed with the text "WARNING: " and
suffixed with a newline.
Expand Down Expand Up @@ -1879,7 +1879,26 @@ static Exit_status safe_connect()
error("Failed on connect: %s", mysql_error(mysql));
return ERROR_STOP;
}
mysql->reconnect= 1;
if (opt_use_semisync)
{
// set semi sync slave status to true
rpl_semi_sync_slave_enabled = 1;
// Note that kTraceDetail and kTraceFunction are the only trace
// levels used by semi-sync slave.
if (opt_semisync_debug == 2)
rpl_semi_sync_slave_trace_level = Trace::kTraceDetail;
else if (opt_semisync_debug == 1)
rpl_semi_sync_slave_trace_level = Trace::kTraceFunction;
else
rpl_semi_sync_slave_trace_level = 0;
// Initialize semi sync slave functionality
repl_semisync.initObject();
// Check with master if it has semisync enabled and notify
// master this is a semisync enabled slave.
if (repl_semisync.slaveRequestDump(mysql))
return ERROR_STOP;
}
mysql->reconnect= 0;
return OK_CONTINUE;
}

Expand Down Expand Up @@ -2112,9 +2131,12 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
my_off_t old_off= start_position_mot;
char fname[FN_REFLEN + 1];
char log_file_name[FN_REFLEN + 1];
char *log_file = NULL;
Exit_status retval= OK_CONTINUE;
enum enum_server_command command= COM_END;

bool semi_sync_need_reply = false;

DBUG_ENTER("dump_remote_log_entries");

fname[0]= log_file_name[0]= 0;
Expand Down Expand Up @@ -2258,8 +2280,24 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
ROTATE_EVENT or FORMAT_DESCRIPTION_EVENT
*/

type= (Log_event_type) net->read_pos[1 + EVENT_TYPE_OFFSET];

const char *event_buf = (const char*)net->read_pos + 1;
ulong event_len = len - 1;
if (rpl_semi_sync_slave_status)
{
// semisync event has 2 extra flags at the beginning of the event
// header.
type = (Log_event_type) net->read_pos[1 + 2 + EVENT_TYPE_OFFSET];
if (repl_semisync.slaveReadSyncHeader((const char*)net->read_pos + 1,
event_len, &semi_sync_need_reply,
&event_buf, &event_len)) {
error("Malformed semi-sync packet");
DBUG_RETURN(ERROR_STOP);
}
}
else
{
type = (Log_event_type) net->read_pos[1 + EVENT_TYPE_OFFSET];
}
/*
Ignore HEARBEAT events. They can show up if mysqlbinlog is
running with:
Expand All @@ -2276,8 +2314,7 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,

if (!raw_mode || (type == ROTATE_EVENT) || (type == FORMAT_DESCRIPTION_EVENT))
{
if (!(ev= Log_event::read_log_event((const char*) net->read_pos + 1 ,
len - 1, &error_msg,
if (!(ev= Log_event::read_log_event(event_buf, event_len, &error_msg,
glob_description_event,
opt_verify_binlog_checksum)))
{
Expand All @@ -2288,7 +2325,7 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
If reading from a remote host, ensure the temp_buf for the
Log_event class is pointing to the incoming stream.
*/
ev->register_temp_buf((char *) net->read_pos + 1);
ev->register_temp_buf((char *)event_buf);
}
if (raw_mode || (type != LOAD_EVENT))
{
Expand Down Expand Up @@ -2322,6 +2359,7 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
{
strmov(log_file_name, rev->new_log_ident);
}
log_file = log_file_name + dirname_length(log_file_name);
}

if (rev->when.tv_sec == 0)
Expand All @@ -2346,7 +2384,7 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
next binlog file (fake rotate) picked by mysqlbinlog --to-last-log
*/
old_off= start_position_mot;
len= 1; // fake Rotate, so don't increment old_off
event_len = 0; // fake Rotate, so don't increment old_off
}
}
else if (type == FORMAT_DESCRIPTION_EVENT)
Expand All @@ -2360,7 +2398,7 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
*/
// fake event when not in raw mode, don't increment old_off
if ((old_off != BIN_LOG_HEADER_SIZE) && (!raw_mode))
len= 1;
event_len = 0;
if (raw_mode)
{
if (result_file && (result_file != stdout))
Expand Down Expand Up @@ -2403,7 +2441,8 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
{
DBUG_EXECUTE_IF("simulate_result_file_write_error",
DBUG_SET("+d,simulate_fwrite_error"););
if (my_fwrite(result_file, net->read_pos + 1 , len - 1, MYF(MY_NABP)))
if (my_fwrite(result_file, (const uchar*) event_buf, event_len,
MYF(MY_NABP)))
{
error("Could not write into log file '%s'", log_file_name);
retval= ERROR_STOP;
Expand Down Expand Up @@ -2447,7 +2486,12 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info,
Let's adjust offset for remote log as for local log to produce
similar text and to have --stop-position to work identically.
*/
old_off+= len-1;
old_off += event_len;
if (rpl_semi_sync_slave_status && semi_sync_need_reply)
{
DBUG_ASSERT(raw_mode);
repl_semisync.slaveReply(mysql, log_file, old_off);
}
}

DBUG_RETURN(OK_CONTINUE);
Expand Down Expand Up @@ -2869,6 +2913,12 @@ static int args_post_process(void)
stop_never_slave_server_id);
#endif

if (opt_use_semisync && !raw_mode)
{
error("--raw option must be used when using --use_semisync option");
DBUG_RETURN(ERROR_STOP);
}

DBUG_RETURN(OK_CONTINUE);
}

Expand Down
71 changes: 71 additions & 0 deletions mysql-test/r/semisync_mysqlbinlog.result
@@ -0,0 +1,71 @@
set @save_master_timeout=@@global.rpl_semi_sync_master_timeout;
set @save_master_wait_no_slave=@@global.rpl_semi_sync_master_wait_no_slave;
set @save_master_enabled=@@global.rpl_semi_sync_master_enabled;
[ enable semi-sync on master ]
set global rpl_semi_sync_master_timeout= 60000 /* 60s */;
set global rpl_semi_sync_master_enabled = 1;
show variables like 'rpl_semi_sync_master_enabled';
Variable_name Value
rpl_semi_sync_master_enabled ON
[ status of semi-sync on master should be ON even without any semi-sync slaves ]
show status like 'Rpl_semi_sync_master_clients';
Variable_name Value
Rpl_semi_sync_master_clients 0
show status like 'Rpl_semi_sync_master_status';
Variable_name Value
Rpl_semi_sync_master_status ON
show status like 'Rpl_semi_sync_master_yes_tx';
Variable_name Value
Rpl_semi_sync_master_yes_tx 0
[ initial master state after the semi-sync mysqlbinlog connected ]
show status like 'Rpl_semi_sync_master_clients';
Variable_name Value
Rpl_semi_sync_master_clients 1
show status like 'Rpl_semi_sync_master_status';
Variable_name Value
Rpl_semi_sync_master_status ON
show status like 'Rpl_semi_sync_master_no_tx';
Variable_name Value
Rpl_semi_sync_master_no_tx 0
show status like 'Rpl_semi_sync_master_yes_tx';
Variable_name Value
Rpl_semi_sync_master_yes_tx 0
create table t1(a int) engine = Innodb;
[ master state after CREATE TABLE statement ]
show status like 'Rpl_semi_sync_master_status';
Variable_name Value
Rpl_semi_sync_master_status ON
show status like 'Rpl_semi_sync_master_no_tx';
Variable_name Value
Rpl_semi_sync_master_no_tx 0
show status like 'Rpl_semi_sync_master_yes_tx';
Variable_name Value
Rpl_semi_sync_master_yes_tx 1
[ insert records to table ]
[ master status after inserts ]
show status like 'Rpl_semi_sync_master_status';
Variable_name Value
Rpl_semi_sync_master_status ON
show status like 'Rpl_semi_sync_master_no_tx';
Variable_name Value
Rpl_semi_sync_master_no_tx 0
show status like 'Rpl_semi_sync_master_yes_tx';
Variable_name Value
Rpl_semi_sync_master_yes_tx 11
set global rpl_semi_sync_master_wait_no_slave = OFF;
show variables like 'rpl_semi_sync_master_wait_no_slave';
Variable_name Value
rpl_semi_sync_master_wait_no_slave OFF
show variables like 'rpl_semi_sync_master_enabled';
Variable_name Value
rpl_semi_sync_master_enabled ON
show status like 'Rpl_semi_sync_master_status';
Variable_name Value
Rpl_semi_sync_master_status ON
#
# Clean up
#
set @@global.rpl_semi_sync_master_timeout=@save_master_timeout;
set @@global.rpl_semi_sync_master_wait_no_slave=@save_master_wait_no_slave;
set @@global.rpl_semi_sync_master_enabled=@save_master_enabled;
drop table t1;
2 changes: 2 additions & 0 deletions mysql-test/t/semisync_mysqlbinlog-master.opt
@@ -0,0 +1,2 @@
$SEMISYNC_PLUGIN_OPT
--force-restart

0 comments on commit 60930e2

Please sign in to comment.