Bug #99572 server will trigger an assertion failure when do ATLER TABLE withconcurrent DML
Submitted: 14 May 2020 11:42 Modified: 14 May 2020 12:17
Reporter: Ceit Zhang Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: DDL Severity:S6 (Debug Builds)
Version:5.7.29, 5.7.30 OS:CentOS (CentOS 7.4)
Assigned to: CPU Architecture:x86
Tags: add index, ALTER TABLE, assertion failure, debug server

[14 May 2020 11:42] Ceit Zhang
Description:
When I test the ALTER TABLE ADD INDEX operation, I found that server may go crash when concurrent DML is running. In most cases this problem will not happen,but it can be reproduced steadily in a carefully constructed scene. The reason is that there is an assertion failure in function Compression::deserialize. Please to see `how to repeat` to get a detail error stack.

How to repeat:
# Please run the following mtr test case in debug version.
#--------------------------------------------------------

--source include/have_debug.inc
--source include/have_debug_sync.inc
--source include/have_innodb.inc
--source include/not_embedded.inc
--source include/not_windows.inc
--source include/count_sessions.inc

--echo # Reset innodb_sort_buffer_size to a lower value to make ALTER TABLE
--echo # use sort files.
--let $restart_parameters="restart: --innodb_sort_buffer_size=65536"
--source include/restart_mysqld.inc

--disable_warnings
--disable_result_log
--disable_query_log

DROP PROCEDURE IF EXISTS load_data;
DELIMITER //;

CREATE PROCEDURE load_data(table_name VARCHAR(256), start INT, count INT)
BEGIN
DECLARE i INT DEFAULT 0;
SET @insert_stmt := CONCAT("INSERT INTO ",table_name,"(a,b,c,d,e,f,g,h,x,y,z)
  VALUES (?, REPEAT('b',100), 3598, REPEAT('d',100), 3598, 3598, 3598, 3598, 3598, 3598, 3598)");
PREPARE insert_stmt FROM @insert_stmt;
WHILE i < count DO
SET @pk = i+start;
EXECUTE insert_stmt USING @pk;
SET i=i+1;
END WHILE;
END//

DELIMITER ;//

--enable_query_log
--enable_result_log
--enable_warnings

CREATE TABLE t_inplace_alter_tmp_file_02(
    a INT PRIMARY KEY,
    b VARCHAR(100),
    c INT DEFAULT NULL,
    d VARCHAR(100),
    e INT, f INT, g INT, h INT, x INT, y INT, z INT) ENGINE=INNODB;

--echo # con1 used to signal at sync point
connect(con1, localhost, root,,);

--echo # connection DEFAULT
--connection default

--echo #                     > Case 1: Generate Row Log Files
--echo # -----------------------------------------------------------------------
--echo # connection default
connection default;
--echo # 1. Prepare data.
CALL load_data("t_inplace_alter_tmp_file_02", 0, 20000);

--echo # 2. Alter table inplace.
SET DEBUG_SYNC='RESET';
SET DEBUG_SYNC='innodb_inplace_alter_table_enter SIGNAL do_dml WAIT_FOR go_on_execute EXECUTE 1';
--send ALTER TABLE t_inplace_alter_tmp_file_02 ADD INDEX ci(c);

--echo # 3. Do concurrent DML.
--echo # connnection con1
connection con1;
SET DEBUG_SYNC='now WAIT_FOR do_dml';
CALL load_data("t_inplace_alter_tmp_file_02", 20000, 20000);
SET DEBUG_SYNC='now SIGNAL go_on_execute';

--connection default
--reap

--echo #                     > Clean Up
--echo # -----------------------------------------------------------------------
disconnect con1;
--source include/wait_until_count_sessions.inc

--source include/force_restart.inc

Suggested fix:
The reason is that:
When the server apply `row log` generated during online ALTER TABLE, row_log_apply_ops() will use function os_file_read_no_error_handling_int_fd() to read the `row log file` and the passed parameter IORequest object is not set disable_compression() to force a raw read.
How to fix:
1. just make the IORequest object passed to os_file_read_no_error_handling_int_fd() in function row_log_apply_ops() to disable compression with IORequest::disable_compression().
[14 May 2020 12:17] MySQL Verification Team
Hello Ceit Zhang,

Thank you for the report and mtr test case.
Observed that 5.7.30 is affected.

regards,
Umesh
[14 May 2020 12:17] MySQL Verification Team
Test results - 5.7.30, 8.0.20

Attachment: 99572.results (application/octet-stream, text), 65.66 KiB.