Skip to content

Commit

Permalink
MDEV-11675 Lag Free Alter On Slave
Browse files Browse the repository at this point in the history
This commit implements two phase binloggable ALTER.
When a new

      @@session.binlog_alter_two_phase = YES

ALTER query gets logged in two parts, the START ALTER and the COMMIT
or ROLLBACK ALTER. START Alter is written in binlog as soon as
necessary locks have been acquired for the table. The timing is
such that any concurrent DML:s that update the same table are either
committed, thus logged into binary log having done work on the old
version of the table, or will be queued for execution on its new
version.

The "COMPLETE" COMMIT or ROLLBACK ALTER are written at the very point
of a normal "single-piece" ALTER that is after the most of
the query work is done. When its result is positive COMMIT ALTER is
written, otherwise ROLLBACK ALTER is written with specific error
happened after START ALTER phase.
Replication of two-phase binloggable ALTER is
cross-version safe. Specifically the OLD slave merely does not
recognized the start alter part, still being able to process and
memorize its gtid.

Two phase logged ALTER is read from binlog by mysqlbinlog to produce
BINLOG 'string', where 'string' contains base64 encoded
Query_log_event containing either the start part of ALTER, or a
completion part. The Query details can be displayed with `-v` flag,
similarly to ROW format events.  Notice, mysqlbinlog output containing
parts of two-phase binloggable ALTER is processable correctly only by
binlog_alter_two_phase server.

@@log_warnings > 2 can reveal details of binlogging and slave side
processing of the ALTER parts.

The current commit also carries fixes to the following list of
reported bugs:
MDEV-27511, MDEV-27471, MDEV-27349, MDEV-27628, MDEV-27528.

Thanks to all people involved into early discussion of the feature
including Kristian Nielsen, those who helped to design, implement and
test: Sergei Golubchik, Andrei Elkin who took the burden of the
implemenation completion, Sujatha Sivakumar, Brandon
Nesterenko, Alice Sherepa, Ramesh Sivaraman, Jan Lindstrom.
  • Loading branch information
SachinSetiya authored and andrelkin committed Jan 27, 2022
1 parent c64e507 commit 0c5d134
Show file tree
Hide file tree
Showing 87 changed files with 8,451 additions and 150 deletions.
1 change: 0 additions & 1 deletion client/mysqlbinlog.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3536,7 +3536,6 @@ int main(int argc, char** argv)
"/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n"
"/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n"
"/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n");

fprintf(result_file, "/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;\n");

if (is_gtid_filtering_enabled())
Expand Down
24 changes: 23 additions & 1 deletion mysql-test/include/show_binlog_events2.inc
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
# ==== Purpose ====
#
# A lighter version of show_binlog_event.inc, with the same purpose
# to execute SHOW BINLOG EVENTS and mask non-deterministic output.
#
#
# Parameters:
#
# $binlog_file
# Filename for the 'IN' clause of SHOW BINLOG EVENTS. If none
# given, no argument is given to SHOW BINLOG EVENTS, meaning that
# it uses the first binlog. If you set this to "LAST", it prints
# the last binlog (according to SHOW MASTER STATUS).
#
# $binlog_start
# Position for the 'FROM' clause of SHOW BINLOG EVENTS. If none
# given, starts right after the Binlog_checkpoint_log_even.
#
# $regexp_replace
# A user's custom addon to standard preexisting list.
#

