1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

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.
This commit is contained in:
Sachin Setiya
2019-09-26 15:05:55 +05:30
parent 13535b2713
commit 27664ef29d
9 changed files with 159 additions and 35 deletions

View File

@ -399,16 +399,27 @@ static int send_file(THD *thd)
/**
Internal to mysql_binlog_send() routine that recalculates checksum for
a FD event (asserted) that needs additional arranment prior sending to slave.
1. FD event (asserted) that needs additional arranment prior sending to slave.
2. Start_encryption_log_event whose Ignored flag is set
TODO DBUG_ASSERT can be removed if this function is used for more general cases
*/
inline void fix_checksum(String *packet, ulong ev_offset)
inline void fix_checksum(enum_binlog_checksum_alg checksum_alg, String *packet,
ulong ev_offset)
{
if (checksum_alg == BINLOG_CHECKSUM_ALG_OFF ||
checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF)
return;
/* recalculate the crc for this event */
uint data_len = uint4korr(packet->ptr() + ev_offset + EVENT_LEN_OFFSET);
ha_checksum crc;
DBUG_ASSERT(data_len ==
DBUG_ASSERT((data_len ==
LOG_EVENT_MINIMAL_HEADER_LEN + FORMAT_DESCRIPTION_HEADER_LEN +
BINLOG_CHECKSUM_ALG_DESC_LEN + BINLOG_CHECKSUM_LEN);
BINLOG_CHECKSUM_ALG_DESC_LEN + BINLOG_CHECKSUM_LEN) ||
(data_len ==
LOG_EVENT_MINIMAL_HEADER_LEN + BINLOG_CRYPTO_SCHEME_LENGTH +
BINLOG_KEY_VERSION_LENGTH + BINLOG_NONCE_LENGTH +
BINLOG_CHECKSUM_LEN));
crc= my_checksum(0, (uchar *)packet->ptr() + ev_offset, data_len -
BINLOG_CHECKSUM_LEN);
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,
THD *thd= info->thd;
String *packet= info->packet;
Log_event_type event_type;
bool initial_log_pos= info->clear_initial_log_pos;
DBUG_ENTER("send_format_descriptor_event");
/**
@ -2233,7 +2245,7 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
(*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
if (info->clear_initial_log_pos)
if (initial_log_pos)
{
info->clear_initial_log_pos= false;
/*
@ -2251,9 +2263,7 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
ST_CREATED_OFFSET+ev_offset, (ulong) 0);
/* fix the checksum due to latest changes in header */
if (info->current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
info->current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
fix_checksum(packet, ev_offset);
fix_checksum(info->current_checksum_alg, packet, ev_offset);
}
else if (info->using_gtid_state)
{
@ -2274,9 +2284,7 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
{
int4store((char*) packet->ptr()+LOG_EVENT_MINIMAL_HEADER_LEN+
ST_CREATED_OFFSET+ev_offset, (ulong) 0);
if (info->current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
info->current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
fix_checksum(packet, ev_offset);
fix_checksum(info->current_checksum_alg, packet, ev_offset);
}
}
@ -2289,12 +2297,16 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
}
/*
Read the following Start_encryption_log_event but don't send it to slave.
Slave doesn't need to know whether master's binlog is encrypted,
and if it'll want to encrypt its logs, it should generate its own
random nonce, not use the one from the master.
Read the following Start_encryption_log_event and send it to slave as
Ignorable_log_event. Although Slave doesn't need to know whether master's
binlog is encrypted but it needs to update slave log pos (for mysqlbinlog).
If slave want to encrypt its logs, it should generate its own
random nonce, it should not use the one from the master.
*/
packet->length(0);
/* reset transmit packet for the event read from binary log file */
if (reset_transmit_packet(info, info->flags, &ev_offset, &info->errmsg))
DBUG_RETURN(1);
info->last_pos= linfo->pos;
error= Log_event::read_log_event(log, packet, info->fdev,
opt_master_verify_checksum
@ -2308,12 +2320,13 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
DBUG_RETURN(1);
}
event_type= (Log_event_type)((uchar)(*packet)[LOG_EVENT_OFFSET]);
event_type= (Log_event_type)((uchar)(*packet)[LOG_EVENT_OFFSET + ev_offset]);
if (event_type == START_ENCRYPTION_EVENT)
{
Start_encryption_log_event *sele= (Start_encryption_log_event *)
Log_event::read_log_event(packet->ptr(), packet->length(), &info->errmsg,
info->fdev, BINLOG_CHECKSUM_ALG_OFF);
Log_event::read_log_event(packet->ptr() + ev_offset, packet->length()
- ev_offset, &info->errmsg, info->fdev,
BINLOG_CHECKSUM_ALG_OFF);
if (!sele)
{
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,
delete sele;
DBUG_RETURN(1);
}
/* Make it Ignorable_log_event and send it */
(*packet)[FLAGS_OFFSET+ev_offset] |= LOG_EVENT_IGNORABLE_F;
if (initial_log_pos)
int4store((char*) packet->ptr()+LOG_POS_OFFSET+ev_offset, (ulong) 0);
/* fix the checksum due to latest changes in header */
fix_checksum(info->current_checksum_alg, packet, ev_offset);
if (my_net_write(info->net, (uchar*) packet->ptr(), packet->length()))
{
info->errmsg= "Failed on my_net_write()";
info->error= ER_UNKNOWN_ERROR;
DBUG_RETURN(1);
}
delete sele;
}
else if (start_pos == BIN_LOG_HEADER_SIZE)