Skip to content

Commit 27664ef

Browse files
MDEV-20574 Position of events reported by mysqlbinlog is wrong with encrypted binlogs, SHOW BINLOG EVENTS reports the correct one.
Analysis Mysqlbinlog output for encrypted binary log #Q> insert into tab1 values (3,'row 003') #190912 17:36:35 server id 10221 end_log_pos 980 CRC32 0x53bcb3d3 Table_map: `test`.`tab1` mapped to number 19 # at 940 #190912 17:36:35 server id 10221 end_log_pos 1026 CRC32 0xf2ae5136 Write_rows: table id 19 flags: STMT_END_F Here we can see Table_map_log_event ends at 980 but Next event starts at 940. And the reason for that is we do not send START_ENCRYPTION_EVENT to the slave Solution:- Send Start_encryption_log_event as Ignorable_log_event to slave(mysqlbinlog), So that mysqlbinlog can update its log_pos. Since Slave can request multiple FORMAT_DESCRIPTION_EVENT while master does not have so We only update slave master pos when master actually have the FORMAT_DESCRIPTION_EVENT. Similar logic should be applied for START_ENCRYPTION_EVENT. Also added the test case when new server reads the data from old server which does not send START_ENCRYPTION_EVENT to slave. Master Slave Upgrade Scenario. When Slave is updated first, Slave will have extra logic of handling START_ENCRYPTION_EVENT But master willnot be sending START_ENCRYPTION_EVENT. So there will be no issue. When Master is updated first, It will send START_ENCRYPTION_EVENT to slave , But slave will ignore this event in queue_event.
1 parent 13535b2 commit 27664ef

File tree

9 files changed

+159
-35
lines changed

9 files changed

+159
-35
lines changed
1022 Bytes
Binary file not shown.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
include/master-slave.inc
2+
[connection master]
3+
connection slave;
4+
include/stop_slave.inc
5+
connection master;
6+
include/rpl_stop_server.inc [server_number=1]
7+
# Data in binlog
8+
# CREATE TABLE t1 (a INT);
9+
# INSERT INTO t1 VALUES (1),(2),(3);
10+
# REPLACE INTO t1 VALUES (4);
11+
include/rpl_start_server.inc [server_number=1]
12+
connection slave;
13+
RESET SLAVE;
14+
RESET MASTER;
15+
CHANGE MASTER TO master_host='127.0.0.1', master_port=SERVER_MYPORT_1, master_user='root', master_log_file='master-bin.000001', master_log_pos=4;
16+
include/start_slave.inc
17+
DESC t1;
18+
Field Type Null Key Default Extra
19+
a int(11) YES NULL
20+
SELECT * FROM t1 ORDER BY a;
21+
a
22+
1
23+
2
24+
3
25+
4
26+
DROP TABLE t1;
27+
include/rpl_end.inc
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# MDEV-20574 Position of events reported by mysqlbinlog is wrong with encrypted binlogs, SHOW BINLOG EVENTS reports the correct one.
2+
# Test replicating off old master.
3+
# Test case Desc:- When new server reads the data from old server binlog which
4+
# does not send START_ENCRYPTION_EVENT to slave.
5+
# We simulate old master by copying in pre-generated binlog files from earlier
6+
# server versions with encrypted binlog.
7+
--source include/have_binlog_format_row.inc
8+
--source include/master-slave.inc
9+
--source include/have_innodb.inc
10+
11+
--connection slave
12+
--source include/stop_slave.inc
13+
14+
--connection master
15+
--let $datadir= `SELECT @@datadir`
16+
17+
--let $rpl_server_number= 1
18+
--source include/rpl_stop_server.inc
19+
20+
--remove_file $datadir/master-bin.000001
21+
--remove_file $datadir/master-bin.state
22+
--echo # Data in binlog
23+
--echo # CREATE TABLE t1 (a INT);
24+
--echo # INSERT INTO t1 VALUES (1),(2),(3);
25+
--echo # REPLACE INTO t1 VALUES (4);
26+
27+
--copy_file $MYSQL_TEST_DIR/std_data/binlog_before_20574.bin $datadir/master-bin.000001
28+
29+
--let $rpl_server_number= 1
30+
--source include/rpl_start_server.inc
31+
32+
--source include/wait_until_connected_again.inc
33+
--save_master_pos
34+
35+
--connection slave
36+
RESET SLAVE;
37+
RESET MASTER;
38+
--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
39+
eval CHANGE MASTER TO master_host='127.0.0.1', master_port=$SERVER_MYPORT_1, master_user='root', master_log_file='master-bin.000001', master_log_pos=4;
40+
--source include/start_slave.inc
41+
--sync_with_master
42+
DESC t1;
43+
SELECT * FROM t1 ORDER BY a;
44+
45+
DROP TABLE t1;
46+
--source include/rpl_end.inc

