revision-id: e8d3583fc88ea8c08a94aceba5a21bd5e367c454 (mariadb-10.2.30-39-ge8d3583fc88) parent(s): 16bce0f6fe6bcad0091dc45a97a8ac7b33fe9d44 author: Sujatha committer: Sujatha timestamp: 2019-12-31 11:49:42 +0530 message: MDEV-18514: Assertion `!writer.checksum_len || writer.remains == 0' failed Analysis: ======== 'max_binlog_cache_size' is configured and a huge transaction is executed. When the transaction specific events size exceeds 'max_binlog_cache_size' the event cannot be written to the binary log cache and cache write error is raised. Upon cache write error the statement is rolled back and the transaction cache should be truncated to a previous statement specific position. The truncate operation should reset the cache to earlier valid positions and flush the new changes. Even though the flush is successful the cache write error is still in marked state. The truncate code interprets the cache write error as cache flush failure and returns abruptly without modifying the write cache parameters. Hence cache is in a invalid state. When a COMMIT statement is executed in this session it tries to flush the contents of transaction cache to binary log. Since cache has partial events the cache write operation will report 'writer.remains' assert. Fix: === During the rollback operation truncate the cache and flush the cache contents. If truncation is successful clear the cache write error. --- .../suite/rpl/r/rpl_binlog_rollback_cleanup.result | 9 +++++ .../suite/rpl/t/rpl_binlog_rollback_cleanup.test | 46 ++++++++++++++++++++++ mysys/mf_iocache.c | 2 + 3 files changed, 57 insertions(+) diff --git a/mysql-test/suite/rpl/r/rpl_binlog_rollback_cleanup.result b/mysql-test/suite/rpl/r/rpl_binlog_rollback_cleanup.result new file mode 100644 index 00000000000..a677cbfecf6 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_binlog_rollback_cleanup.result @@ -0,0 +1,9 @@ +include/master-slave.inc +[connection master] +connection master; +SET GLOBAL max_binlog_cache_size = 65536; +CREATE TABLE t1(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=INNODB; +ERROR HY000: Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again +SET GLOBAL max_binlog_cache_size= ORIGINAL_VALUE; +DROP TABLE t1; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_binlog_rollback_cleanup.test b/mysql-test/suite/rpl/t/rpl_binlog_rollback_cleanup.test new file mode 100644 index 00000000000..ed4d713f626 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_binlog_rollback_cleanup.test @@ -0,0 +1,46 @@ +# ==== Purpose ==== +# +# Test verifies that when flushing an event to binary log fails the transaction +# is successfully rolled back and following COMMIT command doesn't report any +# assert. +# +# ==== Implementation ==== +# +# Steps: +# 0 - SET max_binlog_cache_size=64K +# 1 - Create an Innodb table and insert required amount of data. Execute an +# UPDATE operation which generates a big update event whose size exceeds +# max_binlog_cache_size. +# 2 - Wait for error 1197. Execute COMMIT command. +# 3 - COMMIT should be successful. +# +# ==== References ==== +# +# MDEV-18514: Assertion `!writer.checksum_len || writer.remains == 0' failed +# +--source include/have_innodb.inc +--source include/have_binlog_format_row.inc +--source include/master-slave.inc +--connection master +let $old_max_binlog_cache_size= query_get_value(SHOW VARIABLES LIKE "max_binlog_cache_size", Value, 1); +SET GLOBAL max_binlog_cache_size = 65536; +CREATE TABLE t1(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=INNODB; +let $data = `select concat('"', repeat('a',6000), '"')`; +let $data1 = `select concat('"', repeat('b',6000), '"')`; +--disable_query_log +eval INSERT INTO t1 (a, data) VALUES (1, CONCAT($data, $data)); +eval INSERT INTO t1 (a, data) VALUES (2, CONCAT($data, $data)); +eval INSERT INTO t1 (a, data) VALUES (3, CONCAT($data, $data)); +eval INSERT INTO t1 (a, data) VALUES (4, CONCAT($data, $data)); +eval INSERT INTO t1 (a, data) VALUES (5, CONCAT($data, $data)); +START TRANSACTION; +--error ER_TRANS_CACHE_FULL +eval UPDATE t1 SET data=$data1; +COMMIT; +--enable_query_log + +--replace_result $old_max_binlog_cache_size ORIGINAL_VALUE +--eval SET GLOBAL max_binlog_cache_size= $old_max_binlog_cache_size +DROP TABLE t1; + +--source include/rpl_end.inc diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index d7689e204b6..6196e65b9de 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -1949,6 +1949,8 @@ int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock) int res= info->write_function(info, info->write_buffer, length); if (res) DBUG_RETURN(res); + else + info->error= 0; set_if_bigger(info->end_of_file, info->pos_in_file); }