if ($binlog_start)
{
--let $_binlog_start=$binlog_start
Expand All @@ -13,5 +35,5 @@ if ($binlog_file)
--let $_from_binlog_start=from $_binlog_start
--replace_result "$_from_binlog_start" "from <binlog_start>" $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--replace_column 2 # 5 #
--replace_regex /\/\* xid=.* \*\//\/* XID *\// /table_id: [0-9]+/table_id: #/ /file_id=[0-9]+/file_id=#/ /GTID [0-9]+-[0-9]+-[0-9]+/GTID #-#-#/
--replace_regex /\/\* xid=.* \*\//\/* XID *\// /table_id: [0-9]+/table_id: #/ /file_id=[0-9]+/file_id=#/ /GTID [0-9]+-[0-9]+-[0-9]+/GTID #-#-#/ $replace_regexp
--eval show binlog events $_in_binlog_file from $_binlog_start
2 changes: 2 additions & 0 deletions mysql-test/include/show_events.inc
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ let $script=
s{block_len=[0-9]+}{block_len=#};
s{Server ver:.*DOLLAR}{SERVER_VERSION, BINLOG_VERSION};
s{GTID [0-9]+-[0-9]+-[0-9]+}{GTID #-#-#};
s{COMMIT ALTER id=[0-9]+}{COMMIT ALTER id=#};
s{ROLLBACK ALTER id=[0-9]+}{ROLLBACK ALTER id=#};
s{\[([0-9]-[0-9]-[0-9]+,?)+\]}{[#-#-#]};
s{cid=[0-9]+}{cid=#};
s{SQL_LOAD-[a-z,0-9,-]*.[a-z]*}{SQL_LOAD-<SERVER UUID>-<MASTER server-id>-<file-id>.<extension>};
Expand Down
5 changes: 5 additions & 0 deletions mysql-test/main/mysqld--help.result
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ The following specify which files/extra groups are read (specified before remain
full' errors. No longer needed, as the server now handles
this automatically.
--bind-address=name IP address to bind to.
--binlog-alter-two-phase=name
When set, split ALTER at binary logging into 2
statements: START ALTER and COMMIT/ROLLBACK ALTER. One
of: No, Yes
--binlog-annotate-row-events
Tells the master to annotate RBR events with the
statement that caused these events
Expand Down Expand Up @@ -1476,6 +1480,7 @@ automatic-sp-privileges TRUE
back-log 80
big-tables FALSE
bind-address (No default value)
binlog-alter-two-phase No
binlog-annotate-row-events TRUE
binlog-cache-size 32768
binlog-checksum CRC32
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
set global binlog_alter_two_phase=YES;
set binlog_alter_two_phase=YES;
RESET MASTER;
create table myt (a int) engine=InnoDB;
alter table myt add column (b int);
FLUSH LOGS;
# Exec MYSQL_BINLOG --base64-output=decode-rows -v MYSQLD_DATADIR/BINLOG_FILENAME > MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql
Verbose statements from : master-bin.000001
select replace(txt,'\r', '') as stmt from raw_binlog_rows where txt like '###%';
stmt
### alter table myt add column (b int)
### alter table myt add column (b int)
drop table raw_binlog_rows;
drop table myt;
set global binlog_alter_two_phase=No;
42 changes: 42 additions & 0 deletions mysql-test/suite/binlog/r/start_alter_mysqlbinlog_replay.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#
# Test verifies replay of binary logs which contain
# SA/RA/CA works fine.
# Generate a binary log with alter events and use mysqlbinlog tool to
# generate a sql file for replay. Source it on an clean master and
# verify the correctness. Use the latest binlog and repeat the same
# process mentioned above and observe replay works fine.
#
set global binlog_alter_two_phase=YES;
set binlog_alter_two_phase=YES;
create table t1 (f1 int primary key) engine=InnoDB;
create table t2 (f1 int primary key, constraint c1 foreign key (f1) references t1(f1)) engine=innodb;
alter table t2 add constraint c1 foreign key (f1) references t1(f1);
ERROR HY000: Can't create table `test`.`t2` (errno: 121 "Duplicate key on write or update")
drop table t2, t1;
select @@gtid_binlog_state;
@@gtid_binlog_state
0-1-5
FLUSH LOGS;
# reset the binlog
RESET MASTER;
# execute the binlog
SELECT @@gtid_binlog_state;
@@gtid_binlog_state
0-1-5
FLUSH LOGS;
# Replay 1: One more time to simulate S->S case
RESET MASTER;
# execute the binlog
SELECT @@gtid_binlog_state;
@@gtid_binlog_state
0-1-5
FLUSH LOGS;
# Replay 2: One more time to simulate S->S case
RESET MASTER;
# execute the binlog
SELECT @@gtid_binlog_state;
@@gtid_binlog_state
0-1-5
# clean up
RESET MASTER;
set global binlog_alter_two_phase=No;;
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#
# Purpose:
#
# This test ensures that mysqlbinlog prints a comment when two-phase alter
# is enabled which shows the original alter query issued.
#
#
# References:
# MENT-662: Finalize MDEV-11675 "Lag Free Alter On Slave"
#

# Just row format for faster testing
--source include/have_binlog_format_row.inc
--source include/have_innodb.inc

#---
# Setup
#---
--let $binlog_alter_two_phase= `select @@binlog_alter_two_phase`
set global binlog_alter_two_phase=YES;
set binlog_alter_two_phase=YES;

RESET MASTER;
create table myt (a int) engine=InnoDB;


#---
# Issue ALTER
#---
alter table myt add column (b int);


#---
# Check binlog output
#---
FLUSH LOGS;

--disable_query_log
--let $MYSQLD_DATADIR= `select @@datadir`
--let $BINLOG_FILENAME= query_get_value(SHOW BINARY LOGS, Log_name, 1)

--echo # Exec MYSQL_BINLOG --base64-output=decode-rows -v MYSQLD_DATADIR/BINLOG_FILENAME > MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql
--exec $MYSQL_BINLOG --base64-output=decode-rows -v $MYSQLD_DATADIR/$BINLOG_FILENAME > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql

create table raw_binlog_rows (txt varchar(1000));
--eval load data local infile '$MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql' into table raw_binlog_rows columns terminated by '\n'
--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_verbose.sql
--enable_query_log
--echo Verbose statements from : $BINLOG_FILENAME
# Output --verbose lines, with extra Windows CR's trimmed
select replace(txt,'\r', '') as stmt from raw_binlog_rows where txt like '###%';

#---
# Cleanup
#---
drop table raw_binlog_rows;
drop table myt;
--eval set global binlog_alter_two_phase=$binlog_alter_two_phase
61 changes: 61 additions & 0 deletions mysql-test/suite/binlog/t/start_alter_mysqlbinlog_replay.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#
# MENT-662: Lag Free Alter On Slave
#

--echo #
--echo # Test verifies replay of binary logs which contain
--echo # SA/RA/CA works fine.
--echo # Generate a binary log with alter events and use mysqlbinlog tool to
--echo # generate a sql file for replay. Source it on an clean master and
--echo # verify the correctness. Use the latest binlog and repeat the same
--echo # process mentioned above and observe replay works fine.
--echo #
--source include/have_log_bin.inc
--source include/have_innodb.inc
--source include/have_binlog_format_statement.inc

--let $binlog_alter_two_phase= `select @@binlog_alter_two_phase`
set global binlog_alter_two_phase=YES;
set binlog_alter_two_phase=YES;

create table t1 (f1 int primary key) engine=InnoDB;
create table t2 (f1 int primary key, constraint c1 foreign key (f1) references t1(f1)) engine=innodb;

--error ER_CANT_CREATE_TABLE
alter table t2 add constraint c1 foreign key (f1) references t1(f1);

drop table t2, t1;
select @@gtid_binlog_state;
FLUSH LOGS;

let MYSQLD_DATADIR= `select @@datadir;`;
--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/slave_1.sql

--echo # reset the binlog
RESET MASTER;
--echo # execute the binlog
--exec $MYSQL --port=$MASTER_MYPORT --host=127.0.0.1 -e "source $MYSQLTEST_VARDIR/tmp/slave_1.sql"
SELECT @@gtid_binlog_state;
FLUSH LOGS;
--echo # Replay 1: One more time to simulate S->S case
--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/slave_2.sql

RESET MASTER;
--echo # execute the binlog
--exec $MYSQL --port=$MASTER_MYPORT --host=127.0.0.1 -e "source $MYSQLTEST_VARDIR/tmp/slave_2.sql"
SELECT @@gtid_binlog_state;
FLUSH LOGS;
--echo # Replay 2: One more time to simulate S->S case
--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/slave_3.sql
RESET MASTER;
--echo # execute the binlog
--exec $MYSQL --port=$MASTER_MYPORT --host=127.0.0.1 -e "source $MYSQLTEST_VARDIR/tmp/slave_3.sql"
SELECT @@gtid_binlog_state;

--echo # clean up
remove_file $MYSQLTEST_VARDIR/tmp/slave_1.sql;
remove_file $MYSQLTEST_VARDIR/tmp/slave_2.sql;
remove_file $MYSQLTEST_VARDIR/tmp/slave_3.sql;
RESET MASTER;

--eval set global binlog_alter_two_phase=$binlog_alter_two_phase;
6 changes: 6 additions & 0 deletions mysql-test/suite/galera/r/galera_as_slave.result
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ SELECT COUNT(*) = 3 FROM t1;
COUNT(*) = 3
1
connection node_3;
SET @@session.binlog_alter_two_phase = 1;
ALTER TABLE t1 ADD COLUMN f2 INT;
INSERT INTO t1 VALUES (4,1);
connection node_1;
connection node_2;
connection node_3;
DROP TABLE t1;
connection node_2;
STOP SLAVE;
Expand Down
16 changes: 16 additions & 0 deletions mysql-test/suite/galera/t/galera_as_slave.test
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,25 @@ INSERT INTO t1 VALUES (3);
--connection node_2
SELECT COUNT(*) = 3 FROM t1;

--connection node_3
SET @@session.binlog_alter_two_phase = 1;
ALTER TABLE t1 ADD COLUMN f2 INT;
INSERT INTO t1 VALUES (4,1);

--connection node_1
--let $count = 4
--let $table = t1
--source include/wait_until_rows_count.inc

--connection node_2
--let $count = 4
--let $table = t1
--source include/wait_until_rows_count.inc

--connection node_3
DROP TABLE t1;


--connection node_2
--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
--source include/wait_condition.inc
Expand Down
60 changes: 60 additions & 0 deletions mysql-test/suite/rpl/include/start_alter_basic.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#
# Run start alter basic tests (CA/RA with given engine)
# Params:-
# $engine
# $sync_slave
# master_node and slave_node connection should be defined

--echo # $engine
--connection master_node
--eval create table t1(a int, b int) engine=$engine;
insert into t1 values(1,1);
insert into t1 values(2,2);
--echo # Normal Alter
alter table t1 add column c int;
show create table t1;
--echo # Failed Alter
insert into t1 values(1,1, NULL);
--error ER_DUP_ENTRY
alter table t1 change a a int unique;
set @@session.binlog_alter_two_phase = 0;
alter table t1 change a a int;
set @@session.binlog_alter_two_phase = 1;
alter table t1 change a a int;
show create table t1;

# The following restriction should be removed post MDEV-26005 fix.
# TODO MDEV-26130 , should be removed after fixing
#if ($engine != 'aria')
#{
#--eval create temporary table tmp_tbl(a int, b int) engine=$engine;
#insert into tmp_tbl values(1,1);
#insert into tmp_tbl values(2,2);
#--echo # Normal Alter
#alter table tmp_tbl add column c int;
#--echo # Failed Alter
#insert into tmp_tbl values(1,1, NULL);
#--error ER_DUP_ENTRY
#alter table tmp_tbl change a a int unique ;
#show create table tmp_tbl;
#}
if ($sync_slave)
{
--source include/save_master_gtid.inc
--connection slave_node
--source include/sync_with_master_gtid.inc
show create table t1;
}

--connection master_node
drop table t1;
#if ($engine != 'aria')
#{
#drop temporary table tmp_tbl;
#}
if ($sync_slave)
{
--source include/save_master_gtid.inc
--connection slave_node
--source include/sync_with_master_gtid.inc
}
Loading

0 comments on commit 0c5d134

Please sign in to comment.