Commit cb5a5388 authored by unknown's avatar unknown

MDEV-26: Global transaction ID.

Slave now loads the GTID state from the master when connecting with
old-style filename/offset position.

This allows the user to use MASTER_GTID_POS=AUTO on next CHANGE MASTER
without any other action needed.
parent d98e2ea9
......@@ -6173,8 +6173,6 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
list_element *elist= 0, *next;
element *elem;
DBUG_ASSERT(in_transaction /* ToDo: new transaction for DDL etc. */);
mysql_reset_thd_for_next_command(thd, 0);
tlist.init_one_table(STRING_WITH_LEN("mysql"),
......@@ -6247,14 +6245,14 @@ end:
*/
ha_rollback_trans(thd, FALSE);
close_thread_tables(thd);
if (in_transaction)
if (!in_transaction)
ha_rollback_trans(thd, TRUE);
}
else
{
ha_commit_trans(thd, FALSE);
close_thread_tables(thd);
if (in_transaction)
if (!in_transaction)
ha_commit_trans(thd, TRUE);
}
}
......@@ -6387,6 +6385,89 @@ err:
}
/*
Parse a GTID at the start of a string, and update the pointer to point
at the first character after the parsed GTID.
GTID can be in short form with domain_id=0 implied, SERVERID-SEQNO.
Or long form, DOMAINID-SERVERID-SEQNO.
Returns 0 on ok, non-zero on parse error.
*/
static int
gtid_parser_helper(char **ptr, char *end, rpl_gtid *out_gtid)
{
char *q;
char *p= *ptr;
uint64 v1, v2, v3;
int err= 0;
q= end;
v1= (uint64)my_strtoll10(p, &q, &err);
if (err != 0 || v1 > (uint32)0xffffffff || q == end || *q != '-')
return 1;
p= q+1;
q= end;
v2= (uint64)my_strtoll10(p, &q, &err);
if (err != 0)
return 1;
if (q == end || *q != '-')
{
/* Short form SERVERID-SEQNO, domain_id=0 implied. */
out_gtid->domain_id= 0;
out_gtid->server_id= v1;
out_gtid->seq_no= v2;
*ptr= q;
return 0;
}
/* Long form DOMAINID-SERVERID-SEQNO. */
if (v2 > (uint32)0xffffffff)
return 1;
p= q+1;
q= end;
v3= (uint64)my_strtoll10(p, &q, &err);
if (err != 0)
return 1;
out_gtid->domain_id= v1;
out_gtid->server_id= v2;
out_gtid->seq_no= v3;
*ptr= q;
return 0;
}
/*
Update the slave replication state with the GTID position obtained from
master when connecting with old-style (filename,offset) position.
Returns 0 if ok, non-zero if error.
*/
int
rpl_slave_state::load(THD *thd, char *state_from_master)
{
char *end= state_from_master + strlen(state_from_master);
for (;;)
{
rpl_gtid gtid;
uint64 sub_id;
if (gtid_parser_helper(&state_from_master, end, &gtid) ||
!(sub_id= next_subid(gtid.domain_id)) ||
record_gtid(thd, &gtid, sub_id, false) ||
update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no))
return 1;
if (state_from_master == end)
break;
if (*state_from_master != ',')
return 1;
}
return 0;
}
bool
rpl_slave_state::is_empty()
{
......@@ -6649,52 +6730,29 @@ slave_connection_state::~slave_connection_state()
int
slave_connection_state::load(char *slave_request, size_t len)
{
char *p, *q, *end;
uint64 v;
uint32 domain_id, server_id;
uint64 seq_no;
char *p, *end;
uchar *rec;
rpl_gtid *gtid;
int err= 0;
my_hash_reset(&hash);
p= slave_request;
end= slave_request + len;
for (;;)
{
q= end;
v= (uint64)my_strtoll10(p, &q, &err);
if (err != 0 || v > (uint32)0xffffffff || *q != '-')
return 1;
domain_id= (uint32)v;
p= q+1;
q= end;
v= (uint64)my_strtoll10(p, &q, &err);
if (err != 0 || v > (uint32)0xffffffff || *q != '-')
return 1;
server_id= (uint32)v;
p= q+1;
q= end;
seq_no= (uint64)my_strtoll10(p, &q, &err);
if (err != 0)
return 1;
if (!(rec= (uchar *)my_malloc(sizeof(*gtid), MYF(MY_WME))))
return 1;
gtid= (rpl_gtid *)rec;
gtid->domain_id= domain_id;
gtid->server_id= server_id;
gtid->seq_no= seq_no;
if (my_hash_insert(&hash, rec))
if (gtid_parser_helper(&p, end, gtid) ||
my_hash_insert(&hash, rec))
{
my_free(rec);
return 1;
}
if (q == end)
if (p == end)
break; /* Finished. */
if (*q != ',')
if (*p != ',')
return 1;
p= q+1;
++p;
}
return 0;
......@@ -7650,31 +7708,22 @@ int Xid_log_event::do_apply_event(Relay_log_info const *rli)
mysql.rpl_slave_state is non-transactional and the row is not removed
by rollback.
*/
rpl_slave_state::element *elem=
rpl_global_gtid_slave_state.get_element(gtid.domain_id);
rpl_slave_state::list_element *lelem=
(rpl_slave_state::list_element *)my_malloc(sizeof(*lelem), MYF(MY_WME));
if (elem && lelem)
{
lelem->sub_id= sub_id;
lelem->server_id= gtid.server_id;
lelem->seq_no= gtid.seq_no;
elem->add(lelem);
}
else
rpl_global_gtid_slave_state.lock();
err= rpl_global_gtid_slave_state.update(gtid.domain_id, gtid.server_id,
sub_id, gtid.seq_no);
rpl_global_gtid_slave_state.unlock();
if (err)
{
if (lelem)
my_free(lelem);
sql_print_warning("Slave: Out of memory during slave state maintenance. "
"Some no longer necessary rows in table "
"mysql.rpl_slave_state may be left undeleted.");
}
/*
Such failure is not fatal. We will fail to delete the row for this GTID,
but it will do no harm and will be removed automatically on next server
restart.
Such failure is not fatal. We will fail to delete the row for this
GTID, but it will do no harm and will be removed automatically on next
server restart.
*/
}
}
/*
Increment the global status commit count variable
......
......@@ -3009,6 +3009,7 @@ struct rpl_slave_state
bool in_transaction);
uint64 next_subid(uint32 domain_id);
int tostring(String *dest, rpl_gtid *extra_gtids, uint32 num_extra);
int load(THD *thd, char *state_from_master);
bool is_empty();
void lock() { DBUG_ASSERT(inited); mysql_mutex_lock(&LOCK_slave_state); }
......
......@@ -1837,6 +1837,60 @@ after_set_capability:
}
}
}
else
{
/*
If we are not using GTID to connect this time, then instead request
the corresponding GTID position from the master, so that the user
can reconnect the next time using MASTER_GTID_POS=AUTO.
*/
char quote_buf[2*sizeof(mi->master_log_name)+1];
char str_buf[28+2*sizeof(mi->master_log_name)+10];
String query(str_buf, sizeof(str_buf), system_charset_info);
query.length(0);
query.append("SELECT binlog_gtid_pos('");
escape_quotes_for_mysql(&my_charset_bin, quote_buf, sizeof(quote_buf),
mi->master_log_name, strlen(mi->master_log_name));
query.append(quote_buf);
query.append("',");
query.append_ulonglong(mi->master_log_pos);
query.append(")");
if (!mysql_real_query(mysql, query.c_ptr_safe(), query.length()) &&
(master_res= mysql_store_result(mysql)) &&
(master_row= mysql_fetch_row(master_res)) &&
(master_row[0] != NULL))
{
rpl_global_gtid_slave_state.load(mi->io_thd, master_row[0]);
}
else if (check_io_slave_killed(mi->io_thd, mi, NULL))
goto slave_killed_err;
else if (is_network_error(mysql_errno(mysql)))
{
mi->report(WARNING_LEVEL, mysql_errno(mysql),
"Get master GTID position failed with error: %s", mysql_error(mysql));
goto network_err;
}
else
{
/*
ToDo: If the master does not have the binlog_gtid_pos() function, it
just means that it is an old master with no GTID support, so we should
do nothing.
However, if binlog_gtid_pos() exists, but fails or returns NULL, then
it means that the requested position is not valid. We could use this
to catch attempts to replicate from within the middle of an event,
avoiding strange failures or possible corruption.
*/
}
if (master_res)
{
mysql_free_result(master_res);
master_res= NULL;
}
}
err:
if (errmsg)
......
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