revision-id: 69d95c1b79c9cb0bfe92b47e2852ffeefbedffc7 (mariadb-10.1.35-59-g69d95c1b79c) parent(s): d3a8b5aa9cee24a6397662e30df2e915f45460e0 author: Andrei Elkin committer: Andrei Elkin timestamp: 2018-09-19 14:09:02 +0300 message: MDEV-17133 dump thread reads from a past position According to logs analysis the Dump thread attempted to read again data which was already sent. The reason of regressed read turns out in an _my_b_cache_read() early exit branch which missed to distinguish between total zero size read (e.g when Count argument is zero) from a case when the data are fully read by sole accessing the cache's file. In the latter case this then effective reading must be reflected in the cache's state. Fixed with set up a separation mark (length= 0), retain early exit to the zero Count *and* when the mark is there, and conduct standard cache state change otherwise. --- mysys/mf_iocache.c | 7 ++-- unittest/sql/mf_iocache-t.cc | 83 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 56b1ae3fc6e..79893d6f7c1 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -563,7 +563,7 @@ int _my_b_write(IO_CACHE *info, const uchar *Buffer, size_t Count) int _my_b_cache_read(IO_CACHE *info, uchar *Buffer, size_t Count) { - size_t length, diff_length, left_length= 0, max_length; + size_t length= 0, diff_length, left_length= 0, max_length; my_off_t pos_in_file; DBUG_ENTER("_my_b_cache_read"); @@ -668,7 +668,10 @@ int _my_b_cache_read(IO_CACHE *info, uchar *Buffer, size_t Count) else { info->error= 0; - DBUG_RETURN(0); /* EOF */ + if (length == 0) /* nothing was read */ + DBUG_RETURN(0); /* EOF */ + + length= 0; /* non-zero size read was done */ } } else if ((length= mysql_file_read(info->file,info->buffer, max_length, diff --git a/unittest/sql/mf_iocache-t.cc b/unittest/sql/mf_iocache-t.cc index 8f97745f0fc..3f28134084d 100644 --- a/unittest/sql/mf_iocache-t.cc +++ b/unittest/sql/mf_iocache-t.cc @@ -285,11 +285,91 @@ void mdev14014() close_cached_file(&info); } +void mdev17133() +{ + int res, k; + const int eof_iter=16, read_iter= 16; + uchar buf_i[1024*256]; // read + uchar buf_o[sizeof(buf_i)]; // write + const size_t eof_block_size= sizeof(buf_o) / eof_iter; + const size_t read_size= eof_block_size / read_iter; + size_t total; + + memset(buf_i, 0, sizeof( buf_i)); + memset(buf_o, FILL, sizeof(buf_o)); + + diag("MDEV-17133 Dump thread reads from the past"); + + init_io_cache_encryption(); + + res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0); + ok(res == 0, "open_cached_file" INFO_TAIL); + + res= my_b_write(&info, buf_o, sizeof(buf_o)); + ok(res == 0, "buffer is written" INFO_TAIL); + res= my_b_tell(&info); + ok(res == sizeof(buf_o), "cache size as expected"); + + res= my_b_flush_io_cache(&info, 1); + ok(res == 0, "flush" INFO_TAIL); + res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0); + ok(res == 0, "reinit READ_CACHE" INFO_TAIL); + + // read the written data by chunks of variable size eof_iter times + for (k= eof_iter, info.end_of_file=0, total= 0; k; k--) + { + int i; + size_t curr_read_size; + info.end_of_file= + k == 1 ? sizeof(buf_o) : + MY_MIN(sizeof(buf_o), + info.end_of_file + eof_block_size + + // plus 10% of randomization to the average + (eof_block_size/10 - rand() % (eof_block_size/5))); + + // read a chunk by blocks of variable size read_iter times + // the last block completes the current chunk + for (i= 0; i < read_iter; i++, total += curr_read_size) + { + char buf_check[eof_block_size]; + + curr_read_size= + i == read_iter - 1 ? info.end_of_file - total : + MY_MIN(info.end_of_file - total, + read_size + read_size/10 - rand() % (read_size/5)); + + res= my_b_read(&info, buf_i + total, MY_MIN(19, curr_read_size)); + ok(res == 0, "read of 19"); + // mark read bytes in the used part of the cache buffer + memset(info.buffer, 0, info.read_pos - info.buffer); + + // random size 2nd read + res= my_b_read(&info, buf_i + total + MY_MIN(19, curr_read_size), + 19 >= curr_read_size ? 0 : curr_read_size - 19); + ok(res == 0, "rest of read"); + // mark read bytes in the used part of the cache buffer + memset(info.buffer, 0, info.read_pos - info.buffer); + + // check that no marked bytes are read + memset(buf_check, FILL, curr_read_size); + ok(memcmp(buf_i + total, buf_check, curr_read_size) == 0, + "read correct data"); + } + ok(info.pos_in_file + (info.read_end - info.buffer) == info.end_of_file, + "cache is read up to eof"); + ok(total == info.end_of_file, "total matches eof"); + } + ok(total == sizeof(buf_i), "read total size match"); + ok(buf_i[sizeof(buf_i) - 1] == FILL, "data read correctly"); + + close_cached_file(&info); +} + int main(int argc __attribute__((unused)),char *argv[]) { MY_INIT(argv[0]); - plan(51); + plan(602); /* temp files with and without encryption */ encrypt_tmp_files= 1; @@ -306,6 +386,7 @@ int main(int argc __attribute__((unused)),char *argv[]) encrypt_tmp_files= 0; mdev14014(); + mdev17133(); my_end(0); return exit_status();