mysql-test/suite/binlog_encryption/binlog_row_annotate.result

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ DELIMITER /*!*/;
104104
#010909 4:46:40 server id # end_log_pos # Start: binlog v 4, server v #.##.## created 010909 4:46:40 at startup
105105
ROLLBACK/*!*/;
106106
# at #
107+
#010909 4:46:40 server id # end_log_pos # Ignorable
108+
# Ignorable event type 164 (Start_encryption)
109+
# at #
107110
#010909 4:46:40 server id # end_log_pos # Gtid list []
108111
# at #
109112
#010909 4:46:40 server id # end_log_pos # Binlog checkpoint master-bin.000001
@@ -336,6 +339,9 @@ DELIMITER /*!*/;
336339
#010909 4:46:40 server id # end_log_pos # Start: binlog v 4, server v #.##.## created 010909 4:46:40 at startup
337340
ROLLBACK/*!*/;
338341
# at #
342+
#010909 4:46:40 server id # end_log_pos # Ignorable
343+
# Ignorable event type 164 (Start_encryption)
344+
# at #
339345
#010909 4:46:40 server id # end_log_pos # Gtid list []
340346
# at #
341347
#010909 4:46:40 server id # end_log_pos # Binlog checkpoint master-bin.000001
@@ -495,6 +501,9 @@ DELIMITER /*!*/;
495501
#010909 4:46:40 server id # end_log_pos # Start: binlog v 4, server v #.##.## created 010909 4:46:40 at startup
496502
ROLLBACK/*!*/;
497503
# at #
504+
#010909 4:46:40 server id # end_log_pos # Ignorable
505+
# Ignorable event type 164 (Start_encryption)
506+
# at #
498507
#010909 4:46:40 server id # end_log_pos # Gtid list []
499508
# at #
500509
#010909 4:46:40 server id # end_log_pos # Binlog checkpoint master-bin.000001

mysql-test/suite/binlog_encryption/mysqlbinlog.result

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ INSERT INTO t1 VALUES (1),(2),(3);
44
REPLACE INTO t1 VALUES (4);
55
DROP TABLE t1;
66
FLUSH LOGS;
7+
FOUND 1 /Ignorable event type 164.*/ in binlog_enc.sql

mysql-test/suite/binlog_encryption/mysqlbinlog.test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@ let outfile=$MYSQLTEST_VARDIR/tmp/binlog_enc.sql;
1717
exec $MYSQL_BINLOG $local > $outfile;
1818
exec $MYSQL_BINLOG $local --force-read >> $outfile;
1919
exec $MYSQL_BINLOG $remote >> $outfile;
20+
--let SEARCH_FILE= $outfile
21+
--let SEARCH_PATTERN= Ignorable event type 164.*
22+
--source include/search_pattern_in_file.inc
2023
remove_file $outfile;
2124

sql/log_event.cc

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2077,6 +2077,19 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
20772077
alg != BINLOG_CHECKSUM_ALG_OFF))
20782078
event_len= event_len - BINLOG_CHECKSUM_LEN;
20792079

