From afcf7c641ea42eb3215cd3e4b514e4696db08df1 Mon Sep 17 00:00:00 2001 From: Mark Callaghan Date: Wed, 15 Feb 2012 10:10:55 -0800 Subject: [PATCH] Add rpl_event_buffer_size Summary: This pre-allocates a buffer per slave connection to avoid calling malloc & free for every event sent to that slave. Events small enough to fit in the buffer avoid the calls. This code was extracted from the Google patch for MySQL 5.0.37 Test Plan: mtr Reviewers: chip Reviewed By: chip CC: db-eng@lists, amenghra Differential Revision: https://phabricator.fb.com/D409665 Revert Plan: Database Impact: Memcache Impact: Other Notes: EImportant: - begin *PUBLIC* platform impact section - Bugzilla: # - end platform impact - --- .../suite/rpl/r/rpl_event_buffer_size.result | 30 +++++++++++++ mysql-test/suite/rpl/t/rpl_event_buffer_size.test | 42 ++++++++++++++++++ sql/mysql_priv.h | 2 + sql/mysqld.cc | 15 +++++++ sql/set_var.cc | 3 + sql/sql_repl.cc | 45 +++++++++++++++++++- 6 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_event_buffer_size.result create mode 100644 mysql-test/suite/rpl/t/rpl_event_buffer_size.test diff --git a/mysql-test/suite/rpl/r/rpl_event_buffer_size.result b/mysql-test/suite/rpl/r/rpl_event_buffer_size.result new file mode 100644 index 0000000..9acd77c --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_event_buffer_size.result @@ -0,0 +1,30 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +drop table if exists t1; +SET @@global.rpl_event_buffer_size=16384; +SELECT @@global.rpl_event_buffer_size; +@@global.rpl_event_buffer_size +16384 +include/stop_slave.inc +include/start_slave.inc +create table `t1` (`f1` LONGTEXT) ENGINE=MyISAM; +INSERT INTO t1(f1) VALUES(REPEAT('a', 10000)); +INSERT INTO t1(f1) VALUES(REPEAT('b', 20000)); +INSERT INTO t1(f1) VALUES(REPEAT('b', 100000)); +select length(f1), md5(f1) from t1 order by length(f1); +length(f1) md5(f1) +10000 0d0c9c4db6953fee9e03f528cafd7d3e +20000 2d0580be1e7272df1c79b31ebe4d259d +100000 09bfb3d92f4ec0691eec3644563f3ef4 +select length(f1), md5(f1) from t1 order by length(f1); +length(f1) md5(f1) +10000 0d0c9c4db6953fee9e03f528cafd7d3e +20000 2d0580be1e7272df1c79b31ebe4d259d +100000 09bfb3d92f4ec0691eec3644563f3ef4 +==== clean up ==== +DROP TABLE t1; +SET @@global.rpl_event_buffer_size= 1048576; diff --git a/mysql-test/suite/rpl/t/rpl_event_buffer_size.test b/mysql-test/suite/rpl/t/rpl_event_buffer_size.test new file mode 100644 index 0000000..2fe9312 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_event_buffer_size.test @@ -0,0 +1,42 @@ +# ==== Purpose ==== +# +# Check rpl_event_buffer_size +# + +source include/master-slave.inc; + +disable_warnings; +drop table if exists t1; +enable_warnings; + +connection master; + +let $old_rpl_event_buffer_size= `SELECT @@global.rpl_event_buffer_size`; +SET @@global.rpl_event_buffer_size=16384; +SELECT @@global.rpl_event_buffer_size; + +# Restart slave for setting to take effect +connection slave; +source include/stop_slave.inc; +source include/start_slave.inc; + +connection master; + +create table `t1` (`f1` LONGTEXT) ENGINE=MyISAM; + +INSERT INTO t1(f1) VALUES(REPEAT('a', 10000)); +INSERT INTO t1(f1) VALUES(REPEAT('b', 20000)); +INSERT INTO t1(f1) VALUES(REPEAT('b', 100000)); + +select length(f1), md5(f1) from t1 order by length(f1); + +sync_slave_with_master; +select length(f1), md5(f1) from t1 order by length(f1); + +--echo ==== clean up ==== +connection master; +DROP TABLE t1; +eval SET @@global.rpl_event_buffer_size= $old_rpl_event_buffer_size; + +sync_slave_with_master; +connection master; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 7d40e76..96f8a78 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2169,6 +2169,8 @@ extern my_bool admission_control_wait_reentry; extern my_bool admission_control_disabled; extern my_bool transaction_control_disabled; +extern ulong rpl_event_buffer_size; + /* Excuse me for doing this. This is a global variable exported by the InnoDB plugin. It is the maximum number of transaction slots that diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c9bfbc4..8a20e75 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -698,6 +698,9 @@ my_bool force_binlog_order= TRUE; ulong group_commit_timeout_usecs= 1000; ulong group_commit_min_size= 8; +/* Size for preallocated replication event buffer */ +ulong rpl_event_buffer_size; + /** Limit of the total number of prepared statements in the server. Is necessary to protect the server against out-of-memory attacks. @@ -6221,6 +6224,7 @@ enum options_mysqld OPT_LOG_SLOW_EXTRA, OPT_NET_COMPRESSION_LEVEL, OPT_RPL_TRANSACTION_ENABLED, + OPT_RPL_EVENT_BUFFER_SIZE, OPT_FORCE_BINLOG_ORDER, OPT_ALLOW_HINT_TO_MISSING_INDEX, OPT_ADMISSION_CONTROL, @@ -7898,6 +7902,15 @@ thread is in the relay logs.", &rpl_transaction_enabled, &rpl_transaction_enabled, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0}, #endif /* HAVE_INNODB_BINLOG */ + {"rpl_event_buffer_size", OPT_RPL_EVENT_BUFFER_SIZE, + "The size of the preallocated event buffer for slave connections that " + " avoids calls to malloc & free for events smaller than this.", + &rpl_event_buffer_size, &rpl_event_buffer_size, + 0, GET_ULONG, REQUIRED_ARG, + 1024*1024, /* the default size */ + 16*1024, /* the minimum size */ + 128*1024*1024, /* the maximum size */ + 0, 1, 0}, {"process_can_disable_bin_log", OPT_PROCESS_CAN_DISABLE_BIN_LOG, "The PROCESS privilege is sufficient to set sql_log_bin=0", &process_can_disable_bin_log, &process_can_disable_bin_log, @@ -8801,6 +8814,8 @@ static int mysql_init_variables(void) group_commit_min_size= 8; group_commit_timeout_usecs= 1000; + rpl_event_buffer_size= 1024 * 1024; + /* Character sets */ system_charset_info= &my_charset_utf8_general_ci; files_charset_info= &my_charset_utf8_general_ci; diff --git a/sql/set_var.cc b/sql/set_var.cc index a6b3385..6c6d732 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -628,6 +628,9 @@ static sys_var_thd_ulong sys_sort_buffer(&vars, "sort_buffer_size", static sys_var_bool_ptr sys_rpl_transaction_enabled(&vars, "rpl_transaction_enabled", &rpl_transaction_enabled); +static sys_var_long_ptr sys_rpl_event_buffer_size(&vars, "rpl_event_buffer_size", + &rpl_event_buffer_size); + static sys_var_long_ptr sys_log_query_sample_rate(&vars, "log_query_sample_rate", &opt_log_query_sample_rate); diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 9cd2094..a5defbe 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -486,6 +486,18 @@ send_event_to_slave(THD *thd, NET *net, String* const packet) return NULL; /* Success */ } +static void repl_cleanup(String *packet, char *packet_buffer) +{ + if (packet_buffer != NULL) + { + /* Make sure it does not reference packet_buffer */ + packet->free(); + + /* Free the fixed packet buffer. */ + my_free(packet_buffer, MYF(0)); + } +} + /* TODO: Clean up loop to only have one call to send_file() */ @@ -498,7 +510,13 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, char search_file_name[FN_REFLEN], *name; IO_CACHE log; File file = -1; - String* const packet = &thd->packet; + /* + Use a local string here to avoid disturbing the contents of thd->packet. + Note that calling thd->packet->free() here will make code elsewhere + crash. + */ + String packet_str; + String* packet= &packet_str; int error; const char *errmsg = "Unknown error", *tmp_msg; NET* net = &thd->net; @@ -524,6 +542,14 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, /* The number of times to skip calls to processlist_slave_offset */ int skip_state_update; + /* Preallocate fixed buffer for event packets. If an event is more + than the size, String class will re-allocate memory and we will + reset the packet memory for the next packet creation command. + This reduces calls to malloc and free. + */ + const ulong packet_buffer_size = rpl_event_buffer_size; + char *packet_buffer = NULL; + DBUG_ENTER("mysql_binlog_send"); DBUG_PRINT("enter",("log_ident: '%s' pos: %ld", log_ident, (long) pos)); @@ -551,6 +577,13 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, goto err; } + packet_buffer = (char*) my_malloc(packet_buffer_size, MYF(MY_WME)); + if (packet_buffer == NULL) { + errmsg = "Master failed pre-allocate event fixed buffer"; + my_errno = ER_OUTOFMEMORY; + goto err; + } + name=search_file_name; if (log_ident[0]) mysql_bin_log.make_log_name(search_file_name, log_ident); @@ -768,7 +801,13 @@ impossible position"; goto err; } - packet->set("\0", 1, &my_charset_bin); + /* + packet_buffer is only used in this case as it is the common case and + I prefer to reduce the size of the diff. + */ + packet->set(packet_buffer, (uint32) packet_buffer_size, &my_charset_bin); + packet->length(0); + packet->append("\0", 1); } /* @@ -984,6 +1023,7 @@ end: thd->variables.max_allowed_packet= old_max_allowed_packet; /* Undo any calls done by processlist_slave_offset */ thd->set_query(orig_query, orig_query_length); + repl_cleanup(packet, packet_buffer); DBUG_VOID_RETURN; err: @@ -1010,6 +1050,7 @@ err: my_message(my_errno, errmsg, MYF(0)); /* Undo any calls done by processlist_slave_offset */ thd->set_query(orig_query, orig_query_length); + repl_cleanup(packet, packet_buffer); DBUG_VOID_RETURN; } -- 1.7.8.4