Commit f9c2b402 authored by unknown's avatar unknown

MDEV-26: Global transaction ID.

Implement @@gtid_binlog_state. This is the internal state of the binlog
(most recent GTID logged for every domain_id and server_id). This allows
to save the state before RESET MASTER and restore it afterwards.
parent 62d35829
......@@ -32,6 +32,7 @@ BEGIN
AND variable_name != 'INNODB_IBUF_MAX_SIZE'
AND variable_name != 'INNODB_USE_NATIVE_AIO'
AND variable_name not like 'GTID%POS'
AND variable_name != 'GTID_BINLOG_STATE'
ORDER BY variable_name;
-- Dump all databases, there should be none
......
......@@ -162,4 +162,42 @@ BINLOG_GTID_POS('master-bin.000001',18446744073709551616)
NULL
Warnings:
Warning 1916 Got overflow when converting '18446744073709551616' to INT. Value truncated.
*** Some tests of @@GLOBAL.gtid_binlog_state ***
include/stop_slave.inc
SET @old_state= @@GLOBAL.gtid_binlog_state;
SET GLOBAL gtid_binlog_state = '';
ERROR HY000: This operation is not allowed if any GTID has been logged to the binary log. Run RESET MASTER first to erase the log
RESET MASTER;
SET GLOBAL gtid_binlog_state = '';
FLUSH LOGS;
show binary logs;
Log_name File_size
master-bin.000001 #
master-bin.000002 #
SET GLOBAL gtid_binlog_state = '0-1-10,1-2-20,0-3-30';
show binary logs;
Log_name File_size
master-bin.000001 #
show binlog events in 'master-bin.000001' from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Format_desc # # SERVER_VERSION, BINLOG_VERSION
master-bin.000001 # Gtid_list # # [1-2-20,0-1-10,0-3-30]
master-bin.000001 # Binlog_checkpoint # # master-bin.000001
SELECT @@GLOBAL.gtid_binlog_pos;
@@GLOBAL.gtid_binlog_pos
1-2-20,0-3-30
SELECT @@GLOBAL.gtid_binlog_state;
@@GLOBAL.gtid_binlog_state
1-2-20,0-1-10,0-3-30
SET GLOBAL gtid_binlog_state = @old_state;
ERROR HY000: This operation is not allowed if any GTID has been logged to the binary log. Run RESET MASTER first to erase the log
RESET MASTER;
SET GLOBAL gtid_binlog_state = @old_state;
CREATE TABLE t1 (a INT PRIMARY KEY);
INSERT INTO t1 VALUES (1);
include/start_slave.inc
SELECT * FROM t1;
a
1
DROP TABLE t1;
include/rpl_end.inc
......@@ -160,4 +160,47 @@ eval SELECT BINLOG_GTID_POS('$valid_binlog_name',0);
eval SELECT BINLOG_GTID_POS('$valid_binlog_name',18446744073709551615);
eval SELECT BINLOG_GTID_POS('$valid_binlog_name',18446744073709551616);
--echo *** Some tests of @@GLOBAL.gtid_binlog_state ***
--connection server_2
--source include/stop_slave.inc
--connection server_1
SET @old_state= @@GLOBAL.gtid_binlog_state;
--error ER_BINLOG_MUST_BE_EMPTY
SET GLOBAL gtid_binlog_state = '';
RESET MASTER;
SET GLOBAL gtid_binlog_state = '';
FLUSH LOGS;
--source include/show_binary_logs.inc
SET GLOBAL gtid_binlog_state = '0-1-10,1-2-20,0-3-30';
--source include/show_binary_logs.inc
--let $binlog_file= master-bin.000001
--let $binlog_start= 4
--source include/show_binlog_events.inc
SELECT @@GLOBAL.gtid_binlog_pos;
SELECT @@GLOBAL.gtid_binlog_state;
--error ER_BINLOG_MUST_BE_EMPTY
SET GLOBAL gtid_binlog_state = @old_state;
RESET MASTER;
SET GLOBAL gtid_binlog_state = @old_state;
# Check that slave can reconnect again, despite the RESET MASTER, as we
# restored the state.
CREATE TABLE t1 (a INT PRIMARY KEY);
INSERT INTO t1 VALUES (1);
--save_master_pos
--connection server_2
--source include/start_slave.inc
--sync_with_master
SELECT * FROM t1;
--connection server_1
DROP TABLE t1;
--source include/rpl_end.inc
SELECT @@GLOBAL.gtid_slave_pos;
@@GLOBAL.gtid_slave_pos
SET gtid_binlog_state= '';
ERROR HY000: Variable 'gtid_binlog_state' is a GLOBAL variable and should be set with SET GLOBAL
SET SESSION gtid_binlog_state= '';
ERROR HY000: Variable 'gtid_binlog_state' is a GLOBAL variable and should be set with SET GLOBAL
SET GLOBAL gtid_binlog_state= DEFAULT;
ERROR 42000: Variable 'gtid_binlog_state' doesn't have a default value
SELECT @@session.gtid_binlog_state;
ERROR HY000: Variable 'gtid_binlog_state' is a GLOBAL variable
......@@ -21,13 +21,13 @@ SELECT @@gtid_slave_pos;
@@gtid_slave_pos
1-2-3,2-4-6
SET GLOBAL gtid_slave_pos= '-1-2-3';
ERROR HY000: Could not parse GTID list for GTID_POS
ERROR HY000: Could not parse GTID list
SET GLOBAL gtid_slave_pos= '1-2 -3';
ERROR HY000: Could not parse GTID list for GTID_POS
ERROR HY000: Could not parse GTID list
SET GLOBAL gtid_slave_pos= '1-2-3 ';
ERROR HY000: Could not parse GTID list for GTID_POS
ERROR HY000: Could not parse GTID list
SET GLOBAL gtid_slave_pos= '1-2-3,2-4';
ERROR HY000: Could not parse GTID list for GTID_POS
ERROR HY000: Could not parse GTID list
SET GLOBAL gtid_slave_pos= '0-1-10,0-2-20';
ERROR HY000: GTID 0-2-20 and 0-1-10 conflict (duplicate domain id 0)
SET GLOBAL gtid_slave_pos= '0-1-10,1-2-20,2-3-30,1-20-200,3-4-1';
......
--source include/not_embedded.inc
# Most things with gtid_binlog_state requires binlog enabled, and so is
# tested in rpl suite.
SELECT @@GLOBAL.gtid_slave_pos;
--error ER_GLOBAL_VARIABLE
SET gtid_binlog_state= '';
--error ER_GLOBAL_VARIABLE
SET SESSION gtid_binlog_state= '';
--error ER_NO_DEFAULT
SET GLOBAL gtid_binlog_state= DEFAULT;
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
SELECT @@session.gtid_binlog_state;
......@@ -3703,7 +3703,8 @@ int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock)
1 error
*/
bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log)
bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log,
rpl_gtid *init_state, uint32 init_state_len)
{
LOG_INFO linfo;
bool error=0;
......@@ -3722,6 +3723,14 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log)
if (!is_relay_log)
{
if (init_state && !is_empty_state())
{
my_error(ER_BINLOG_MUST_BE_EMPTY, MYF(0));
mysql_mutex_unlock(&LOCK_index);
mysql_mutex_unlock(&LOCK_log);
DBUG_RETURN(1);
}
/*
Mark that a RESET MASTER is in progress.
This ensures that a binlog checkpoint will not try to write binlog
......@@ -3839,7 +3848,10 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log)
if (!is_relay_log)
{
rpl_global_gtid_binlog_state.reset();
if (init_state)
rpl_global_gtid_binlog_state.load(init_state, init_state_len);
else
rpl_global_gtid_binlog_state.reset();
}
/* Start logging with a new file */
......@@ -5524,6 +5536,30 @@ MYSQL_BIN_LOG::append_state_pos(String *str)
}
bool
MYSQL_BIN_LOG::append_state(String *str)
{
bool err;
mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
err= rpl_global_gtid_binlog_state.append_state(str);
mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
return err;
}
bool
MYSQL_BIN_LOG::is_empty_state()
{
bool res;
mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
res= (rpl_global_gtid_binlog_state.count() == 0);
mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
return res;
}
bool
MYSQL_BIN_LOG::find_in_binlog_state(uint32 domain_id, uint32 server_id,
rpl_gtid *out_gtid)
......
......@@ -751,7 +751,8 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
int register_create_index_entry(const char* entry);
int purge_index_entry(THD *thd, ulonglong *decrease_log_space,
bool need_mutex);
bool reset_logs(THD* thd, bool create_new_log);
bool reset_logs(THD* thd, bool create_new_log,
rpl_gtid *init_state, uint32 init_state_len);
void close(uint exiting);
void clear_inuse_flag_when_closing(File file);
......@@ -780,6 +781,8 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
int write_state_to_file();
int get_most_recent_gtid_list(rpl_gtid **list, uint32 *size);
bool append_state_pos(String *str);
bool append_state(String *str);
bool is_empty_state();
bool find_in_binlog_state(uint32 domain_id, uint32 server_id,
rpl_gtid *out_gtid);
bool lookup_domain_in_binlog_state(uint32 domain_id, rpl_gtid *out_gtid);
......
......@@ -719,6 +719,45 @@ gtid_parser_helper(char **ptr, char *end, rpl_gtid *out_gtid)
}
rpl_gtid *
gtid_parse_string_to_list(const char *str, size_t str_len, uint32 *out_len)
{
char *p= const_cast<char *>(str);
char *end= p + str_len;
uint32 len= 0, alloc_len= 5;
rpl_gtid *list= NULL;
for (;;)
{
rpl_gtid gtid;
if (len >= (((uint32)1 << 28)-1) || gtid_parser_helper(&p, end, &gtid))
{
my_free(list);
return NULL;
}
if ((!list || len >= alloc_len) &&
!(list=
(rpl_gtid *)my_realloc(list,
(alloc_len= alloc_len*2) * sizeof(rpl_gtid),
MYF(MY_FREE_ON_ERROR|MY_ALLOW_ZERO_PTR))))
return NULL;
list[len++]= gtid;
if (p == end)
break;
if (*p != ',')
{
my_free(list);
return NULL;
}
++p;
}
*out_len= len;
return list;
}
/*
Update the slave replication state with the GTID position obtained from
master when connecting with old-style (filename,offset) position.
......@@ -1234,6 +1273,41 @@ rpl_binlog_state::append_pos(String *str)
}
bool
rpl_binlog_state::append_state(String *str)
{
uint32 i, j;
bool first= true;
for (i= 0; i < hash.records; ++i)
{
element *e= (element *)my_hash_element(&hash, i);
if (!e->last_gtid)
{
DBUG_ASSERT(e->hash.records==0);
continue;
}
for (j= 0; j <= e->hash.records; ++j)
{
const rpl_gtid *gtid;
if (j < e->hash.records)
{
gtid= (rpl_gtid *)my_hash_element(&e->hash, j);
if (gtid == e->last_gtid)
continue;
}
else
gtid= e->last_gtid;
if (rpl_slave_state_tostring_helper(str, gtid, &first))
return true;
}
}
return false;
}
slave_connection_state::slave_connection_state()
{
my_hash_init(&hash, &my_charset_bin, 32,
......
......@@ -163,6 +163,7 @@ struct rpl_binlog_state
int get_gtid_list(rpl_gtid *gtid_list, uint32 list_size);
int get_most_recent_gtid_list(rpl_gtid **list, uint32 *size);
bool append_pos(String *str);
bool append_state(String *str);
rpl_gtid *find(uint32 domain_id, uint32 server_id);
rpl_gtid *find_most_recent(uint32 domain_id);
};
......@@ -205,5 +206,7 @@ struct slave_connection_state
extern bool rpl_slave_state_tostring_helper(String *dest, const rpl_gtid *gtid,
bool *first);
extern int gtid_check_rpl_slave_state_table(TABLE *table);
extern rpl_gtid *gtid_parse_string_to_list(const char *p, size_t len,
uint32 *out_len);
#endif /* RPL_GTID_H */
......@@ -1011,7 +1011,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
rli->cur_log_fd= -1;
}
if (rli->relay_log.reset_logs(thd, !just_reset))
if (rli->relay_log.reset_logs(thd, !just_reset, NULL, 0))
{
*errmsg = "Failed during log reset";
error=1;
......
......@@ -6527,7 +6527,7 @@ ER_SQL_DISCOVER_ERROR
ER_FAILED_GTID_STATE_INIT
eng "Failed initializing replication GTID state"
ER_INCORRECT_GTID_STATE
eng "Could not parse GTID list for GTID_POS"
eng "Could not parse GTID list"
ER_CANNOT_UPDATE_GTID_STATE
eng "Could not update replication slave gtid state"
ER_DUPLICATE_GTID_DOMAIN
......@@ -6557,3 +6557,5 @@ ER_STORED_FUNCTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO
eng "Cannot modify @@session.gtid_domain_id or @@session.gtid_seq_no inside a stored function or trigger"
ER_GTID_POSITION_NOT_FOUND_IN_BINLOG2
eng "Connecting slave requested to start from GTID %u-%u-%llu, which is not in the master's binlog. Since the master's binlog contains GTIDs with higher sequence numbers, it probably means that the slave has diverged due to executing extra errorneous transactions"
ER_BINLOG_MUST_BE_EMPTY
eng "This operation is not allowed if any GTID has been logged to the binary log. Run RESET MASTER first to erase the log"
......@@ -322,7 +322,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
{
DBUG_ASSERT(thd);
tmp_write_to_binlog= 0;
if (reset_master(thd))
if (reset_master(thd, NULL, 0))
{
/* NOTE: my_error() has been already called by reset_master(). */
result= 1;
......
......@@ -3474,7 +3474,7 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added)
@retval 0 success
@retval 1 error
*/
int reset_master(THD* thd)
int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len)
{
if (!mysql_bin_log.is_open())
{
......@@ -3483,7 +3483,7 @@ int reset_master(THD* thd)
return 1;
}
if (mysql_bin_log.reset_logs(thd, 1))
if (mysql_bin_log.reset_logs(thd, 1, init_state, init_state_len))
return 1;
RUN_HOOK(binlog_transmit, after_reset_master, (thd, 0 /* flags */));
return 0;
......
......@@ -46,7 +46,7 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report);
bool change_master(THD* thd, Master_info* mi, bool *master_info_added);
bool mysql_show_binlog_events(THD* thd);
int reset_slave(THD *thd, Master_info* mi);
int reset_master(THD* thd);
int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len);
bool purge_master_logs(THD* thd, const char* to_log);
bool purge_master_logs_before_date(THD* thd, time_t purge_time);
bool log_in_use(const char* log_name);
......
......@@ -1434,6 +1434,107 @@ static Sys_var_mybool Sys_gtid_strict_mode(
"generate an out-of-order binlog if executed.",
GLOBAL_VAR(opt_gtid_strict_mode),
CMD_LINE(OPT_ARG), DEFAULT(FALSE));
struct gtid_binlog_state_data { rpl_gtid *list; uint32 list_len; };
bool
Sys_var_gtid_binlog_state::do_check(THD *thd, set_var *var)
{
String str, *res;
struct gtid_binlog_state_data *data;
rpl_gtid *list;
uint32 list_len;
DBUG_ASSERT(var->type == OPT_GLOBAL);
if (!(res= var->value->val_str(&str)))
return true;
if (thd->in_active_multi_stmt_transaction())
{
my_error(ER_CANT_DO_THIS_DURING_AN_TRANSACTION, MYF(0));
return true;
}
if (!mysql_bin_log.is_open())
{
my_error(ER_FLUSH_MASTER_BINLOG_CLOSED, MYF(0));
return true;
}
if (!mysql_bin_log.is_empty_state())
{
my_error(ER_BINLOG_MUST_BE_EMPTY, MYF(0));
return true;
}
if (res->length() == 0)
list= NULL;
else if (!(list= gtid_parse_string_to_list(res->ptr(), res->length(),
&list_len)))
{
my_error(ER_INCORRECT_GTID_STATE, MYF(0));
return true;
}
if (!(data= (gtid_binlog_state_data *)my_malloc(sizeof(*data), MYF(0))))
{
my_free(list);
my_error(ER_OUT_OF_RESOURCES, MYF(0));
return true;
}
data->list= list;
data->list_len= list_len;
var->save_result.ptr= data;
return false;
}
bool
Sys_var_gtid_binlog_state::global_update(THD *thd, set_var *var)
{
bool res;
DBUG_ASSERT(var->type == OPT_GLOBAL);
if (!var->value)
{
my_error(ER_NO_DEFAULT, MYF(0), var->var->name.str);
return true;
}
struct gtid_binlog_state_data *data=
(struct gtid_binlog_state_data *)var->save_result.ptr;
mysql_mutex_unlock(&LOCK_global_system_variables);
res= (0 != reset_master(thd, data->list, data->list_len));
mysql_mutex_lock(&LOCK_global_system_variables);
my_free(data->list);
my_free(data);
return res;
}
uchar *
Sys_var_gtid_binlog_state::global_value_ptr(THD *thd, LEX_STRING *base)
{
char buf[512];
String str(buf, sizeof(buf), system_charset_info);
char *p;
str.length(0);
if ((opt_bin_log && mysql_bin_log.append_state(&str)) ||
!(p= thd->strmake(str.ptr(), str.length())))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
return NULL;
}
return (uchar *)p;
}
static unsigned char opt_gtid_binlog_state_dummy;
static Sys_var_gtid_binlog_state Sys_gtid_binlog_state(
"gtid_binlog_state",
"The internal GTID state of the binlog, used to keep track of all "
"GTIDs ever logged to the binlog.",
GLOBAL_VAR(opt_gtid_binlog_state_dummy), NO_CMD_LINE);
#endif
......
......@@ -2167,3 +2167,44 @@ class Sys_var_gtid_slave_pos: public sys_var
}
uchar *global_value_ptr(THD *thd, LEX_STRING *base);
};
/**
Class for @@global.gtid_binlog_state.
*/
class Sys_var_gtid_binlog_state: public sys_var
{
public:
Sys_var_gtid_binlog_state(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt)
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
NULL, NULL, NULL)
{
option.var_type= GET_STR;
}
bool do_check(THD *thd, set_var *var);
bool session_update(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
return true;
}
bool global_update(THD *thd, set_var *var);
bool check_update_type(Item_result type) { return type != STRING_RESULT; }
void session_save_default(THD *thd, set_var *var)
{
DBUG_ASSERT(false);
}
void global_save_default(THD *thd, set_var *var)
{
/* Record the attempt to use default so we can error. */
var->value= 0;
}
uchar *session_value_ptr(THD *thd, LEX_STRING *base)
{
DBUG_ASSERT(false);
return NULL;
}
uchar *global_value_ptr(THD *thd, LEX_STRING *base);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment