The description says: "DML executed on primary may be blocked by any long
running DML that takes write locks, like SELECT FOR UPDATE or SELECT IN
SHARE MODE, on the replica".
I do not understand what is meant. How can DML on the master be blocked by
DML running on a slave?
From the title, it seems like the intention is to give the slave threads the
ability to kill non-slave queries if it gets blocked on any metadata lock or
engine table/row lock. It is not clear if this kill should be immediate, or
after some timeout like --slave-abort-blocking-timeout.
Also it is not clear what 'breaking the "high availability"' means here.
From other discussions I think this comes from a specific case at a user,
where some MaxScale switchover was delayed or blocked, but I am not sure the
actual root cause in this scenario was ever fully understood.
I would like to see a detailed description of the intended use case or use
cases here, be able to understand if this is the best and correct solution
for these use cases.
In a normal read-only slave, there should not normally be a need to use
SELECT FOR UPDATE or IN SHARE MODE in user queries. REPEATABLE READ will
provide snapshot isolation that should give consistent queries without
needing any locks that would block slave threads. This would be a better
solution that avoids user SELECTs getting randomly killed.
If the slave is non-readonly and there are conflicting user updates, the
issue is more complex, and merely killing off user queries will likely not
avoid problems, eg. the slave diverging from the master for example.
Also, even if there are user queries temporarily blocking a slave thread,
those queries would release their locks at transaction commit. So if they
are permanently blocking the slave, something else seems to be wrong, again
suggesting the real solution may lie elsewhere.
Now, onto possible implementation details.
There are (at least) two different kinds of locks that can block a slave
thread: metadata locks and storage engine locks (typically InnoDB row- or
table-level locks).
The new --slave-abort-blocking-timeout option works on metadata locks.
Metadata locks can be exclusive; these can block against other queries, and
are used mostly by DDL, I think. Normal DML would not block each other on
metadata locks, IIUC.
There is an existing facility for blocking storage engine locks, using the
functions thd_need_wait_reports() and thd_rpl_deadlock_check(). This
facility is used for detecting deadlocks between in-order committed
transactions in parallel replication, so currently only active for slave
threads.
This facility could be extended so that it would kill any non-slave
transaction that blocks any slave transaction. This needs support in each
storage engine, support is there currently for InnoDB. There is no support
in this facility to have a timeout before the kill happens, it would be
immediate whenever a slave thread gets blocked on an InnoDB lock.
The thd_rpl_deadlock_check() can have performance impact on non-slave
threads, see MDEV-24948, and there is a pending patch for a performance bug
in branch origin/knielsen_mdev24948. Therefore, using this to kill
non-slave queries would need to be configurable. Enabling the facility would
only affect new connections, as thd_rpl_deadlock_check() is called at
connection start and cached, for efficiency.
Can we improve the logging for this type of error by recording which thread id blocked the slave SQL thread? In the case of a user thread, it’s easy to identify.
2025-03-04 10:04:29 525132 [Warning] Aborted connection 525132 to db: 'database' user: 'appuser' host: '127.0.0.1' real ip: '10.232.147.130' (Got timeout reading communication packets)
2025-03-04 10:04:32 526165 [ERROR] Slave worker thread retried transaction 10 time(s) in vain, giving up. Consider raising the value of the slave_transaction_retries variable.
2025-03-04 10:04:32 526165 [ERROR] Slave SQL: Lock wait timeout exceeded; try restarting transaction, Gtid 1-1-1191236814, Internal MariaDB error code: 1205