2080+
/*
2081+
Create an object of Ignorable_log_event for unrecognized sub-class.
2082+
So that SLAVE SQL THREAD will only update the position and continue.
2083+
We should look for this flag first instead of judging by event_type
2084+
Any event can be Ignorable_log_event if it has this flag on.
2085+
look into @note of Ignorable_log_event
2086+
*/
2087+
if (uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F)
2088+
{
2089+
ev= new Ignorable_log_event(buf, fdle,
2090+
get_type_str((Log_event_type) event_type));
2091+
goto exit;
2092+
}
20802093
switch(event_type) {
20812094
case QUERY_EVENT:
20822095
ev = new Query_log_event(buf, event_len, fdle, QUERY_EVENT);
@@ -2203,24 +2216,13 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
22032216
ev = new Start_encryption_log_event(buf, event_len, fdle);
22042217
break;
22052218
default:
2206-
/*
2207-
Create an object of Ignorable_log_event for unrecognized sub-class.
2208-
So that SLAVE SQL THREAD will only update the position and continue.
2209-
*/
2210-
if (uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F)
2211-
{
2212-
ev= new Ignorable_log_event(buf, fdle,
2213-
get_type_str((Log_event_type) event_type));
2214-
}
2215-
else
2216-
{
2217-
DBUG_PRINT("error",("Unknown event code: %d",
2218-
(uchar) buf[EVENT_TYPE_OFFSET]));
2219-
ev= NULL;
2220-
break;
2221-
}
2219+
DBUG_PRINT("error",("Unknown event code: %d",
2220+
(uchar) buf[EVENT_TYPE_OFFSET]));
2221+
ev= NULL;
2222+
break;
22222223
}
22232224
}
2225+
exit:
22242226

22252227
if (ev)
22262228
{

sql/slave.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6317,7 +6317,18 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
63176317
mi->last_queued_gtid.seq_no == 1000)
63186318
goto skip_relay_logging;
63196319
});
6320+
goto default_action;
63206321
#endif
6322+
case START_ENCRYPTION_EVENT:
6323+
if (uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F)
6324+
{
6325+
/*
6326+
If the event was not requested by the slave (the slave did not ask for
6327+
it), i.e. has end_log_pos=0, we do not increment mi->master_log_pos
6328+
*/
6329+
inc_pos= uint4korr(buf+LOG_POS_OFFSET) ? event_len : 0;
6330+
break;
6331+
}
63216332
/* fall through */
63226333
default:
63236334
default_action:

sql/sql_repl.cc

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -399,16 +399,27 @@ static int send_file(THD *thd)
399399

