diff --git a/mysql-test/suite/innodb/r/purge_stop_shutdown_crash.result b/mysql-test/suite/innodb/r/purge_stop_shutdown_crash.result new file mode 100644 index 0000000000000000000000000000000000000000..bb6de46c482bf767ccffa7a2ed5abc90fc1845f2 --- /dev/null +++ b/mysql-test/suite/innodb/r/purge_stop_shutdown_crash.result @@ -0,0 +1,34 @@ +SET GLOBAL innodb_purge_stop_now = ON; +CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1; +INSERT INTO t1 VALUES (1); +DELETE FROM t1; +SET GLOBAL DEBUG = '+d,innodb_purge_fail_clust_on_shutdown'; +SET GLOBAL innodb_purge_run_now = ON; +SET DEBUG_SYNC = +'now WAIT_FOR innodb_purge_fail_clust_reached TIMEOUT 10'; +# restart +SELECT CAST(purge_age.VARIABLE_VALUE AS UNSIGNED) <= +CAST(purge_view_age.VARIABLE_VALUE AS UNSIGNED) AS purge_caught_up +FROM performance_schema.global_status AS purge_age +CROSS JOIN performance_schema.global_status AS purge_view_age +WHERE purge_age.VARIABLE_NAME = 'INNODB_PURGE_TRX_ID_AGE' + AND purge_view_age.VARIABLE_NAME = 'INNODB_PURGE_VIEW_TRX_ID_AGE'; +purge_caught_up +1 +SELECT COUNT(*) AS logical_rows FROM t1; +logical_rows +0 +SET @old_stats_include_delete_marked = +@@GLOBAL.innodb_stats_include_delete_marked; +SET GLOBAL innodb_stats_include_delete_marked = ON; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +SELECT n_rows AS physical_rows_including_delete_marked +FROM mysql.innodb_table_stats +WHERE database_name = 'test' AND table_name = 't1'; +physical_rows_including_delete_marked +1 +SET GLOBAL innodb_stats_include_delete_marked = +@old_stats_include_delete_marked; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/purge_stop_shutdown_crash.test b/mysql-test/suite/innodb/t/purge_stop_shutdown_crash.test new file mode 100644 index 0000000000000000000000000000000000000000..cb2896154d54e4c885d78b7e6383908a9e5bd4ee --- /dev/null +++ b/mysql-test/suite/innodb/t/purge_stop_shutdown_crash.test @@ -0,0 +1,38 @@ +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +SET GLOBAL innodb_purge_stop_now = ON; + +CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1; +INSERT INTO t1 VALUES (1); +DELETE FROM t1; + +SET GLOBAL DEBUG = '+d,innodb_purge_fail_clust_on_shutdown'; +SET GLOBAL innodb_purge_run_now = ON; +SET DEBUG_SYNC = + 'now WAIT_FOR innodb_purge_fail_clust_reached TIMEOUT 10'; + +--source include/restart_mysqld.inc +--source include/wait_innodb_all_purged.inc + +SELECT CAST(purge_age.VARIABLE_VALUE AS UNSIGNED) <= + CAST(purge_view_age.VARIABLE_VALUE AS UNSIGNED) AS purge_caught_up +FROM performance_schema.global_status AS purge_age +CROSS JOIN performance_schema.global_status AS purge_view_age +WHERE purge_age.VARIABLE_NAME = 'INNODB_PURGE_TRX_ID_AGE' + AND purge_view_age.VARIABLE_NAME = 'INNODB_PURGE_VIEW_TRX_ID_AGE'; + +SELECT COUNT(*) AS logical_rows FROM t1; + +SET @old_stats_include_delete_marked = + @@GLOBAL.innodb_stats_include_delete_marked; +SET GLOBAL innodb_stats_include_delete_marked = ON; +ANALYZE TABLE t1; +SELECT n_rows AS physical_rows_including_delete_marked +FROM mysql.innodb_table_stats +WHERE database_name = 'test' AND table_name = 't1'; +SET GLOBAL innodb_stats_include_delete_marked = + @old_stats_include_delete_marked; + +DROP TABLE t1; diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc index 2a35d168d1332d4eb591081ac007990f18b42219..f119d31cfde962fadc872c6228dd3a66d16ee576 100644 --- a/storage/innobase/row/row0purge.cc +++ b/storage/innobase/row/row0purge.cc @@ -687,6 +687,22 @@ static inline void row_purge_remove_multi_sec_if_poss(purge_node_t *node, mem_heap_free(heap); +#ifndef NDEBUG + DBUG_EXECUTE_IF("innodb_purge_fail_clust_on_shutdown", { + const char act[] = "now SIGNAL innodb_purge_fail_clust_reached"; + assert(opt_debug_sync_timeout > 0); + assert(!debug_sync_set_action(current_thd, STRING_WITH_LEN(act))); + + /* Simulate a failed clustered-record purge exactly when shutdown starts, + so row_purge() takes its shutdown early-return path. */ + while (srv_shutdown_state.load() < SRV_SHUTDOWN_PURGE) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + return (false); + }); +#endif /* NDEBUG */ + return (row_purge_remove_clust_if_poss(node)); }