← Back to team overview

maria-developers team mailing list archive

Re: [External] Obsolete GTID domain delete on master (MDEV-12012, MDEV-11969)

 

andrei.elkin@xxxxxxxxxx writes:

> The patch is ready for review and can be located on bb-10.1-andrei,
> https://github.com/MariaDB/server/pull/460

I think the patch is ok, it looks of good quality and well thought out.

A few comments/suggestions:


1. In drop_domain(), the use of the condition if (strlen(errbuf)) would be
clearer if it was a separate flag. As the code is now, it implicitly
requires errbuf to be initialised to the empty string by caller, which is
needlessly errorprone.

(In general, I think using the errmsg also as a flag is best avoided, but I
realise this is partly inherited also from existing code, as a work-around
for C not allowing to return multiple values easily. But inside
drop_domain() it is easy to use a separate flag).


2. I would re-consider if this warning is useful:

  sprintf(errbuf,
          "missing gtids from '%u-%u' domain-server pair "
          "which is referred in Gtid list describing earlier binlog state; "
          "ignore it if the domain was already explicitly deleted",
          glev->list[l].domain_id, glev->list[l].server_id);

Since, as you write in the patch, with this feature it will be a normal
state of affairs that domains can be removed from the binlog state.


3. I am not sure I understand the purpose of this warning:

  sprintf(errbuf,
          "having gtid '%u-%u-%llu' which is lesser than "
          "'%u-%u-%llu' of Gtid list describing earlier binlog state; "
          "possibly the binlog state was affected by smaller sequence number "
          "gtid injection (manually or via replication)",
          rb_state_gtid->domain_id, rb_state_gtid->server_id,
          rb_state_gtid->seq_no, glev->list[l].domain_id,
          glev->list[l].server_id, glev->list[l].seq_no);
  push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
                      ER_BINLOG_CANT_DELETE_GTID_DOMAIN,
                      "the current gtid binlog state is incompatible to "
                      "a former one %s", errbuf);

The ER_BINLOG_CANT_DELETE_GTID_DOMAIN says "Could not delete gtid domain".
But if I understand correctly, this warning is actually unrelated to the
domains being listed in DELETE DOMAIN_ID (and in fact such domains can be
deleted successfully despite this message).

Having a warning here might be ok (this condition would be considered a
corruption of the binary log, out-of-order within the same server-id is not
valid). But it might be confusing to users the way it is done here?


4. Also consider asking Ian Gilfillan (who did a lot of documentation on
MariaDB) for help in clarifying the different error and warning messages in
the patch. Eg. "is lesser than" is not correct English (like you, I also am
not a native English speaker).


Thanks for the patch, this is something that has been requested a number of
times. You might consider taking over this task from Jira, which (if I
understand the description correctly) you have basically solved (if with a
different/better syntax):

  https://jira.mariadb.org/browse/MDEV-9241

 - Kristian.