400400
/**
401401
Internal to mysql_binlog_send() routine that recalculates checksum for
402-
a FD event (asserted) that needs additional arranment prior sending to slave.
402+
1. FD event (asserted) that needs additional arranment prior sending to slave.
403+
2. Start_encryption_log_event whose Ignored flag is set
404+
TODO DBUG_ASSERT can be removed if this function is used for more general cases
403405
*/
404-
inline void fix_checksum(String *packet, ulong ev_offset)
406+
407+
inline void fix_checksum(enum_binlog_checksum_alg checksum_alg, String *packet,
408+
ulong ev_offset)
405409
{
410+
if (checksum_alg == BINLOG_CHECKSUM_ALG_OFF ||
411+
checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF)
412+
return;
406413
/* recalculate the crc for this event */
407414
uint data_len = uint4korr(packet->ptr() + ev_offset + EVENT_LEN_OFFSET);
408415
ha_checksum crc;
409-
DBUG_ASSERT(data_len ==
416+
DBUG_ASSERT((data_len ==
410417
LOG_EVENT_MINIMAL_HEADER_LEN + FORMAT_DESCRIPTION_HEADER_LEN +
411-
BINLOG_CHECKSUM_ALG_DESC_LEN + BINLOG_CHECKSUM_LEN);
418+
BINLOG_CHECKSUM_ALG_DESC_LEN + BINLOG_CHECKSUM_LEN) ||
419+
(data_len ==
420+
LOG_EVENT_MINIMAL_HEADER_LEN + BINLOG_CRYPTO_SCHEME_LENGTH +
421+
BINLOG_KEY_VERSION_LENGTH + BINLOG_NONCE_LENGTH +
422+
BINLOG_CHECKSUM_LEN));
412423
crc= my_checksum(0, (uchar *)packet->ptr() + ev_offset, data_len -
413424
BINLOG_CHECKSUM_LEN);
414425
int4store(packet->ptr() + ev_offset + data_len - BINLOG_CHECKSUM_LEN, crc);
@@ -2135,6 +2146,7 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
21352146
THD *thd= info->thd;
21362147
String *packet= info->packet;
21372148
Log_event_type event_type;
2149+
bool initial_log_pos= info->clear_initial_log_pos;
21382150
DBUG_ENTER("send_format_descriptor_event");
21392151

21402152
/**
@@ -2233,7 +2245,7 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
22332245

22342246
(*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
22352247

2236-
if (info->clear_initial_log_pos)
2248+
if (initial_log_pos)
22372249
{
22382250
info->clear_initial_log_pos= false;
22392251
/*
@@ -2251,9 +2263,7 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
22512263
ST_CREATED_OFFSET+ev_offset, (ulong) 0);
22522264

22532265
/* fix the checksum due to latest changes in header */
2254-
if (info->current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
2255-
info->current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
2256-
fix_checksum(packet, ev_offset);
2266+
fix_checksum(info->current_checksum_alg, packet, ev_offset);
22572267
}
22582268
else if (info->using_gtid_state)
22592269
{
@@ -2274,9 +2284,7 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
22742284
{
22752285
int4store((char*) packet->ptr()+LOG_EVENT_MINIMAL_HEADER_LEN+
22762286
ST_CREATED_OFFSET+ev_offset, (ulong) 0);
2277-
if (info->current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
2278-
info->current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
2279-
fix_checksum(packet, ev_offset);
2287+
fix_checksum(info->current_checksum_alg, packet, ev_offset);
22802288
}
22812289
}
22822290

@@ -2289,12 +2297,16 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
22892297
}
22902298

22912299
/*
2292-
Read the following Start_encryption_log_event but don't send it to slave.
2293-
Slave doesn't need to know whether master's binlog is encrypted,
2294-
and if it'll want to encrypt its logs, it should generate its own
2295-
random nonce, not use the one from the master.
2300+
Read the following Start_encryption_log_event and send it to slave as
2301+
Ignorable_log_event. Although Slave doesn't need to know whether master's
2302+
binlog is encrypted but it needs to update slave log pos (for mysqlbinlog).
2303+
2304+
If slave want to encrypt its logs, it should generate its own
2305+
random nonce, it should not use the one from the master.
22962306
*/
2297-
packet->length(0);
2307+
/* reset transmit packet for the event read from binary log file */
2308+
if (reset_transmit_packet(info, info->flags, &ev_offset, &info->errmsg))
2309+
DBUG_RETURN(1);
22982310
info->last_pos= linfo->pos;
22992311
error= Log_event::read_log_event(log, packet, info->fdev,
23002312
opt_master_verify_checksum
@@ -2308,12 +2320,13 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
23082320
DBUG_RETURN(1);
23092321
}
23102322

2311-
event_type= (Log_event_type)((uchar)(*packet)[LOG_EVENT_OFFSET]);
2323+
event_type= (Log_event_type)((uchar)(*packet)[LOG_EVENT_OFFSET + ev_offset]);
23122324
if (event_type == START_ENCRYPTION_EVENT)
23132325
{
23142326
Start_encryption_log_event *sele= (Start_encryption_log_event *)
2315-
Log_event::read_log_event(packet->ptr(), packet->length(), &info->errmsg,
2316-
info->fdev, BINLOG_CHECKSUM_ALG_OFF);
2327+
Log_event::read_log_event(packet->ptr() + ev_offset, packet->length()
2328+
- ev_offset, &info->errmsg, info->fdev,
2329+
BINLOG_CHECKSUM_ALG_OFF);
23172330
if (!sele)
23182331
{
23192332
info->error= ER_MASTER_FATAL_ERROR_READING_BINLOG;
@@ -2327,6 +2340,18 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
23272340
delete sele;
23282341
DBUG_RETURN(1);
23292342
}
2343+
/* Make it Ignorable_log_event and send it */
2344+
(*packet)[FLAGS_OFFSET+ev_offset] |= LOG_EVENT_IGNORABLE_F;
2345+
if (initial_log_pos)
2346+
int4store((char*) packet->ptr()+LOG_POS_OFFSET+ev_offset, (ulong) 0);
2347+
/* fix the checksum due to latest changes in header */
2348+
fix_checksum(info->current_checksum_alg, packet, ev_offset);
2349+
if (my_net_write(info->net, (uchar*) packet->ptr(), packet->length()))
2350+
{
2351+
info->errmsg= "Failed on my_net_write()";
2352+
info->error= ER_UNKNOWN_ERROR;
2353+
DBUG_RETURN(1);
2354+
}
23302355
delete sele;
23312356
}
23322357
else if (start_pos == BIN_LOG_HEADER_SIZE)

0 commit comments

Comments
 (0)