diff --git a/mysql-test/suite/binlog_nogtid/r/binlog_persist_only_variables.result b/mysql-test/suite/binlog_nogtid/r/binlog_persist_only_variables.result index 1a63b9038639..979d35f264a5 100644 --- a/mysql-test/suite/binlog_nogtid/r/binlog_persist_only_variables.result +++ b/mysql-test/suite/binlog_nogtid/r/binlog_persist_only_variables.result @@ -177,6 +177,7 @@ SET PERSIST_ONLY rpl_receive_buffer_size = @@GLOBAL.rpl_receive_buffer_size; SET PERSIST_ONLY rpl_semi_sync_replica_enabled = @@GLOBAL.rpl_semi_sync_replica_enabled; SET PERSIST_ONLY rpl_semi_sync_replica_trace_level = @@GLOBAL.rpl_semi_sync_replica_trace_level; SET PERSIST_ONLY rpl_semi_sync_source_enabled = @@GLOBAL.rpl_semi_sync_source_enabled; +SET PERSIST_ONLY rpl_semi_sync_source_histogram_trx_wait_step_size = @@GLOBAL.rpl_semi_sync_source_histogram_trx_wait_step_size; SET PERSIST_ONLY rpl_semi_sync_source_timeout = @@GLOBAL.rpl_semi_sync_source_timeout; SET PERSIST_ONLY rpl_semi_sync_source_trace_level = @@GLOBAL.rpl_semi_sync_source_trace_level; SET PERSIST_ONLY rpl_semi_sync_source_wait_for_replica_count = @@GLOBAL.rpl_semi_sync_source_wait_for_replica_count; @@ -394,6 +395,7 @@ RESET PERSIST rpl_receive_buffer_size; RESET PERSIST rpl_semi_sync_replica_enabled; RESET PERSIST rpl_semi_sync_replica_trace_level; RESET PERSIST rpl_semi_sync_source_enabled; +RESET PERSIST rpl_semi_sync_source_histogram_trx_wait_step_size; RESET PERSIST rpl_semi_sync_source_timeout; RESET PERSIST rpl_semi_sync_source_trace_level; RESET PERSIST rpl_semi_sync_source_wait_for_replica_count; diff --git a/mysql-test/suite/binlog_nogtid/r/binlog_persist_variables.result b/mysql-test/suite/binlog_nogtid/r/binlog_persist_variables.result index 00105ba4946b..b7f2bfe83df6 100644 --- a/mysql-test/suite/binlog_nogtid/r/binlog_persist_variables.result +++ b/mysql-test/suite/binlog_nogtid/r/binlog_persist_variables.result @@ -160,6 +160,7 @@ SET PERSIST rpl_receive_buffer_size = @@GLOBAL.rpl_receive_buffer_size; SET PERSIST rpl_semi_sync_replica_enabled = @@GLOBAL.rpl_semi_sync_replica_enabled; SET PERSIST rpl_semi_sync_replica_trace_level = @@GLOBAL.rpl_semi_sync_replica_trace_level; SET PERSIST rpl_semi_sync_source_enabled = @@GLOBAL.rpl_semi_sync_source_enabled; +SET PERSIST rpl_semi_sync_source_histogram_trx_wait_step_size = @@GLOBAL.rpl_semi_sync_source_histogram_trx_wait_step_size; SET PERSIST rpl_semi_sync_source_timeout = @@GLOBAL.rpl_semi_sync_source_timeout; SET PERSIST rpl_semi_sync_source_trace_level = @@GLOBAL.rpl_semi_sync_source_trace_level; SET PERSIST rpl_semi_sync_source_wait_for_replica_count = @@GLOBAL.rpl_semi_sync_source_wait_for_replica_count; @@ -411,6 +412,7 @@ RESET PERSIST IF EXISTS rpl_receive_buffer_size; RESET PERSIST IF EXISTS rpl_semi_sync_replica_enabled; RESET PERSIST IF EXISTS rpl_semi_sync_replica_trace_level; RESET PERSIST IF EXISTS rpl_semi_sync_source_enabled; +RESET PERSIST IF EXISTS rpl_semi_sync_source_histogram_trx_wait_step_size; RESET PERSIST IF EXISTS rpl_semi_sync_source_timeout; RESET PERSIST IF EXISTS rpl_semi_sync_source_trace_level; RESET PERSIST IF EXISTS rpl_semi_sync_source_wait_for_replica_count; diff --git a/mysql-test/suite/rpl_nogtid/r/rpl_semi_sync.result b/mysql-test/suite/rpl_nogtid/r/rpl_semi_sync.result index 784ab13c92b0..fa743ccc7bea 100644 --- a/mysql-test/suite/rpl_nogtid/r/rpl_semi_sync.result +++ b/mysql-test/suite/rpl_nogtid/r/rpl_semi_sync.result @@ -36,6 +36,18 @@ Rpl_semi_sync_source_status ON show status like 'Rpl_semi_sync_source_yes_tx'; Variable_name Value Rpl_semi_sync_source_yes_tx 0 +show status like 'Rpl_semi_sync_master_trx_wait_histogram%'; +Variable_name Value +Rpl_semi_sync_master_trx_wait_histogram_0-500us 0 +Rpl_semi_sync_master_trx_wait_histogram_500-1500us 0 +Rpl_semi_sync_master_trx_wait_histogram_1500-3500us 0 +Rpl_semi_sync_master_trx_wait_histogram_3500-7500us 0 +Rpl_semi_sync_master_trx_wait_histogram_7500-15500us 0 +Rpl_semi_sync_master_trx_wait_histogram_15500-31500us 0 +Rpl_semi_sync_master_trx_wait_histogram_31500-63500us 0 +Rpl_semi_sync_master_trx_wait_histogram_63500-127500us 0 +Rpl_semi_sync_master_trx_wait_histogram_127500-255500us 0 +Rpl_semi_sync_master_trx_wait_histogram_255500-MAXus 0 # # BUG#45672 Semisync repl: ActiveTranx:insert_tranx_node: transaction node allocation failed # BUG#45673 Semisync reports correct operation even if no slave is connected diff --git a/mysql-test/suite/rpl_nogtid/r/rpl_semi_sync_optimize_for_static_plugin_config.result b/mysql-test/suite/rpl_nogtid/r/rpl_semi_sync_optimize_for_static_plugin_config.result index 15d793eee384..914f256241db 100644 --- a/mysql-test/suite/rpl_nogtid/r/rpl_semi_sync_optimize_for_static_plugin_config.result +++ b/mysql-test/suite/rpl_nogtid/r/rpl_semi_sync_optimize_for_static_plugin_config.result @@ -36,6 +36,18 @@ Rpl_semi_sync_source_status ON show status like 'Rpl_semi_sync_source_yes_tx'; Variable_name Value Rpl_semi_sync_source_yes_tx 0 +show status like 'Rpl_semi_sync_master_trx_wait_histogram%'; +Variable_name Value +Rpl_semi_sync_master_trx_wait_histogram_0-500us 0 +Rpl_semi_sync_master_trx_wait_histogram_500-1500us 0 +Rpl_semi_sync_master_trx_wait_histogram_1500-3500us 0 +Rpl_semi_sync_master_trx_wait_histogram_3500-7500us 0 +Rpl_semi_sync_master_trx_wait_histogram_7500-15500us 0 +Rpl_semi_sync_master_trx_wait_histogram_15500-31500us 0 +Rpl_semi_sync_master_trx_wait_histogram_31500-63500us 0 +Rpl_semi_sync_master_trx_wait_histogram_63500-127500us 0 +Rpl_semi_sync_master_trx_wait_histogram_127500-255500us 0 +Rpl_semi_sync_master_trx_wait_histogram_255500-MAXus 0 # # BUG#45672 Semisync repl: ActiveTranx:insert_tranx_node: transaction node allocation failed # BUG#45673 Semisync reports correct operation even if no slave is connected diff --git a/mysql-test/suite/rpl_nogtid/r/rpl_semi_sync_sender_observe_commit_only.result b/mysql-test/suite/rpl_nogtid/r/rpl_semi_sync_sender_observe_commit_only.result index 784ab13c92b0..fa743ccc7bea 100644 --- a/mysql-test/suite/rpl_nogtid/r/rpl_semi_sync_sender_observe_commit_only.result +++ b/mysql-test/suite/rpl_nogtid/r/rpl_semi_sync_sender_observe_commit_only.result @@ -36,6 +36,18 @@ Rpl_semi_sync_source_status ON show status like 'Rpl_semi_sync_source_yes_tx'; Variable_name Value Rpl_semi_sync_source_yes_tx 0 +show status like 'Rpl_semi_sync_master_trx_wait_histogram%'; +Variable_name Value +Rpl_semi_sync_master_trx_wait_histogram_0-500us 0 +Rpl_semi_sync_master_trx_wait_histogram_500-1500us 0 +Rpl_semi_sync_master_trx_wait_histogram_1500-3500us 0 +Rpl_semi_sync_master_trx_wait_histogram_3500-7500us 0 +Rpl_semi_sync_master_trx_wait_histogram_7500-15500us 0 +Rpl_semi_sync_master_trx_wait_histogram_15500-31500us 0 +Rpl_semi_sync_master_trx_wait_histogram_31500-63500us 0 +Rpl_semi_sync_master_trx_wait_histogram_63500-127500us 0 +Rpl_semi_sync_master_trx_wait_histogram_127500-255500us 0 +Rpl_semi_sync_master_trx_wait_histogram_255500-MAXus 0 # # BUG#45672 Semisync repl: ActiveTranx:insert_tranx_node: transaction node allocation failed # BUG#45673 Semisync reports correct operation even if no slave is connected diff --git a/mysql-test/suite/rpl_nogtid/t/rpl_semi_sync.test b/mysql-test/suite/rpl_nogtid/t/rpl_semi_sync.test index 3381ff79d723..cc22f6068418 100644 --- a/mysql-test/suite/rpl_nogtid/t/rpl_semi_sync.test +++ b/mysql-test/suite/rpl_nogtid/t/rpl_semi_sync.test @@ -90,6 +90,7 @@ echo [ status of semi-sync on master should be ON even without any semi-sync sla show status like 'Rpl_semi_sync_source_clients'; show status like 'Rpl_semi_sync_source_status'; show status like 'Rpl_semi_sync_source_yes_tx'; +show status like 'Rpl_semi_sync_master_trx_wait_histogram%'; --echo # --echo # BUG#45672 Semisync repl: ActiveTranx:insert_tranx_node: transaction node allocation failed diff --git a/mysql-test/suite/sys_vars/r/rpl_semi_sync_source_histogram_trx_wait_step_size_basic.result b/mysql-test/suite/sys_vars/r/rpl_semi_sync_source_histogram_trx_wait_step_size_basic.result new file mode 100644 index 000000000000..8e5cc2987c74 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/rpl_semi_sync_source_histogram_trx_wait_step_size_basic.result @@ -0,0 +1,75 @@ +SELECT COUNT(@@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size); +COUNT(@@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size) +1 +1 Expected +SET @start_global_value = @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size; +SELECT @start_global_value; +@start_global_value +500us +16ms Expected +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='16us'; +select @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size; +@@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size +16us +16us Expected +select * from performance_schema.global_variables where variable_name='rpl_semi_sync_master_histogram_trx_wait_step_size'; +VARIABLE_NAME VARIABLE_VALUE +rpl_semi_sync_master_histogram_trx_wait_step_size 16us +SELECT @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size = VARIABLE_VALUE +FROM performance_schema.global_variables +WHERE VARIABLE_NAME='rpl_semi_sync_master_histogram_trx_wait_step_size'; +@@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size = VARIABLE_VALUE +1 +1 Expected +SELECT COUNT(@@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size); +COUNT(@@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size) +1 +1 Expected +SELECT COUNT(VARIABLE_VALUE) +FROM performance_schema.global_variables +WHERE VARIABLE_NAME='rpl_semi_sync_master_histogram_trx_wait_step_size'; +COUNT(VARIABLE_VALUE) +1 +1 Expected +SELECT COUNT(@@local.rpl_semi_sync_master_histogram_trx_wait_step_size); +ERROR HY000: Variable 'rpl_semi_sync_master_histogram_trx_wait_step_size' is a GLOBAL variable +Expected error 'Variable is a GLOBAL variable' +SELECT COUNT(@@SESSION.rpl_semi_sync_master_histogram_trx_wait_step_size); +ERROR HY000: Variable 'rpl_semi_sync_master_histogram_trx_wait_step_size' is a GLOBAL variable +Expected error 'Variable is a GLOBAL variable' +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='32'; +ERROR 42000: Variable 'rpl_semi_sync_master_histogram_trx_wait_step_size' can't be set to the value of '32' +Expected error 'Variable cannot be set to this value'; +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='0'; +select @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size; +@@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size +0 +0 Expected +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='ms32'; +ERROR 42000: Variable 'rpl_semi_sync_master_histogram_trx_wait_step_size' can't be set to the value of 'ms32' +Expected error 'Variable cannot be set to this value'; +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='32ps'; +ERROR 42000: Variable 'rpl_semi_sync_master_histogram_trx_wait_step_size' can't be set to the value of '32ps' +Expected error 'Variable cannot be set to this value'; +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='3s2'; +ERROR 42000: Variable 'rpl_semi_sync_master_histogram_trx_wait_step_size' can't be set to the value of '3s2' +Expected error 'Variable cannot be set to this value'; +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='32@s'; +ERROR 42000: Variable 'rpl_semi_sync_master_histogram_trx_wait_step_size' can't be set to the value of '32@s' +Expected error 'Variable cannot be set to this value'; +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='32s.'; +ERROR 42000: Variable 'rpl_semi_sync_master_histogram_trx_wait_step_size' can't be set to the value of '32s.' +Expected error 'Variable cannot be set to this value'; +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='s'; +ERROR 42000: Variable 'rpl_semi_sync_master_histogram_trx_wait_step_size' can't be set to the value of 's' +Expected error 'Variable cannot be set to this value' +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='16.5us'; +select @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size; +@@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size +16.5us +16.5us Expected +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size = @start_global_value; +SELECT @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size; +@@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size +500us +16ms Expected diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_source_histogram_trx_wait_step_size_basic-master.opt b/mysql-test/suite/sys_vars/t/rpl_semi_sync_source_histogram_trx_wait_step_size_basic-master.opt new file mode 100644 index 000000000000..8c9e13fe2c4a --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_source_histogram_trx_wait_step_size_basic-master.opt @@ -0,0 +1 @@ +$SEMISYNC_MASTER_PLUGIN_OPT $SEMISYNC_MASTER_PLUGIN_LOAD diff --git a/mysql-test/suite/sys_vars/t/rpl_semi_sync_source_histogram_trx_wait_step_size_basic.test b/mysql-test/suite/sys_vars/t/rpl_semi_sync_source_histogram_trx_wait_step_size_basic.test new file mode 100644 index 000000000000..5838a3de3fd5 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/rpl_semi_sync_source_histogram_trx_wait_step_size_basic.test @@ -0,0 +1,73 @@ +SELECT COUNT(@@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size); +--echo 1 Expected + +SET @start_global_value = @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size; +SELECT @start_global_value; +--echo 16ms Expected + +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='16us'; +select @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size; +--echo 16us Expected + +select * from performance_schema.global_variables where variable_name='rpl_semi_sync_master_histogram_trx_wait_step_size'; + +SELECT @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size = VARIABLE_VALUE +FROM performance_schema.global_variables +WHERE VARIABLE_NAME='rpl_semi_sync_master_histogram_trx_wait_step_size'; +--echo 1 Expected + +SELECT COUNT(@@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size); +--echo 1 Expected + +SELECT COUNT(VARIABLE_VALUE) +FROM performance_schema.global_variables +WHERE VARIABLE_NAME='rpl_semi_sync_master_histogram_trx_wait_step_size'; +--echo 1 Expected + +--Error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT COUNT(@@local.rpl_semi_sync_master_histogram_trx_wait_step_size); +--echo Expected error 'Variable is a GLOBAL variable' + +--Error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT COUNT(@@SESSION.rpl_semi_sync_master_histogram_trx_wait_step_size); +--echo Expected error 'Variable is a GLOBAL variable' + +--Error ER_WRONG_VALUE_FOR_VAR +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='32'; +--echo Expected error 'Variable cannot be set to this value'; + +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='0'; +select @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size; +--echo 0 Expected + +--Error ER_WRONG_VALUE_FOR_VAR +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='ms32'; +--echo Expected error 'Variable cannot be set to this value'; + +--Error ER_WRONG_VALUE_FOR_VAR +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='32ps'; +--echo Expected error 'Variable cannot be set to this value'; + +--Error ER_WRONG_VALUE_FOR_VAR +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='3s2'; +--echo Expected error 'Variable cannot be set to this value'; + +--Error ER_WRONG_VALUE_FOR_VAR +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='32@s'; +--echo Expected error 'Variable cannot be set to this value'; + +--Error ER_WRONG_VALUE_FOR_VAR +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='32s.'; +--echo Expected error 'Variable cannot be set to this value'; + +--Error ER_WRONG_VALUE_FOR_VAR +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='s'; +--echo Expected error 'Variable cannot be set to this value' + +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size='16.5us'; +select @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size; +--echo 16.5us Expected + +SET @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size = @start_global_value; +SELECT @@GLOBAL.rpl_semi_sync_master_histogram_trx_wait_step_size; +--echo 16ms Expected diff --git a/plugin/semisync/semisync_source.cc b/plugin/semisync/semisync_source.cc index 45f34a489827..8f9fd64f12a9 100644 --- a/plugin/semisync/semisync_source.cc +++ b/plugin/semisync/semisync_source.cc @@ -66,6 +66,10 @@ unsigned long long rpl_semi_sync_source_net_wait_time = 0; unsigned long long rpl_semi_sync_source_trx_wait_time = 0; bool rpl_semi_sync_source_wait_no_replica = true; unsigned int rpl_semi_sync_source_wait_for_replica_count = 1; +char *histogram_trx_wait_step_size = 0; +latency_histogram histogram_trx_wait; +SHOW_VAR latency_histogram_trx_wait[NUMBER_OF_HISTOGRAM_BINS + 1]; +ulonglong histogram_trx_wait_values[NUMBER_OF_HISTOGRAM_BINS]; static int getWaitTime(const struct timespec &start_ts); @@ -439,6 +443,7 @@ int ReplSemiSyncMaster::initObject() { else result = disableMaster(); + latency_histogram_init(&histogram_trx_wait, histogram_trx_wait_step_size); return result; } @@ -509,6 +514,7 @@ int ReplSemiSyncMaster::disableMaster() { } ReplSemiSyncMaster::~ReplSemiSyncMaster() { + free_latency_histogram_sysvars(latency_histogram_trx_wait); if (init_done_) { mysql_mutex_destroy(&LOCK_binlog_); } @@ -847,6 +853,10 @@ int ReplSemiSyncMaster::commitTrx(const char *trx_wait_binlog_name, } else { rpl_semi_sync_source_trx_wait_num++; rpl_semi_sync_source_trx_wait_time += wait_time; + if (histogram_trx_wait_step_size) + latency_histogram_increment( + &histogram_trx_wait, + microseconds_to_my_timer((double)wait_time), 1); } } } @@ -1211,6 +1221,11 @@ void ReplSemiSyncMaster::setExportStats() { ((double)rpl_semi_sync_source_net_wait_num)) : 0); + for (size_t i_bins = 0; i_bins < NUMBER_OF_HISTOGRAM_BINS; ++i_bins) { + histogram_trx_wait_values[i_bins] = + latency_histogram_get_count(&histogram_trx_wait, i_bins); + } + unlock(); } @@ -1336,3 +1351,10 @@ static int getWaitTime(const struct timespec &start_ts) { return (int)(end_usecs - start_usecs); } + +void ReplSemiSyncMaster::update_histogram_trx_wait_step_size( + const char *step_size) { + lock(); + latency_histogram_init(&histogram_trx_wait, step_size); + unlock(); +} diff --git a/plugin/semisync/semisync_source.h b/plugin/semisync/semisync_source.h index 0413c4f8e932..7f42452d4b23 100644 --- a/plugin/semisync/semisync_source.h +++ b/plugin/semisync/semisync_source.h @@ -32,6 +32,7 @@ #include "my_io.h" #include "my_psi_config.h" #include "plugin/semisync/semisync.h" +#include "sql/mysqld.h" extern PSI_memory_key key_ss_memory_TranxNodeAllocator_block; @@ -833,6 +834,14 @@ class ReplSemiSyncMaster : public ReplSemiSyncBase { } unlock(); } + + /* Reinitializes the latency histogram when trx_wait_step_size + * is updated. + * + * Input: + * step_size - (IN) updated step_size + */ + void update_histogram_trx_wait_step_size(const char *step_size); }; /* System and status variables for the master component */ @@ -856,6 +865,12 @@ extern unsigned long long rpl_semi_sync_source_trx_wait_num; extern unsigned long long rpl_semi_sync_source_net_wait_time; extern unsigned long long rpl_semi_sync_source_trx_wait_time; +extern char *histogram_trx_wait_step_size; +extern latency_histogram histogram_trx_wait; +/* status variables for trx_wait_time histogram */ +extern SHOW_VAR latency_histogram_trx_wait[NUMBER_OF_HISTOGRAM_BINS + 1]; +extern ulonglong histogram_trx_wait_values[NUMBER_OF_HISTOGRAM_BINS]; + /* This indicates whether we should keep waiting if no semi-sync slave is available. diff --git a/plugin/semisync/semisync_source_plugin.cc b/plugin/semisync/semisync_source_plugin.cc index 1033a57dcff8..841e35a7b89e 100644 --- a/plugin/semisync/semisync_source_plugin.cc +++ b/plugin/semisync/semisync_source_plugin.cc @@ -277,6 +277,63 @@ static void update_whitelist(THD *, SYS_VAR *, void *var_ptr, ack_receiver->unlock(); } +/* + Checks whether a valid argument is given to + rpl_semi_sync_master_trx_wait_step_size sys_var. + + @return 0 valid step size + >0 invalid step size + + @param thd thread handler + @param var pointer to the system variable + @return save Output value is stored here. This is the immediate result + for sys_var update function + @param value input value +*/ +static int check_histogram_step_size(MYSQL_THD, SYS_VAR *, void *save, + struct st_mysql_value *value) { + int len = 0; + const char *step_size_local = value->val_str(value, nullptr, &len); + + size_t length = 0; + if (step_size_local) length = strlen(step_size_local); + + if (length == 0) { + *static_cast(save) = nullptr; + return 0; + } + + /* + Validating if the string (non empty) ends with ms/us/s and the + rest of it is a valid floating point number + */ + int ret = histogram_validate_step_size_string(step_size_local); + if (!ret) *static_cast(save) = step_size_local; + + return ret; +} + +/* + Reinitializes the latency histogram when the trx_wait_step_size is + updated. + + @param thd thread handler + @param var pointer to system variable + @result var_ptr output value of the system variable + @param save input string value. This is the immediate result from + sys_var check function. +*/ +static void update_histogram_trx_wait_step_size(MYSQL_THD, SYS_VAR *, + void *var_ptr, + const void *save) { + const char *step_size_local = *static_cast(save); + + if (step_size_local) + repl_semisync->update_histogram_trx_wait_step_size(step_size_local); + + *static_cast(var_ptr) = step_size_local; +} + static MYSQL_SYSVAR_BOOL( enabled, rpl_semi_sync_source_enabled, PLUGIN_VAR_OPCMDARG, "Enable semi-synchronous replication source (disabled by default). ", @@ -390,6 +447,13 @@ static MYSQL_SYSVAR_STR( "list will lead to discarding all ACKs.", nullptr, update_whitelist, "ANY"); +static MYSQL_SYSVAR_STR(histogram_trx_wait_step_size, + histogram_trx_wait_step_size, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC, + "Histogram step size for transaction wait time. ", + check_histogram_step_size, + update_histogram_trx_wait_step_size, "500us"); + static SYS_VAR *semi_sync_master_system_vars[] = { MYSQL_SYSVAR(enabled), MYSQL_SYSVAR(timeout), @@ -399,6 +463,7 @@ static SYS_VAR *semi_sync_master_system_vars[] = { MYSQL_SYSVAR(wait_point), MYSQL_SYSVAR(WAIT_FOR_REPLICA_COUNT_NAME), MYSQL_SYSVAR(whitelist), + MYSQL_SYSVAR(histogram_trx_wait_step_size), nullptr, }; static void fix_rpl_semi_sync_source_timeout(MYSQL_THD, SYS_VAR *, void *ptr, @@ -506,6 +571,24 @@ DEF_SHOW_FUNC(net_wait_num, SHOW_LONGLONG) DEF_SHOW_FUNC(avg_net_wait_time, SHOW_LONG) DEF_SHOW_FUNC(avg_trx_wait_time, SHOW_LONG) +static SHOW_VAR semisync_histogram_status_variables[] = { + {"Rpl_semi_sync_master_trx_wait_histogram", + (char *)&latency_histogram_trx_wait, SHOW_ARRAY, SHOW_SCOPE_GLOBAL}, + {NULL, NULL, SHOW_LONG, SHOW_SCOPE_GLOBAL}}; + +static int rpl_semi_sync_master_trx_wait_histogram(MYSQL_THD, SHOW_VAR *var, + char *) { + prepare_latency_histogram_vars(&histogram_trx_wait, + latency_histogram_trx_wait, + histogram_trx_wait_values); + + repl_semisync->setExportStats(); + var->type = SHOW_ARRAY; + var->value = (char *)&semisync_histogram_status_variables; + var->scope = SHOW_SCOPE_GLOBAL; + return 0; +} + /* plugin status variables */ static SHOW_VAR semi_sync_master_status_vars[] = { {STATUS_VAR_PREFIX "status", (char *)&SHOW_FNAME(status), SHOW_FUNC, @@ -538,6 +621,9 @@ static SHOW_VAR semi_sync_master_status_vars[] = { SHOW_FUNC, SHOW_SCOPE_GLOBAL}, {STATUS_VAR_PREFIX "net_avg_wait_time", (char *)&SHOW_FNAME(avg_net_wait_time), SHOW_FUNC, SHOW_SCOPE_GLOBAL}, + {"Rpl_semi_sync_master_trx_wait_histogram", + (char *)&rpl_semi_sync_master_trx_wait_histogram, SHOW_FUNC, + SHOW_SCOPE_GLOBAL}, {nullptr, nullptr, SHOW_LONG, SHOW_SCOPE_GLOBAL}, }; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 1179957b244a..1ab3defe28ba 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4113,6 +4113,246 @@ void init_my_timer(void) { } } +/** + Create a new Histogram. + + @param current_histogram The histogram being initialized. + @param step_size_with_unit Configurable system variable containing + step size and unit of the Histogram. +*/ +void latency_histogram_init(latency_histogram *current_histogram, + const char *step_size_with_unit) { + assert(current_histogram != 0); + + double step_size_base_time = 0.0; + current_histogram->num_bins = NUMBER_OF_HISTOGRAM_BINS; + + // this can potentially be made configurable later. + current_histogram->step_ratio = 2.0; + current_histogram->step_size = 0; + char *histogram_unit = NULL; + + for (int i = 0; i < NUMBER_OF_HISTOGRAM_BINS; i++) + current_histogram->count_per_bin[i].store(0); + + if (!step_size_with_unit) return; + + step_size_base_time = strtod(step_size_with_unit, &histogram_unit); + if (histogram_unit) { + if (!strcmp(histogram_unit, "s")) { + current_histogram->step_size = + microseconds_to_my_timer(step_size_base_time * 1000000.0); + } else if (!strcmp(histogram_unit, "ms")) { + current_histogram->step_size = + microseconds_to_my_timer(step_size_base_time * 1000.0); + } else if (!strcmp(histogram_unit, "us")) { + current_histogram->step_size = + microseconds_to_my_timer(step_size_base_time); + } + /* Special case when step size is passed to be '0' */ + else if (*histogram_unit == '\0') { + if (step_size_base_time == 0.0) { + current_histogram->step_size = 0; + } else + current_histogram->step_size = + microseconds_to_my_timer(step_size_base_time); + } else { + sql_print_error("Invalid units given to histogram step size."); + return; + } + } else { + /* NO_LINT_DEBUG */ + sql_print_error("Invalid histogram step size."); + return; + } +} + +/** + Search a value in the histogram bins. + + @param current_histogram The current histogram. + @param value Value to be searched. + + @return Returns the bin that contains this value. + -1 if Step Size is 0 +*/ +static int latency_histogram_bin_search(latency_histogram *current_histogram, + ulonglong value) { + if (current_histogram->step_size == 0 || value == 0 || + current_histogram->step_ratio <= 0.0) + return -1; + + double dbin_no = std::log2((double)value / current_histogram->step_size) / + std::log2(current_histogram->step_ratio); + + int ibin_no = (int)dbin_no; + if (ibin_no < 0) return 0; + + return min(ibin_no, (int)current_histogram->num_bins - 1); +} + +/** + Increment the count of a bin in Histogram. + + @param current_histogram The current histogram. + @param value Value of which corresponding bin has to be found. + @param count Amount by which the count of a bin has to be + increased. + +*/ +void latency_histogram_increment(latency_histogram *current_histogram, + ulonglong value, ulonglong count) { + int index = latency_histogram_bin_search(current_histogram, value); + if (index < 0) return; + current_histogram->count_per_bin[index] += count; +} + +/** + Get the count corresponding to a bin of the Histogram. + + @param current_histogram The current histogram. + @param bin_num The bin whose count has to be returned. + + @return Returns the count of that bin. +*/ +ulonglong latency_histogram_get_count(latency_histogram *current_histogram, + size_t bin_num) { + return (current_histogram->count_per_bin)[bin_num]; +} + +/** + Validate if the string passed to the configurable histogram step size + conforms to proper syntax. + + @param step_size_with_unit The configurable step size string to be checked. + + @return 1 if invalid, 0 if valid. +*/ +int histogram_validate_step_size_string(const char *step_size_with_unit) { + if (step_size_with_unit == nullptr) return 0; + + int ret = 0; + char *histogram_unit = NULL; + double histogram_step_size = strtod(step_size_with_unit, &histogram_unit); + if (histogram_step_size && histogram_unit) { + if (strcmp(histogram_unit, "ms") && strcmp(histogram_unit, "us") && + strcmp(histogram_unit, "s")) + ret = 1; + } + /* Special case when step size is passed to be '0' */ + else if (*histogram_unit == '\0' && histogram_step_size == 0.0) + return 0; + else + ret = 1; + return ret; +} + +/** + This function is called to convert the histogram bucket ranges in system time + units to a string and calculates units on the fly, which can be displayed in + the output of SHOW GLOBAL STATUS. + The string has the following form: + + _- + + @param bucket_lower_display Lower Range value of the Histogram Bucket + @param bucket_upper_display Upper Range value of the Histogram Bucket + @param is_last_bucket Flag to denote last bucket in the histogram + + @return The display string for the Histogram Bucket +*/ +histogram_display_string histogram_bucket_to_display_string( + ulonglong bucket_lower_display, ulonglong bucket_upper_display, + bool is_last_bucket) { + struct histogram_display_string histogram_bucket_name; + + std::string time_unit_suffix; + ulonglong time_factor = 1; + + if ((bucket_upper_display % 1000000) == 0 && + (bucket_lower_display % 1000000) == 0) { + time_unit_suffix = "s"; + time_factor = 1000000; + } else if ((bucket_upper_display % 1000) == 0 && + (bucket_lower_display % 1000) == 0) { + time_unit_suffix = "ms"; + time_factor = 1000; + } else { + time_unit_suffix = "us"; + } + + std::string bucket_display_format; + if (is_last_bucket) { + bucket_display_format = "%llu-MAX" + time_unit_suffix; + snprintf(histogram_bucket_name.name, HISTOGRAM_BUCKET_NAME_MAX_SIZE, + bucket_display_format.c_str(), bucket_lower_display / time_factor); + } else { + bucket_display_format = "%llu-%llu" + time_unit_suffix; + snprintf(histogram_bucket_name.name, HISTOGRAM_BUCKET_NAME_MAX_SIZE, + bucket_display_format.c_str(), bucket_lower_display / time_factor, + bucket_upper_display / time_factor); + } + + return histogram_bucket_name; +} + +/** + Frees old histogram bucket display strings before assigning new ones. +*/ +void free_latency_histogram_sysvars(SHOW_VAR *latency_histogram_data) { + size_t i; + for (i = 0; i < NUMBER_OF_HISTOGRAM_BINS; ++i) { + if (latency_histogram_data[i].name) { + my_free(const_cast(latency_histogram_data[i].name)); + latency_histogram_data[i].name = nullptr; + } + } +} + +/** + This function is called by the plugin callback function + to add entries into the latency_histogram_xxxx array, by forming + the appropriate display string and fetching the histogram bin + counts. + + @param current_histogram Histogram whose values are currently added + in the SHOW_VAR array + @param latency_histogram_data SHOW_VAR array for the corresponding Histogram + @param histogram_values Values to be exported to Innodb status. + This array contains the bin counts of the + respective Histograms. +*/ +void prepare_latency_histogram_vars(latency_histogram *current_histogram, + SHOW_VAR *latency_histogram_data, + ulonglong *histogram_values) { + size_t i; + ulonglong bucket_lower_display, bucket_upper_display; + const SHOW_VAR temp_last = {NullS, NullS, SHOW_LONG, SHOW_SCOPE_GLOBAL}; + + free_latency_histogram_sysvars(latency_histogram_data); + + ulonglong itr_step_size = current_histogram->step_size; + for (i = 0, bucket_lower_display = 0; i < NUMBER_OF_HISTOGRAM_BINS; ++i) { + bucket_upper_display = my_timer_to_microseconds_ulonglong(itr_step_size) + + bucket_lower_display; + + struct histogram_display_string histogram_bucket_name = + histogram_bucket_to_display_string(bucket_lower_display, + bucket_upper_display, + i == NUMBER_OF_HISTOGRAM_BINS - 1); + + const SHOW_VAR temp = {my_strdup(key_memory_global_system_variables, + histogram_bucket_name.name, MYF(0)), + (char *)&(histogram_values[i]), SHOW_LONGLONG, + SHOW_SCOPE_GLOBAL}; + latency_histogram_data[i] = temp; + + bucket_lower_display = bucket_upper_display; + itr_step_size *= current_histogram->step_ratio; + } + latency_histogram_data[NUMBER_OF_HISTOGRAM_BINS] = temp_last; +} + /** Initialize one of the global date/time format variables. diff --git a/sql/mysqld.h b/sql/mysqld.h index 33b5ca4c4700..d6f7c27ee596 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -1001,6 +1001,122 @@ extern SERVICE_TYPE_NO_CONST(registry) * srv_registry; extern SERVICE_TYPE(dynamic_loader_scheme_file) * scheme_file_srv; extern SERVICE_TYPE(dynamic_loader) * dynamic_loader_srv; +/* Histogram struct to track various latencies */ +#define NUMBER_OF_HISTOGRAM_BINS 10 +struct latency_histogram { + size_t num_bins; + ulonglong step_size; + double step_ratio; + std::atomic count_per_bin[NUMBER_OF_HISTOGRAM_BINS]; +}; + +/* Convert native timer units in a ulonglong into microseconds in a ulonglong */ +inline ulonglong my_timer_to_microseconds_ulonglong(ulonglong when) { + ulonglong ret = (ulonglong)(when); + ret *= 1000000; + ret = (ulonglong)((ret + my_timer.frequency - 1) / my_timer.frequency); + return ret; +} + +/** Compression statistics for a fil_space */ +/** + Create a new Histogram. + + @param current_histogram The histogram being initialized. + @param step_size_with_unit Configurable system variable containing + step size and unit of the Histogram. +*/ +void latency_histogram_init(latency_histogram *current_histogram, + const char *step_size_with_unit); + +/** + Increment the count of a bin in Histogram. + + @param current_histogram The current histogram. + @param value Value of which corresponding bin has to be found. + @param count Amount by which the count of a bin has to be + increased. + +*/ +void latency_histogram_increment(latency_histogram *current_histogram, + ulonglong value, ulonglong count); + +/** + Get the count corresponding to a bin of the Histogram. + + @param current_histogram The current histogram. + @param bin_num The bin whose count has to be returned. + + @return Returns the count of that bin. +*/ +ulonglong latency_histogram_get_count(latency_histogram *current_histogram, + size_t bin_num); + +/** + Validate if the string passed to the configurable histogram step size + conforms to proper syntax. + + @param step_size_with_unit The configurable step size string to be checked. + + @return 1 if invalid, 0 if valid. +*/ +int histogram_validate_step_size_string(const char *step_size_with_unit); + +#define HISTOGRAM_BUCKET_NAME_MAX_SIZE \ + 64 /**< This is the maximum size \ + of the string: \ + "LowerBucketValue-" \ + "UpperBucketValue" \ + where bucket is the latency \ + histogram bucket and units \ + can be us,ms or s */ + +/** To return the displayable histogram name from + my_timer_to_display_string() */ +struct histogram_display_string { + char name[HISTOGRAM_BUCKET_NAME_MAX_SIZE]; +}; + +/** + This function is called to convert the histogram bucket ranges in system time + units to a string and calculates units on the fly, which can be displayed in + the output of SHOW GLOBAL STATUS. + The string has the following form: + + _- + + @param bucket_lower_display Lower Range value of the Histogram Bucket + @param bucket_upper_display Upper Range value of the Histogram Bucket + @param is_last_bucket Flag to denote last bucket in the histogram + + @return The display string for the Histogram Bucket +*/ +histogram_display_string histogram_bucket_to_display_string( + ulonglong bucket_lower_display, ulonglong bucket_upper_display, + bool is_last_bucket = false); + +/** + This function is called by the plugin callback function + to add entries into the latency_histogram_xxxx array, by forming + the appropriate display string and fetching the histogram bin + counts. + + @param current_histogram Histogram whose values are currently added + in the SHOW_VAR array + @param latency_histogram_data SHOW_VAR array for the corresponding Histogram + @param histogram_values Values to be exported to Innodb status. + This array contains the bin counts of the + respective Histograms. +*/ +void prepare_latency_histogram_vars(latency_histogram *current_histogram, + SHOW_VAR *latency_histogram_data, + ulonglong *histogram_values); + +/** + Frees old histogram bucket display strings before assigning new ones. +*/ +void free_latency_histogram_sysvars(SHOW_VAR *latency_histogram_data); + class Deployed_components; extern Deployed_components *g_deployed_components;