1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-41465 Assertion `table->stat_initialized()' fails on the slave upon RBR with indexed virtual column

Problem:
========
  After MDEV-39261, purge opens a MariaDB TABLE handle (via
ha_innobase::open()) to compute indexed virtual columns.
ha_innobase::open() skips statistics initialization for
background threads, by design due to commit
9739cf1859 (MDEV-25664)

When purge is done with the table, it is closed and goes back into
the shared table cache.  The table cache can later hand this
very same object to a replication thread. On that reuse,
MariaDB does not call ha_innobase::open() again, so the table
statistics never get a second chance to be initialized.
Next DML on that table aborts the server saying table
statistics was not initialized.

Solution:
========
ha_innobase::external_lock(): When the lock is being acquired
and the caller is not a background thread, check if the
table's statistics are still uninitialized, and if so, initialize the
table statistics. This runs once per table per statement, so it
catches the gap regardless of whether the TABLE object came
from a fresh open() or was reused from the table cache.
This commit is contained in:
Thirunarayanan Balathandayuthapani
2026-10-13 15:55:37 +05:30
parent 8226e48ab5
commit 51c61f1655
3 changed files with 72 additions and 0 deletions

View File

@@ -377,4 +377,26 @@ SET GLOBAL slave_run_triggers_for_rbr= @save_slave_run_triggers_for_rbr;
connection master;
DROP TABLE t3,t2,t1;
connection slave;
#
# Test case 10: After a slave restart, purge must not be
# allowed to leave a table with uninitialized statistics
#
connection master;
CREATE TABLE t1 (a INT PRIMARY KEY, b INT, v INT AS (b) VIRTUAL,
KEY(v))ENGINE=InnoDB;
INSERT INTO t1 (a,b) VALUES (1,1),(2,2),(3,3);
UPDATE t1 SET b=b+10;
connection slave;
# Restart the slave
include/rpl_restart_server.inc [server_number=2]
connection slave;
SET GLOBAL innodb_max_purge_lag_wait=0;
include/start_slave.inc
connection master;
UPDATE t1 SET b=b+1;
connection slave;
include/diff_tables.inc [master:test.t1, slave:test.t1]
connection master;
DROP TABLE t1;
connection slave;
include/rpl_end.inc

View File

@@ -18,10 +18,15 @@
# 'slave_run_triggers_for_rbr=NO'
# 9) Triggers are Foreign Keys are present only on slave and
# 'slave_run_triggers_for_rbr=YES'
# 10) After a slave restart, purge (a background thread) is the first
# to open a table with an indexed virtual column, before the SQL
# thread applies any statement against it. The next replicated
# UPDATE on that table must not assert.
#
# ==== References ====
#
# MDEV-23033: All slaves crash once in ~24 hours and loop restart with signal 11
# MDEV-41465: Assertion `table->stat_initialized()' fails on the slave upon RBR with indexed virtual column
#
--source include/have_binlog_format_row.inc
@@ -422,4 +427,34 @@ PROCEDURE
--source $MYSQLTEST_VARDIR/tmp/trig_fk_on_slave.inc
--remove_file $MYSQLTEST_VARDIR/tmp/trig_fk_on_slave.inc
--echo #
--echo # Test case 10: After a slave restart, purge must not be
--echo # allowed to leave a table with uninitialized statistics
--echo #
--connection master
CREATE TABLE t1 (a INT PRIMARY KEY, b INT, v INT AS (b) VIRTUAL,
KEY(v))ENGINE=InnoDB;
INSERT INTO t1 (a,b) VALUES (1,1),(2,2),(3,3);
UPDATE t1 SET b=b+10;
--sync_slave_with_master
--echo # Restart the slave
--let $rpl_server_number= 2
--source include/rpl_restart_server.inc
--connection slave
SET GLOBAL innodb_max_purge_lag_wait=0;
--source include/start_slave.inc
--connection master
UPDATE t1 SET b=b+1;
--sync_slave_with_master
--let $diff_tables= master:test.t1, slave:test.t1
--source include/diff_tables.inc
--connection master
DROP TABLE t1;
--sync_slave_with_master
--source include/rpl_end.inc

View File

@@ -16687,6 +16687,21 @@ ha_innobase::external_lock(
ut_ad(not_autocommit || trx->n_mysql_tables_in_use
|| UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
set_lock:
/* A background thread may have been the one
that first opened this table. In that case,
ha_innobase::open() skips stats initialization
for such threads, and the resulting
TABLE object can later be handed out of the
table cache to a real client or replication thread
without open() being called again. Make sure a
non-background user never runs
DML against uninitialized statistics. */
if (!THDVAR(thd, background_thread)
&& !m_prebuilt->table->stat_initialized()) {
info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE
| HA_STATUS_CONST | HA_STATUS_OPEN);
}
*trx->detailed_error = 0;
innobase_register_trx(ht, thd, trx);