> From 3e9d06db84ab8cd761717fcb5ca4a05dfed70da0 Mon Sep 17 00:00:00 2001
> From: Andrei Elkin <andrei.elkin@xxxxxxxxxxx>
> Date: Fri, 29 Sep 2017 21:56:59 +0300
> Subject: [PATCH] MDEV-12012/MDEV-11969 Can't remove GTIDs for a stale GTID
>  Domain ID
> 
> As reported in MDEV-11969 "there's no way to ditch knowledge" about some
> domain that is no longer updated on a server. Besides being of annoyance to
> clutter output in DBA console stale domains can prevent the slave
> to connect the master as MDEV-12012 witnesses.
> What domain is obsolete must be evaluated by the user (DBA) according
> to whether the domain info is still relevant and will the domain ever
> receive any update.
> 
> This patch introduces a method to discard obsolete gtid domains from
> the server binlog state. The removal requires no event group from such
> domain present in existing binlog files though. If there are any the
> containing logs must be first PURGEd in order for
> 
>   FLUSH BINARY LOGS DELETE_DOMAIN_ID=(list-of-domains)
> 
> succeed. Otherwise the command returns an error.
> 
> The list of obsolete domains can be computed through
> intersecting two sets - the earliest (first) binlog's Gtid_list
> and the current value of @@global.gtid_binlog_state - and extracting
> the domain id components from the intersection list items.
> The new DELETE_DOMAIN_ID featured FLUSH continues to rotate binlog
> omitting the deleted domains from the active binlog file's Gtid_list.
> Notice though when the command is ineffective - that none of requested to delete
> domain exists in the binlog state - rotation does not occur.
> 
> Obsolete domain deletion is not harmful for connected slaves as long
> as master side binlog files *purge* is synchronized with FLUSH-DELETE_DOMAIN_ID.
> The slaves must have the last event from purged files processed as usual,
> in order not to bump later into requesting a gtid from a file which
> was already gone.
> While the command is not replicated (as ordinary FLUSH BINLOG LOGS is)
> slaves, even though having extra domains, won't suffer from reconnection errors
> thanks to master-slave gtid connection protocol allowing the master
> to be ignorant about a gtid domain.
> Should at failover such slave to be promoted into master role it may run
> the ex-master's
> 
>  FLUSH BINARY LOGS DELETE_DOMAIN_ID=(list-of-domains)
> 
> to clean its own binlog state.
> 
> NOTES.
>   suite/perfschema/r/start_server_low_digest.result
> is re-recorded as consequence of internal parser codes changes.
> ---
>  mysql-test/include/show_gtid_list.inc              |  15 ++
>  .../r/binlog_flush_binlogs_delete_domain.result    |  78 +++++++++
>  .../r/binlog_gtid_delete_domain_debug.result       |   6 +
>  .../t/binlog_flush_binlogs_delete_domain.test      | 136 +++++++++++++++
>  .../binlog/t/binlog_gtid_delete_domain_debug.test  |  11 ++
>  .../perfschema/r/start_server_low_digest.result    |   4 +-
>  .../suite/rpl/r/rpl_gtid_delete_domain.result      |  30 ++++
>  mysql-test/suite/rpl/t/rpl_gtid_delete_domain.test |  95 ++++++++++
>  sql/lex.h                                          |   1 +
>  sql/log.cc                                         | 194 ++++++++++++++++++++-
>  sql/log.h                                          |   7 +-
>  sql/rpl_gtid.cc                                    | 150 +++++++++++++++-
>  sql/rpl_gtid.h                                     |   9 +
>  sql/share/errmsg-utf8.txt                          |   2 +
>  sql/sql_lex.cc                                     |   5 +
>  sql/sql_lex.h                                      |   7 +
>  sql/sql_reload.cc                                  |   5 +-
>  sql/sql_repl.cc                                    |  68 +-------
>  sql/sql_repl.h                                     |   1 -
>  sql/sql_yacc.yy                                    |  22 ++-
>  20 files changed, 769 insertions(+), 77 deletions(-)
>  create mode 100644 mysql-test/include/show_gtid_list.inc
>  create mode 100644 mysql-test/suite/binlog/r/binlog_flush_binlogs_delete_domain.result
>  create mode 100644 mysql-test/suite/binlog/r/binlog_gtid_delete_domain_debug.result
>  create mode 100644 mysql-test/suite/binlog/t/binlog_flush_binlogs_delete_domain.test
>  create mode 100644 mysql-test/suite/binlog/t/binlog_gtid_delete_domain_debug.test
>  create mode 100644 mysql-test/suite/rpl/r/rpl_gtid_delete_domain.result
>  create mode 100644 mysql-test/suite/rpl/t/rpl_gtid_delete_domain.test
> 
> diff --git a/mysql-test/include/show_gtid_list.inc b/mysql-test/include/show_gtid_list.inc
> new file mode 100644
> index 000000000000..96f813f180cb
> --- /dev/null
> +++ b/mysql-test/include/show_gtid_list.inc
> @@ -0,0 +1,15 @@
> +# ==== Purpose ====
> +#
> +# Extract Gtid_list info from SHOW BINLOG EVENTS output masking
> +# non-deterministic fields.
> +#
> +# ==== Usage ====
> +#
> +# [--let $binlog_file=filename
> +#
> +if ($binlog_file)
> +{
> +  --let $_in_binlog_file=in '$binlog_file'
> +}
> +--replace_column 2 # 5 #
> +--eval show binlog events $_in_binlog_file limit 1,1
> diff --git a/mysql-test/suite/binlog/r/binlog_flush_binlogs_delete_domain.result b/mysql-test/suite/binlog/r/binlog_flush_binlogs_delete_domain.result
> new file mode 100644
> index 000000000000..584515aff354
> --- /dev/null
> +++ b/mysql-test/suite/binlog/r/binlog_flush_binlogs_delete_domain.result
> @@ -0,0 +1,78 @@
> +RESET MASTER;
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = ();
> +and the command execution is effective thence rotates binlog as usual
> +show binary logs;
> +Log_name	File_size
> +master-bin.000001	#
> +master-bin.000002	#
> +Non-existed domain is warned, the command completes without rotation
> +but with a warning
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (99);
> +Warnings:
> +Warning	1982	being deleted gtid domain '99' is not in the current binlog state
> +show binary logs;
> +Log_name	File_size
> +master-bin.000001	#
> +master-bin.000002	#
> +SET @@SESSION.gtid_domain_id=1;
> +SET @@SESSION.server_id=1;
> +CREATE TABLE t (a int);
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (1);
> +ERROR HY000: Could not delete gtid domain. Reason: binlog files may contain gtids from being deleted domain '1'; make sure first to purge those files.
> +FLUSH BINARY LOGS;
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (1);
> +ERROR HY000: Could not delete gtid domain. Reason: binlog files may contain gtids from being deleted domain '1'; make sure first to purge those files.
> +PURGE BINARY LOGS TO 'master-bin.000003';;
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (1);
> +Gtid_list of the current binlog does not contain '1':
> +show binlog events in 'master-bin.000004' limit 1,1;
> +Log_name	Pos	Event_type	Server_id	End_log_pos	Info
> +master-bin.000004	#	Gtid_list	1	#	[]
> +But the previous log's Gtid_list may have it which explains a warning from the following command
> +show binlog events in 'master-bin.000003' limit 1,1;
> +Log_name	Pos	Event_type	Server_id	End_log_pos	Info
> +master-bin.000003	#	Gtid_list	1	#	[1-1-1]
> +Already deleted domain in Gtid_list of the earliest log is benign
> +but may cause a warning
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (1);
> +Warnings:
> +Warning	1982	the current gtid binlog state is incompatible to a former one missing gtids from '1-1' domain-server pair which is referred in Gtid list describing earlier binlog state; ignore it if the domain was already explicitly deleted
> +Warning	1982	being deleted gtid domain '1' is not in the current binlog state
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,  0);
> +ERROR HY000: Could not delete gtid domain. Reason: binlog files may contain gtids from being deleted domain '1'; make sure first to purge those files.
> +FLUSH BINARY LOGS;
> +PURGE BINARY LOGS TO 'master-bin.000005';
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,  0);
> +Warnings:
> +Warning	1982	being deleted gtid domain '0' is not in the current binlog state
> +Gtid_list of the current binlog does not contain 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,  0:
> +show binlog events in 'master-bin.000006' limit 1,1;
> +Log_name	Pos	Event_type	Server_id	End_log_pos	Info
> +master-bin.000006	#	Gtid_list	1	#	[]
> +SET @@SESSION.gtid_domain_id=1;;
> +SET @@SESSION.server_id=1;
> +SET @@SESSION.gtid_seq_no=1;
> +INSERT INTO t SET a=1;
> +SET @@SESSION.server_id=2;
> +SET @@SESSION.gtid_seq_no=2;
> +INSERT INTO t SET a=2;
> +SET @@SESSION.gtid_domain_id=11;
> +SET @@SESSION.server_id=11;
> +SET @@SESSION.gtid_seq_no=11;
> +INSERT INTO t SET a=11;
> +SET @gtid_binlog_state_saved=@@GLOBAL.gtid_binlog_state;
> +FLUSH BINARY LOGS;
> +SET @@SESSION.gtid_domain_id=11;
> +SET @@SESSION.server_id=11;
> +SET @@SESSION.gtid_seq_no=1;
> +INSERT INTO t SET a=1;
> +SELECT @gtid_binlog_state_saved "as original state", @@GLOBAL.gtid_binlog_state as "out of order for 11 domain state";
> +as original state	out of order for 11 domain state
> +1-1-1,1-2-2,11-11-11	1-1-1,1-2-2,11-11-1
> +PURGE BINARY LOGS TO 'master-bin.000007';
> +the following command succeeds with warnings
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (1);
> +Warnings:
> +Warning	1982	the current gtid binlog state is incompatible to a former one having gtid '11-11-1' which is lesser than '11-11-11' of Gtid list describing earlier binlog state; possibly the binlog state was affected by smaller sequence number gtid injection (manually or via replication)
> +DROP TABLE t;
> +RESET MASTER;
> diff --git a/mysql-test/suite/binlog/r/binlog_gtid_delete_domain_debug.result b/mysql-test/suite/binlog/r/binlog_gtid_delete_domain_debug.result
> new file mode 100644
> index 000000000000..5193267e2f21
> --- /dev/null
> +++ b/mysql-test/suite/binlog/r/binlog_gtid_delete_domain_debug.result
> @@ -0,0 +1,6 @@
> +SET @@SESSION.debug_dbug='+d,inject_binlog_delete_domain_init_error';
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (99);
> +ERROR HY000: Could not delete gtid domain. Reason: injected error.
> +SHOW WARNINGS;
> +Level	Code	Message
> +Error	1982	Could not delete gtid domain. Reason: injected error.
> diff --git a/mysql-test/suite/binlog/t/binlog_flush_binlogs_delete_domain.test b/mysql-test/suite/binlog/t/binlog_flush_binlogs_delete_domain.test
> new file mode 100644
> index 000000000000..e7b11e598371
> --- /dev/null
> +++ b/mysql-test/suite/binlog/t/binlog_flush_binlogs_delete_domain.test
> @@ -0,0 +1,136 @@
> +# Prove basic properties of
> +#
> +#    FLUSH BINARY LOGS DELETE_DOMAIN_ID = (...)
> +#
> +# The command removes the supplied list of domains from the current
> +# @@global.gtid_binlog_state provided the binlog files do not contain
> +# events from such domains.
> +
> +# The test is not format specific. One format is chosen to run it.
> +--source include/have_binlog_format_mixed.inc
> +
> +# Reset binlog state
> +RESET MASTER;
> +
> +# Empty list is accepted
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = ();
> +--echo and the command execution is effective thence rotates binlog as usual
> +--source include/show_binary_logs.inc
> +
> +--echo Non-existed domain is warned, the command completes without rotation
> +--echo but with a warning
> +--let $binlog_pre_flush=query_get_value(SHOW MASTER STATUS, Position, 1)
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (99);
> +--let $binlog_start=$binlog_pre_flush
> +--source include/show_binary_logs.inc
> +
> +# Log one event in a specified domain and try to delete the domain
> +SET @@SESSION.gtid_domain_id=1;
> +SET @@SESSION.server_id=1;
> +CREATE TABLE t (a int);
> +
> +--error ER_BINLOG_CANT_DELETE_GTID_DOMAIN
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (1);
> +
> +# the same error after log rotation
> +FLUSH BINARY LOGS;
> +--error ER_BINLOG_CANT_DELETE_GTID_DOMAIN
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (1);
> +
> +# the latest binlog does not really contain any events incl ones from 1-domain
> +--let $purge_to_binlog= query_get_value(SHOW MASTER STATUS, File, 1)
> +--eval PURGE BINARY LOGS TO '$purge_to_binlog';
> +# So now it's safe to delete
> +--error 0
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (1);
> +--echo Gtid_list of the current binlog does not contain '1':
> +--let $binlog_file=query_get_value(SHOW MASTER STATUS, File, 1)
> +--source include/show_gtid_list.inc
> +--echo But the previous log's Gtid_list may have it which explains a warning from the following command
> +--let $binlog_file=$purge_to_binlog
> +--source include/show_gtid_list.inc
> +
> +--echo Already deleted domain in Gtid_list of the earliest log is benign
> +--echo but may cause a warning
> +--error 0
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (1);
> +
> +# Few domains delete. The chosen number verifies among others how
> +# expected overrun of the static buffers of underlying dynamic arrays is doing.
> +--let $domain_cnt=17
> +--let $server_in_domain_cnt=3
> +--let $domain_list=
> +--disable_query_log
> +while ($domain_cnt)
> +{
> +  --let servers=$server_in_domain_cnt
> +  --eval SET @@SESSION.gtid_domain_id=$domain_cnt
> +  while ($servers)
> +  {
> +    --eval SET @@SESSION.server_id=10*$domain_cnt + $servers
> +    --eval INSERT INTO t SET a=@@SESSION.server_id
> +
> +    --dec $servers
> +  }
> +  --let $domain_list= $domain_cnt, $domain_list
> +
> +  --dec $domain_cnt
> +}
> +--enable_query_log
> +--let $domain_list= $domain_list 0
> +
> +--error ER_BINLOG_CANT_DELETE_GTID_DOMAIN
> +--eval FLUSH BINARY LOGS DELETE_DOMAIN_ID = ($domain_list)
> +
> +# Now satisfy the safety condtion to purge log files containing $domain list
> +FLUSH BINARY LOGS;
> +--let $purge_to_binlog= query_get_value(SHOW MASTER STATUS, File, 1)
> +--eval PURGE BINARY LOGS TO '$purge_to_binlog'
> +--error 0
> +--eval FLUSH BINARY LOGS DELETE_DOMAIN_ID = ($domain_list)
> +--echo Gtid_list of the current binlog does not contain $domain_list:
> +--let $binlog_file=query_get_value(SHOW MASTER STATUS, File, 1)
> +--source include/show_gtid_list.inc
> +
> +# Show reaction on @@global.gtid_binlog_state not succeeding
> +# earlier state as described by the 1st binlog' Gtid_list.
> +# Now let it be out-order gtid logged to a domain unrelated to deletion.
> +
> +--let $del_d_id=1
> +--eval SET @@SESSION.gtid_domain_id=$del_d_id;
> +SET @@SESSION.server_id=1;
> +SET @@SESSION.gtid_seq_no=1;
> +INSERT INTO t SET a=1;
> +SET @@SESSION.server_id=2;
> +SET @@SESSION.gtid_seq_no=2;
> +INSERT INTO t SET a=2;
> +
> +SET @@SESSION.gtid_domain_id=11;
> +SET @@SESSION.server_id=11;
> +SET @@SESSION.gtid_seq_no=11;
> +INSERT INTO t SET a=11;
> +
> +SET @gtid_binlog_state_saved=@@GLOBAL.gtid_binlog_state;
> +FLUSH BINARY LOGS;
> +
> +# Inject out of order for domain '11' before
> +SET @@SESSION.gtid_domain_id=11;
> +SET @@SESSION.server_id=11;
> +SET @@SESSION.gtid_seq_no=1;
> +INSERT INTO t SET a=1;
> +
> +SELECT @gtid_binlog_state_saved "as original state", @@GLOBAL.gtid_binlog_state as "out of order for 11 domain state";
> +
> +# to delete '1', first to purge logs containing its events
> +--let $purge_to_binlog= query_get_value(SHOW MASTER STATUS, File, 1)
> +--eval PURGE BINARY LOGS TO '$purge_to_binlog'
> +
> +--echo the following command succeeds with warnings
> +--eval FLUSH BINARY LOGS DELETE_DOMAIN_ID = ($del_d_id)
> +
> +#
> +# Cleanup
> +#
> +
> +DROP TABLE t;
> +RESET MASTER;
> diff --git a/mysql-test/suite/binlog/t/binlog_gtid_delete_domain_debug.test b/mysql-test/suite/binlog/t/binlog_gtid_delete_domain_debug.test
> new file mode 100644
> index 000000000000..5de549c45bb0
> --- /dev/null
> +++ b/mysql-test/suite/binlog/t/binlog_gtid_delete_domain_debug.test
> @@ -0,0 +1,11 @@
> +# Check "internal" error branches of
> +#  FLUSH BINARY LOGS DELETE_DOMAIN_ID = (...)
> +# handler.
> +--source include/have_debug.inc
> +--source include/have_binlog_format_mixed.inc
> +
> +SET @@SESSION.debug_dbug='+d,inject_binlog_delete_domain_init_error';
> +--error ER_BINLOG_CANT_DELETE_GTID_DOMAIN
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID = (99);
> +
> +SHOW WARNINGS;
> diff --git a/mysql-test/suite/perfschema/r/start_server_low_digest.result b/mysql-test/suite/perfschema/r/start_server_low_digest.result
> index 8cc92f21964f..6fc41fbb7155 100644
> --- a/mysql-test/suite/perfschema/r/start_server_low_digest.result
> +++ b/mysql-test/suite/perfschema/r/start_server_low_digest.result
> @@ -8,5 +8,5 @@ SELECT 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
>  ####################################
>  SELECT event_name, digest, digest_text, sql_text FROM events_statements_history_long;
>  event_name	digest	digest_text	sql_text
> -statement/sql/truncate	e1c917a43f978456fab15240f89372ca	TRUNCATE TABLE 	truncate table events_statements_history_long
> -statement/sql/select	3f7ca34376814d0e985337bd588b5ffd	SELECT ? + ? + 	SELECT 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
> +statement/sql/truncate	6206ac02a54d832f55015e480e6f2213	TRUNCATE TABLE 	truncate table events_statements_history_long
> +statement/sql/select	4cc1c447d79877c4e8df0423fd0cde9a	SELECT ? + ? + 	SELECT 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
> diff --git a/mysql-test/suite/rpl/r/rpl_gtid_delete_domain.result b/mysql-test/suite/rpl/r/rpl_gtid_delete_domain.result
> new file mode 100644
> index 000000000000..3558a6764d1d
> --- /dev/null
> +++ b/mysql-test/suite/rpl/r/rpl_gtid_delete_domain.result
> @@ -0,0 +1,30 @@
> +include/master-slave.inc
> +[connection master]
> +SET @@SESSION.gtid_domain_id=0;
> +CREATE TABLE t (a INT);
> +call mtr.add_suppression("connecting slave requested to start from.*which is not in the master's binlog");
> +include/stop_slave.inc
> +CHANGE MASTER TO master_use_gtid=slave_pos;
> +SET @@SESSION.gtid_domain_id=11;
> +SET @@SESSION.server_id=111;
> +SET @@SESSION.gtid_seq_no=1;
> +INSERT INTO t SET a=1;
> +SET @save.gtid_slave_pos=@@global.gtid_slave_pos;
> +SET @@global.gtid_slave_pos=concat(@@global.gtid_slave_pos,  ",", 11, "-", 111, "-", 1 + 1);
> +Warnings:
> +Warning	1947	Specified GTID 0-1-1 conflicts with the binary log which contains a more recent GTID 0-2-2. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos.
> +START SLAVE IO_THREAD;
> +include/wait_for_slave_io_error.inc [errno=1236]
> +FLUSH BINARY LOGS;
> +PURGE BINARY LOGS TO 'master-bin.000002';;
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID=(11);
> +include/start_slave.inc
> +INSERT INTO t SET a=1;
> +include/wait_for_slave_io_error.inc [errno=1236]
> +FLUSH BINARY LOGS;
> +PURGE BINARY LOGS TO 'master-bin.000004';;
> +FLUSH BINARY LOGS DELETE_DOMAIN_ID=(11);
> +include/start_slave.inc
> +SET @@SESSION.gtid_domain_id=0;
> +DROP TABLE t;
> +include/rpl_end.inc
> diff --git a/mysql-test/suite/rpl/t/rpl_gtid_delete_domain.test b/mysql-test/suite/rpl/t/rpl_gtid_delete_domain.test
> new file mode 100644
> index 000000000000..c2b7338be2d7
> --- /dev/null
> +++ b/mysql-test/suite/rpl/t/rpl_gtid_delete_domain.test
> @@ -0,0 +1,95 @@
> +# In case master's gtid binlog state is divergent from the slave's gtid_slave_pos
> +# slave may not be able to connect.
> +# For instance when slave is more updated in some of domains, see
> +# MDEV-12012 as example, the master's state may require adjustment.
> +# In a specific case of a divergent domain is "old" that is there
> +# won't be no more event groups from it generated, the states can be
> +# made compatible with the domain wiping away.  After that slave
> +# becomes connectable.
> +#
> +# Notice that the slave applied gtid state is not really required to
> +# be similarly cleaned in order for replication to flow.
> +# However this could lead to an expected error when the master
> +# resumes binlogging of such domain which the test demonstrate.
> +
> +--source include/master-slave.inc
> +
> +--connection master
> +# enforce the default domain_id binlogging explicitly
> +SET @@SESSION.gtid_domain_id=0;
> +CREATE TABLE t (a INT);
> +--sync_slave_with_master
> +
> +--connection slave
> +call mtr.add_suppression("connecting slave requested to start from.*which is not in the master's binlog");
> +
> +--source include/stop_slave.inc
> +CHANGE MASTER TO master_use_gtid=slave_pos;
> +
> +--connection master
> +# create extra gtid domains for binlog state
> +--let $extra_domain_id=11
> +--let $extra_domain_server_id=111
> +--let $extra_gtid_seq_no=1
> +--eval SET @@SESSION.gtid_domain_id=$extra_domain_id
> +--eval SET @@SESSION.server_id=$extra_domain_server_id
> +--eval SET @@SESSION.gtid_seq_no=$extra_gtid_seq_no
> +INSERT INTO t SET a=1;
> +
> +#
> +# Set up the slave replication state as if slave knows more events from the extra
> +# domain.
> +#
> +--connection slave
> +SET @save.gtid_slave_pos=@@global.gtid_slave_pos;
> +--eval  SET @@global.gtid_slave_pos=concat(@@global.gtid_slave_pos,  ",", $extra_domain_id, "-", $extra_domain_server_id, "-", $extra_gtid_seq_no + 1)
> +
> +# unsuccessful attempt to start slave
> +START SLAVE IO_THREAD;
> +--let $slave_io_errno=1236
> +--source include/wait_for_slave_io_error.inc
> +
> +--connection master
> +# adjust the master binlog state
> +FLUSH BINARY LOGS;
> +--let $purge_to_binlog= query_get_value(SHOW MASTER STATUS, File, 1)
> +--eval PURGE BINARY LOGS TO '$purge_to_binlog';
> +# with final removal of the extra domain
> +--eval FLUSH BINARY LOGS DELETE_DOMAIN_ID=($extra_domain_id)
> +
> +--connection slave
> +# start the slave sucessfully
> +--source include/start_slave.inc
> +
> +--connection master
> +# but the following gtid from the *extra* domain will break replication
> +INSERT INTO t SET a=1;
> +
> +# take note of the slave io thread error due to being dismissed
> +# extra domain at connection to master which tried becoming active;
> +# slave is to stop.
> +--connection slave
> +--let $errno=1236
> +--source include/wait_for_slave_io_error.inc
> +
> +# let's heal it by the very same medicine
> +--connection master
> +FLUSH BINARY LOGS;
> +--let $purge_to_binlog= query_get_value(SHOW MASTER STATUS, File, 1)
> +--eval PURGE BINARY LOGS TO '$purge_to_binlog';
> +# with final removal of the extra domain
> +--eval FLUSH BINARY LOGS DELETE_DOMAIN_ID=($extra_domain_id)
> +
> +--connection slave
> +--source include/start_slave.inc
> +
> +#
> +# cleanup
> +#
> +--connection master
> +SET @@SESSION.gtid_domain_id=0;
> +DROP TABLE t;
> +
> +sync_slave_with_master;
> +
> +--source include/rpl_end.inc
> diff --git a/sql/lex.h b/sql/lex.h
> index 85bd20a5f377..6a1cb6653e9d 100644
> --- a/sql/lex.h
> +++ b/sql/lex.h
> @@ -179,6 +179,7 @@ static SYMBOL symbols[] = {
>    { "DELAYED",		SYM(DELAYED_SYM)},
>    { "DELAY_KEY_WRITE",	SYM(DELAY_KEY_WRITE_SYM)},
>    { "DELETE",		SYM(DELETE_SYM)},
> +  { "DELETE_DOMAIN_ID", SYM(DELETE_DOMAIN_ID_SYM)},
>    { "DESC",		SYM(DESC)},
>    { "DESCRIBE",		SYM(DESCRIBE)},
>    { "DES_KEY_FILE",	SYM(DES_KEY_FILE)},
> diff --git a/sql/log.cc b/sql/log.cc
> index a9f486d88c15..2fcb6f6dbf62 100644
> --- a/sql/log.cc
> +++ b/sql/log.cc
> @@ -6622,6 +6622,119 @@ void MYSQL_BIN_LOG::checkpoint_and_purge(ulong binlog_id)
>    purge();
>  }
>  
> +
> +/**
> +  Searches for the first (oldest) binlog file name in in the binlog index.
> +
> +  @param[in,out]  buf_arg  pointer to a buffer to hold found
> +                           the first binary log file name
> +  @return         NULL     on success, otherwise error message
> +*/
> +static const char* get_first_binlog(char* buf_arg)
> +{
> +  IO_CACHE *index_file;
> +  size_t length;
> +  char fname[FN_REFLEN];
> +  const char* errmsg= NULL;
> +
> +  DBUG_ENTER("get_first_binlog");
> +
> +  DBUG_ASSERT(mysql_bin_log.is_open());
> +
> +  mysql_bin_log.lock_index();
> +
> +  index_file=mysql_bin_log.get_index_file();
> +  if (reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 0))
> +  {
> +    errmsg= "failed to create a cache on binlog index";
> +    goto end;
> +  }
> +  /* The file ends with EOF or empty line */
> +  if ((length=my_b_gets(index_file, fname, sizeof(fname))) <= 1)
> +  {
> +    errmsg= "empty binlog index";
> +    goto end;
> +  }
> +  else
> +  {
> +    fname[length-1]= 0;                         // Remove end \n
> +  }
> +  if (normalize_binlog_name(buf_arg, fname, false))
> +  {
> +    errmsg= "cound not normalize the first file name in the binlog index";
> +    goto end;
> +  }
> +end:
> +  mysql_bin_log.unlock_index();
> +
> +  DBUG_RETURN(errmsg);
> +}
> +
> +/**
> +  Check weather the gtid binlog state can safely remove gtid
> +  domains passed as the argument. A safety condition is satisfied when
> +  there are no events from the being deleted domains in the currently existing
> +  binlog files. Upon successful check the supplied domains are removed
> +  from @@gtid_binlog_state. The caller is supposed to rotate binlog so that
> +  the active latest file won't have the deleted domains in its Gtid_list header.
> +
> +  @param  domain_drop_lex  gtid domain id sequence from lex.
> +                           Passed as a pointer to dynamic array must be not empty
> +                           unless pointer value NULL.
> +  @retval zero             on success
> +  @retval > 0              ineffective call none from the *non* empty
> +                           gtid domain sequence is deleted
> +  @retval < 0              on error
> +*/
> +static int do_delete_gtid_domain(DYNAMIC_ARRAY *domain_drop_lex)
> +{
> +  int rc= 0;
> +  Gtid_list_log_event *glev= NULL;
> +  char buf[FN_REFLEN];
> +  File file;
> +  IO_CACHE cache;
> +  const char* errmsg= NULL;
> +  char errbuf[MYSQL_ERRMSG_SIZE]= {0};
> +
> +  if (!domain_drop_lex)
> +    return 0; // still "effective" having empty domain sequence to delete
> +
> +  DBUG_ASSERT(domain_drop_lex->elements > 0);
> +  mysql_mutex_assert_owner(mysql_bin_log.get_log_lock());
> +
> +  if ((errmsg= get_first_binlog(buf)) != NULL)
> +    goto end;
> +  bzero((char*) &cache, sizeof(cache));
> +  if ((file= open_binlog(&cache, buf, &errmsg)) == (File) -1)
> +    goto end;
> +  errmsg= get_gtid_list_event(&cache, &glev);
> +  end_io_cache(&cache);
> +  mysql_file_close(file, MYF(MY_WME));
> +
> +  DBUG_EXECUTE_IF("inject_binlog_delete_domain_init_error",
> +                  errmsg= "injected error";);
> +  if (errmsg)
> +    goto end;
> +  errmsg= rpl_global_gtid_binlog_state.drop_domain(domain_drop_lex, glev, errbuf);
> +
> +end:
> +  if (errmsg)
> +  {
> +    if (strlen(errmsg) > 0)
> +    {
> +      my_error(ER_BINLOG_CANT_DELETE_GTID_DOMAIN, MYF(0), errmsg);
> +      rc= -1;
> +    }
> +    else
> +    {
> +      rc= 1;
> +    }
> +  }
> +  delete glev;
> +
> +  return rc;
> +}
> +
>  /**
>    The method is a shortcut of @c rotate() and @c purge().
>    LOCK_log is acquired prior to rotate and is released after it.
> @@ -6631,9 +6744,10 @@ void MYSQL_BIN_LOG::checkpoint_and_purge(ulong binlog_id)
>    @retval
>      nonzero - error in rotating routine.
>  */
> -int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate)
> +int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate,
> +                                    DYNAMIC_ARRAY *domain_drop_lex)
>  {
> -  int error= 0;
> +  int err_gtid=0, error= 0;
>    ulong prev_binlog_id;
>    DBUG_ENTER("MYSQL_BIN_LOG::rotate_and_purge");
>    bool check_purge= false;
> @@ -6641,7 +6755,14 @@ int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate)
>    //todo: fix the macro def and restore safe_mutex_assert_not_owner(&LOCK_log);
>    mysql_mutex_lock(&LOCK_log);
>    prev_binlog_id= current_binlog_id;
> -  if ((error= rotate(force_rotate, &check_purge)))
> +
> +  if ((err_gtid= do_delete_gtid_domain(domain_drop_lex)))
> +  {
> +    // inffective attempt to delete merely skips rotate and purge
> +    if (err_gtid < 0)
> +      error= 1; // otherwise error is propagated the user
> +  }
> +  else if ((error= rotate(force_rotate, &check_purge)))
>      check_purge= false;
>    /*
>      NOTE: Run purge_logs wo/ holding LOCK_log because it does not need
> @@ -10219,6 +10340,73 @@ TC_LOG_BINLOG::set_status_variables(THD *thd)
>    }
>  }
>  
> +
> +/*
> +  Find the Gtid_list_log_event at the start of a binlog.
> +
> +  NULL for ok, non-NULL error message for error.
> +
> +  If ok, then the event is returned in *out_gtid_list. This can be NULL if we
> +  get back to binlogs written by old server version without GTID support. If
> +  so, it means we have reached the point to start from, as no GTID events can
> +  exist in earlier binlogs.
> +*/
> +const char *
> +get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list)
> +{
> +  Format_description_log_event init_fdle(BINLOG_VERSION);
> +  Format_description_log_event *fdle;
> +  Log_event *ev;
> +  const char *errormsg = NULL;
> +
> +  *out_gtid_list= NULL;
> +
> +  if (!(ev= Log_event::read_log_event(cache, 0, &init_fdle,
> +                                      opt_master_verify_checksum)) ||
> +      ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
> +  {
> +    if (ev)
> +      delete ev;
> +    return "Could not read format description log event while looking for "
> +      "GTID position in binlog";
> +  }
> +
> +  fdle= static_cast<Format_description_log_event *>(ev);
> +
> +  for (;;)
> +  {
> +    Log_event_type typ;
> +
> +    ev= Log_event::read_log_event(cache, 0, fdle, opt_master_verify_checksum);
> +    if (!ev)
> +    {
> +      errormsg= "Could not read GTID list event while looking for GTID "
> +        "position in binlog";
> +      break;
> +    }
> +    typ= ev->get_type_code();
> +    if (typ == GTID_LIST_EVENT)
> +      break;                                    /* Done, found it */
> +    if (typ == START_ENCRYPTION_EVENT)
> +    {
> +      if (fdle->start_decryption((Start_encryption_log_event*) ev))
> +        errormsg= "Could not set up decryption for binlog.";
> +    }
> +    delete ev;
> +    if (typ == ROTATE_EVENT || typ == STOP_EVENT ||
> +        typ == FORMAT_DESCRIPTION_EVENT || typ == START_ENCRYPTION_EVENT)
> +      continue;                                 /* Continue looking */
> +
> +    /* We did not find any Gtid_list_log_event, must be old binlog. */
> +    ev= NULL;
> +    break;
> +  }
> +
> +  delete fdle;
> +  *out_gtid_list= static_cast<Gtid_list_log_event *>(ev);
> +  return errormsg;
> +}
> +
>  struct st_mysql_storage_engine binlog_storage_engine=
>  { MYSQL_HANDLERTON_INTERFACE_VERSION };
>  
> diff --git a/sql/log.h b/sql/log.h
> index bf076fae31db..3026ca11e310 100644
> --- a/sql/log.h
> +++ b/sql/log.h
> @@ -755,7 +755,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
>    int update_log_index(LOG_INFO* linfo, bool need_update_threads);
>    int rotate(bool force_rotate, bool* check_purge);
>    void checkpoint_and_purge(ulong binlog_id);
> -  int rotate_and_purge(bool force_rotate);
> +  int rotate_and_purge(bool force_rotate, DYNAMIC_ARRAY* drop_gtid_domain= NULL);
>    /**
>       Flush binlog cache and synchronize to disk.
>  
> @@ -1165,4 +1165,9 @@ static inline TC_LOG *get_tc_log_implementation()
>    return &tc_log_mmap;
>  }
>  
> +
> +class Gtid_list_log_event;
> +const char *
> +get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list);
> +
>  #endif /* LOG_H */
> diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
> index 51df8f1a789b..c7761f008bf1 100644
> --- a/sql/rpl_gtid.cc
> +++ b/sql/rpl_gtid.cc
> @@ -26,7 +26,7 @@
>  #include "key.h"
>  #include "rpl_gtid.h"
>  #include "rpl_rli.h"
> -
> +#include "log_event.h"
>  
>  const LEX_STRING rpl_gtid_slave_state_table_name=
>    { C_STRING_WITH_LEN("gtid_slave_pos") };
> @@ -1728,6 +1728,154 @@ rpl_binlog_state::append_state(String *str)
>    return res;
>  }
>  
> +/**
> +  Remove domains supplied by the first argument from binlog state.
> +  Removal is done for any domain whose last gtids (from all its servers) match
> +  ones in Gtid list event of the 2nd argument.
> +
> +  @param  ids               gtid domain id sequence, may contain dups
> +  @param  glev              pointer to Gtid list event describing
> +                            the match condition
> +  @param  errbuf [out]      pointer to possible error message array
> +
> +  @retval NULL              as success when at least one domain is removed
> +  @retval ""                empty string to indicate ineffective call
> +                            when no domains removed
> +  @retval NOT EMPTY string  otherwise an error message
> +*/
> +const char*
> +rpl_binlog_state::drop_domain(DYNAMIC_ARRAY *ids,
> +                              Gtid_list_log_event *glev,
> +                              char* errbuf)
> +{
> +  DYNAMIC_ARRAY domain_unique; // sequece (unsorted) of unique element*:s
> +  rpl_binlog_state::element* domain_unique_buffer[16];
> +  ulong k;
> +  const char* errmsg= NULL;
> +
> +  DBUG_ENTER("rpl_binlog_state::drop_domain");
> +
> +  my_init_dynamic_array2(&domain_unique,
> +                         sizeof(element*), domain_unique_buffer,
> +                         sizeof(domain_unique_buffer) / sizeof(element*), 4, 0);
> +
> +  mysql_mutex_lock(&LOCK_binlog_state);
> +
> +  /*
> +    Gtid list is supposed to come from a binlog's Gtid_list event and
> +    therefore should be a subset of the current binlog state. That is
> +    for every domain in the list the binlog state contains a gtid with
> +    sequence number greater than that of the list.
> +    Exceptions of this inclusion rule are:
> +      A. the list may still refer to gtids whose domains domains were
> +         already deleted but the files remain (unpurged yet) *only*
> +         referring to the domains through their Gtid lists.
> +      B. the user injected out of order groups
> +      C. manually build list of binlog files to violate the inclusion
> +         constraint.
> +
> +    It is diagnozed and merely *warned* assuming this might caused by
> +    one of thew two reasons.
> +  */
> +  for (ulong l= 0; l < glev->count; l++)
> +  {
> +    rpl_gtid* rb_state_gtid= find_nolock(glev->list[l].domain_id,
> +                                         glev->list[l].server_id);
> +    if (!rb_state_gtid)
> +      sprintf(errbuf,
> +              "missing gtids from '%u-%u' domain-server pair "
> +              "which is referred in Gtid list describing earlier binlog state; "
> +              "ignore it if the domain was already explicitly deleted",
> +              glev->list[l].domain_id, glev->list[l].server_id);
> +    else if (rb_state_gtid->seq_no < glev->list[l].seq_no)
> +      sprintf(errbuf,
> +              "having gtid '%u-%u-%llu' which is lesser than "
> +              "'%u-%u-%llu' of Gtid list describing earlier binlog state; "
> +              "possibly the binlog state was affected by smaller sequence number "
> +              "gtid injection (manually or via replication)",
> +              rb_state_gtid->domain_id, rb_state_gtid->server_id,
> +              rb_state_gtid->seq_no, glev->list[l].domain_id,
> +              glev->list[l].server_id, glev->list[l].seq_no);
> +    if (strlen(errbuf))
> +      push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
> +                          ER_BINLOG_CANT_DELETE_GTID_DOMAIN,
> +                          "the current gtid binlog state is incompatible to "
> +                          "a former one %s", errbuf);
> +    errbuf[0]= 0; // having been used up so reset
> +  }
> +
> +  /*
> +    For each domain_id from ids
> +      when no such domain in binlog state
> +        warning && continue
> +      For each domain.server's last gtid
> +        when not locate the last gtid in glev.list
> +          error binlog state can't change
> +        otherwise continue
> +  */
> +  for (ulong i= 0; i < ids->elements; i++)
> +  {
> +    rpl_binlog_state::element *elem= NULL;
> +    ulong *ptr_domain_id;
> +    bool not_match;
> +
> +    ptr_domain_id= (ulong*) dynamic_array_ptr(ids, i);
> +    elem= (rpl_binlog_state::element *)
> +      my_hash_search(&hash, (const uchar *) ptr_domain_id, 0);
> +    if (!elem)
> +    {
> +      push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
> +                          ER_BINLOG_CANT_DELETE_GTID_DOMAIN,
> +                          "being deleted gtid domain '%lu' is not in "
> +                          "the current binlog state", *ptr_domain_id);
> +      continue;
> +    }
> +
> +    for (not_match= true, k= 0; k < elem->hash.records; k++)
> +    {
> +      rpl_gtid *d_gtid= (rpl_gtid *)my_hash_element(&elem->hash, k);
> +      for (ulong l= 0; l < glev->count && not_match; l++)
> +        not_match= !(*d_gtid == glev->list[l]);
> +    }
> +
> +    if (not_match)
> +    {
> +      sprintf(errbuf, "binlog files may contain gtids from being deleted domain"
> +              " '%lu'; make sure first to purge those files", *ptr_domain_id);
> +      errmsg= errbuf;
> +      goto end;
> +    }
> +    // compose a sequence of unique pointers to domain object
> +    for (k= 0; k < domain_unique.elements; k++)
> +    {
> +      if ((rpl_binlog_state::element*) dynamic_array_ptr(&domain_unique, k)
> +          == elem)
> +        break; // domain_id's elem has been already in
> +    }
> +    if (k == domain_unique.elements) // proven not to have duplicates
> +      insert_dynamic(&domain_unique, (uchar*) &elem);
> +  }
> +
> +  // Domain removal from binlog state
> +  for (k= 0; k < domain_unique.elements; k++)
> +  {
> +    rpl_binlog_state::element *elem= *(rpl_binlog_state::element**)
> +      dynamic_array_ptr(&domain_unique, k);
> +    my_hash_free(&elem->hash);
> +    my_hash_delete(&hash, (uchar*) elem);
> +  }
> +
> +  DBUG_ASSERT(strlen(errbuf) == 0);
> +
> +  if (domain_unique.elements == 0)
> +    errmsg= "";
> +
> +end:
> +  mysql_mutex_unlock(&LOCK_binlog_state);
> +  delete_dynamic(&domain_unique);
> +
> +  DBUG_RETURN(errmsg);
> +}
>  
>  slave_connection_state::slave_connection_state()
>  {
> diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h
> index ece6effbef6f..79d566bddbfd 100644
> --- a/sql/rpl_gtid.h
> +++ b/sql/rpl_gtid.h
> @@ -34,6 +34,13 @@ struct rpl_gtid
>    uint64 seq_no;
>  };
>  
> +inline bool operator==(const rpl_gtid& lhs, const rpl_gtid& rhs)
> +{
> +  return
> +    lhs.domain_id == rhs.domain_id &&
> +    lhs.server_id == rhs.server_id &&
> +    lhs.seq_no    == rhs.seq_no;
> +};
>  
>  enum enum_gtid_skip_type {
>    GTID_SKIP_NOT, GTID_SKIP_STANDALONE, GTID_SKIP_TRANSACTION
> @@ -93,6 +100,7 @@ struct gtid_waiting {
>  
>  class Relay_log_info;
>  struct rpl_group_info;
> +class Gtid_list_log_event;
>  
>  /*
>    Replication slave state.
> @@ -256,6 +264,7 @@ struct rpl_binlog_state
>    rpl_gtid *find_nolock(uint32 domain_id, uint32 server_id);
>    rpl_gtid *find(uint32 domain_id, uint32 server_id);
>    rpl_gtid *find_most_recent(uint32 domain_id);
> +  const char* drop_domain(DYNAMIC_ARRAY *ids, Gtid_list_log_event *glev, char*);
>  };
>  
>  
> diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
> index d335b0b420b8..15ec98045ea2 100644
> --- a/sql/share/errmsg-utf8.txt
> +++ b/sql/share/errmsg-utf8.txt
> @@ -7142,3 +7142,5 @@ ER_NO_EIS_FOR_FIELD
>  ER_WARN_AGGFUNC_DEPENDENCE
>          eng "Aggregate function '%-.192s)' of SELECT #%d belongs to SELECT #%d"
>          ukr "Агрегатна функція '%-.192s)' з SELECTу #%d належить до SELECTу #%d"
> +ER_BINLOG_CANT_DELETE_GTID_DOMAIN
> +	eng "Could not delete gtid domain. Reason: %s."
> diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
> index b3a30c69a039..018fa9f3af1d 100644
> --- a/sql/sql_lex.cc
> +++ b/sql/sql_lex.cc
> @@ -800,6 +800,7 @@ void lex_end_stage2(LEX *lex)
>  
>    /* Reset LEX_MASTER_INFO */
>    lex->mi.reset(lex->sql_command == SQLCOM_CHANGE_MASTER);
> +  delete_dynamic(&lex->delete_gtid_domain);
>  
>    DBUG_VOID_RETURN;
>  }
> @@ -2878,6 +2879,10 @@ LEX::LEX()
>                        INITIAL_LEX_PLUGIN_LIST_SIZE, 0);
>    reset_query_tables_list(TRUE);
>    mi.init();
> +  init_dynamic_array2(&delete_gtid_domain, sizeof(ulong*),
> +                      gtid_domain_static_buffer,
> +                      initial_gtid_domain_buffer_size,
> +                      initial_gtid_domain_buffer_size, 0);
>  }
>  
>  
> diff --git a/sql/sql_lex.h b/sql/sql_lex.h
> index 240eb2373ebd..718a33f68e47 100644
> --- a/sql/sql_lex.h
> +++ b/sql/sql_lex.h
> @@ -2725,6 +2725,13 @@ struct LEX: public Query_tables_list
>    */
>    Item *limit_rows_examined;
>    ulonglong limit_rows_examined_cnt;
> +  /**
> +    Holds a set of domain_ids for deletion at FLUSH..DELETE_DOMAIN_ID
> +  */
> +  DYNAMIC_ARRAY delete_gtid_domain;
> +  static const ulong initial_gtid_domain_buffer_size= 16;
> +  ulong gtid_domain_static_buffer[initial_gtid_domain_buffer_size];
> +
>    inline void set_limit_rows_examined()
>    {
>      if (limit_rows_examined)
> diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc
> index d68ce96dc85a..0488bf7261bc 100644
> --- a/sql/sql_reload.cc
> +++ b/sql/sql_reload.cc
> @@ -153,7 +153,10 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
>      tmp_write_to_binlog= 0;
>      if (mysql_bin_log.is_open())
>      {
> -      if (mysql_bin_log.rotate_and_purge(true))
> +      DYNAMIC_ARRAY *drop_gtid_domain=
> +        thd->lex->delete_gtid_domain.elements > 0 ?
> +        &thd->lex->delete_gtid_domain : NULL;
> +      if (mysql_bin_log.rotate_and_purge(true, drop_gtid_domain))
>          *write_to_binlog= -1;
>  
>        if (WSREP_ON)
> diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
> index 764047e47206..b5cca334891b 100644
> --- a/sql/sql_repl.cc
> +++ b/sql/sql_repl.cc
> @@ -30,7 +30,7 @@
>  #include <my_dir.h>
>  #include "rpl_handler.h"
>  #include "debug_sync.h"
> -
> +#include "log.h"                                // get_gtid_list_event
>  
>  enum enum_gtid_until_state {
>    GTID_UNTIL_NOT_DONE,
> @@ -875,72 +875,6 @@ get_binlog_list(MEM_ROOT *memroot)
>    DBUG_RETURN(current_list);
>  }
>  
> -/*
> -  Find the Gtid_list_log_event at the start of a binlog.
> -
> -  NULL for ok, non-NULL error message for error.
> -
> -  If ok, then the event is returned in *out_gtid_list. This can be NULL if we
> -  get back to binlogs written by old server version without GTID support. If
> -  so, it means we have reached the point to start from, as no GTID events can
> -  exist in earlier binlogs.
> -*/
> -static const char *
> -get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list)
> -{
> -  Format_description_log_event init_fdle(BINLOG_VERSION);
> -  Format_description_log_event *fdle;
> -  Log_event *ev;
> -  const char *errormsg = NULL;
> -
> -  *out_gtid_list= NULL;
> -
> -  if (!(ev= Log_event::read_log_event(cache, 0, &init_fdle,
> -                                      opt_master_verify_checksum)) ||
> -      ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
> -  {
> -    if (ev)
> -      delete ev;
> -    return "Could not read format description log event while looking for "
> -      "GTID position in binlog";
> -  }
> -
> -  fdle= static_cast<Format_description_log_event *>(ev);
> -
> -  for (;;)
> -  {
> -    Log_event_type typ;
> -
> -    ev= Log_event::read_log_event(cache, 0, fdle, opt_master_verify_checksum);
> -    if (!ev)
> -    {
> -      errormsg= "Could not read GTID list event while looking for GTID "
> -        "position in binlog";
> -      break;
> -    }
> -    typ= ev->get_type_code();
> -    if (typ == GTID_LIST_EVENT)
> -      break;                                    /* Done, found it */
> -    if (typ == START_ENCRYPTION_EVENT)
> -    {
> -      if (fdle->start_decryption((Start_encryption_log_event*) ev))
> -        errormsg= "Could not set up decryption for binlog.";
> -    }
> -    delete ev;
> -    if (typ == ROTATE_EVENT || typ == STOP_EVENT ||
> -        typ == FORMAT_DESCRIPTION_EVENT || typ == START_ENCRYPTION_EVENT)
> -      continue;                                 /* Continue looking */
> -
> -    /* We did not find any Gtid_list_log_event, must be old binlog. */
> -    ev= NULL;
> -    break;
> -  }
> -
> -  delete fdle;
> -  *out_gtid_list= static_cast<Gtid_list_log_event *>(ev);
> -  return errormsg;
> -}
> -
>  
>  /*
>    Check if every GTID requested by the slave is contained in this (or a later)
> diff --git a/sql/sql_repl.h b/sql/sql_repl.h
> index e2000bbca73a..37acff3141f2 100644
> --- a/sql/sql_repl.h
> +++ b/sql/sql_repl.h
> @@ -82,7 +82,6 @@ int rpl_append_gtid_state(String *dest, bool use_binlog);
>  int rpl_load_gtid_state(slave_connection_state *state, bool use_binlog);
>  bool rpl_gtid_pos_check(THD *thd, char *str, size_t len);
>  bool rpl_gtid_pos_update(THD *thd, char *str, size_t len);
> -
>  #else
>  
>  struct LOAD_FILE_IO_CACHE : public IO_CACHE { };
> diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
> index b4c0c4d45c33..0aed06750a8c 100644
> --- a/sql/sql_yacc.yy
> +++ b/sql/sql_yacc.yy
> @@ -1182,6 +1182,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
>  %token  DELAYED_SYM
>  %token  DELAY_KEY_WRITE_SYM
>  %token  DELETE_SYM                    /* SQL-2003-R */
> +%token  DELETE_DOMAIN_ID_SYM
>  %token  DESC                          /* SQL-2003-N */
>  %token  DESCRIBE                      /* SQL-2003-R */
>  %token  DES_KEY_FILE
> @@ -1951,6 +1952,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
>          parse_vcol_expr vcol_opt_specifier vcol_opt_attribute
>          vcol_opt_attribute_list vcol_attribute
>          explainable_command
> +        opt_delete_gtid_domain
>  END_OF_INPUT
>  
>  %type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
> @@ -12768,7 +12770,7 @@ flush_option:
>            { Lex->type|= REFRESH_GENERAL_LOG; }
>          | SLOW LOGS_SYM
>            { Lex->type|= REFRESH_SLOW_LOG; }
> -        | BINARY LOGS_SYM
> +        | BINARY LOGS_SYM opt_delete_gtid_domain
>            { Lex->type|= REFRESH_BINARY_LOG; }
>          | RELAY LOGS_SYM optional_connection_name
>            {
> @@ -12825,6 +12827,24 @@ opt_table_list:
>          | table_list {}
>          ;
>  
> +opt_delete_gtid_domain:
> +          /* empty */ {}
> +        | DELETE_DOMAIN_ID_SYM '=' '(' delete_domain_id_list ')'
> +          {}
> +        ;
> +delete_domain_id_list:
> +          /* Empty */
> +        | delete_domain_id
> +        | delete_domain_id_list ',' delete_domain_id
> +        ;
> +
> +delete_domain_id:
> +          ulong_num
> +          {
> +            insert_dynamic(&Lex->delete_gtid_domain, (uchar*) &($1));
> +          }
> +        ;
> +
>  optional_flush_tables_arguments:
>            /* empty */        {$$= 0;}
>          | AND_SYM DISABLE_SYM CHECKPOINT_SYM {$$= REFRESH_CHECKPOINT; } 


Follow ups

References