1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MDEV-27436: binlog corruption (/tmp no space left on device at the same moment)

This commit fixes several bugs in error handling around disk full when
writing the statement/transaction binlog caches:

1. If the error occurs during a non-transactional statement, the code
attempts to binlog the partially executed statement (as it cannot roll
back). The stmt_cache->error was still set from the disk full error. This
caused MYSQL_BIN_LOG::write_cache() to get an error while trying to read the
cache to copy it to the binlog. This was then wrongly interpreted as a disk
full error writing to the binlog file. As a result, a partial event group
containing just a GTID event (no query or commit) was binlogged. Fixed by
checking if an error is set in the statement cache, and if so binlog an
INCIDENT event instead of a corrupt event group, as for other errors.

2. For LOAD DATA LOCAL INFILE, if a disk full error occured while writing to
the statement cache, the code would attempt to abort and read-and-discard
any remaining data sent by the client. The discard code would however
continue trying to write data to the statement cache, and wrongly interpret
another disk full error as end-of-file from the client. This left the client
connection with extra data which corrupts the communication for the next
command, as well as again causing an corrupt/incomplete event to be
binlogged. Fixed by restoring the default read function before reading any
remaining data from the client connection.

Reviewed-by: Andrei Elkin <andrei.elkin@mariadb.com>
Signed-off-by: Kristian Nielsen <knielsen@knielsen-hq.org>
This commit is contained in:
Kristian Nielsen
2023-10-24 13:06:45 +02:00
parent e52777f1a4
commit 6fa69ad747
8 changed files with 286 additions and 8 deletions

View File

@ -4499,6 +4499,10 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count)
/* buffer contains position where we started last read */
uchar* buffer= (uchar*) my_b_get_buffer_start(file);
uint max_event_size= lf_info->thd->variables.max_allowed_packet;
int res;
#ifndef DBUG_OFF
bool did_dbug_inject= false;
#endif
if (lf_info->thd->is_current_stmt_binlog_format_row())
goto ret;
@ -4506,6 +4510,19 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count)
lf_info->last_pos_in_file >= my_b_get_pos_in_file(file))
goto ret;
DBUG_EXECUTE_IF("load_data_binlog_cache_error",
{
/*
Simulate "disk full" error in the middle of writing to
the binlog cache.
*/
if (lf_info->last_pos_in_file >= 2*4096)
{
DBUG_SET("+d,simulate_file_write_error");
did_dbug_inject= true;
}
};);
for (block_len= (uint) (my_b_get_bytes_in_buffer(file)); block_len > 0;
buffer += MY_MIN(block_len, max_event_size),
block_len -= MY_MIN(block_len, max_event_size))
@ -4517,7 +4534,10 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count)
MY_MIN(block_len, max_event_size),
lf_info->log_delayed);
if (mysql_bin_log.write(&a))
DBUG_RETURN(1);
{
res= 1;
goto err;
}
}
else
{
@ -4526,12 +4546,20 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count)
MY_MIN(block_len, max_event_size),
lf_info->log_delayed);
if (mysql_bin_log.write(&b))
DBUG_RETURN(1);
{
res= 1;
goto err;
}
lf_info->wrote_create_file= 1;
}
}
ret:
int res= Buffer ? lf_info->real_read_function(file, Buffer, Count) : 0;
res= Buffer ? lf_info->real_read_function(file, Buffer, Count) : 0;
err:
#ifndef DBUG_OFF
if (did_dbug_inject)
DBUG_SET("-d,simulate_file_write_error");
#endif
DBUG_RETURN(res);
}