MDEV-23851 BF-BF Conflict issue because of UK GAP locks

Some DML operations on tables having unique secondary keys cause scanning
in the secondary index, for instance to find potential unique key violations
in the seconday index. This scanning may involve GAP locking in the index.
As this locking happens also when applying replication events in high priority
applier threads, there is a probabality for lock conflicts between two wsrep
high priority threads.

This PR avoids lock conflicts of high priority wsrep threads, which do
secondary index scanning e.g. for duplicate key detection.

The actual fix is the patch in sql_class.cc:thd_need_ordering_with(), where
we allow relaxed GAP locking protocol between wsrep high priority threads.
wsrep high priority threads (replication appliers, replayers and TOI processors)
are ordered by the replication provider, and they will not need serializability
support gained by secondary index GAP locks.

PR contains also a mtr test, which exercises a scenario where two replication
applier threads have a false positive conflict in GAP of unique secondary index.
The conflicting local committing transaction has to replay, and the test verifies
also that the replaying phase will not conflict with the latter repllication applier.
Commit also contains new test scenario for galera.galera_UK_conflict.test,
where replayer starts applying after a slave applier thread, with later seqno,
has advanced to commit phase. The applier and replayer have false positive GAP
lock conflict on secondary unique index, and replayer should ignore this.
This test scenario caused crash with earlier version in this PR, and to fix this,
the secondary index uniquenes checking has been relaxed even further.

Now innodb trx_t structure has new member: bool wsrep_UK_scan, which is set to
true, when high priority thread is performing unique secondary index scanning.
The member trx_t::wsrep_UK_scan is defined inside WITH_WSREP directive, to make
it possible to prepare a MariaDB build where this additional trx_t member is
not present and is not used in the code base. trx->wsrep_UK_scan is set to true
only for the duration of function call for: lock_rec_lock() trx->wsrep_UK_scan
is used only in lock_rec_has_to_wait() function to relax the need to wait if
wsrep_UK_scan is set and conflicting transaction is also high priority.

Reviewed-by: Jan Lindström <jan.lindstrom@mariadb.com>
This commit is contained in:
sjaakola 2020-12-09 21:53:18 +02:00 committed by Jan Lindström
commit beaea31ab1
9 changed files with 349 additions and 10 deletions

View file

@ -3,7 +3,7 @@
Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
Copyright (c) 2012, Facebook Inc.
Copyright (c) 2015, 2020, MariaDB Corporation.
Copyright (c) 2015, 2021, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
@ -67,6 +67,9 @@ Created 10/16/1994 Heikki Tuuri
#include "srv0start.h"
#include "mysql_com.h"
#include "dict0stats.h"
#ifdef WITH_WSREP
#include "mysql/service_wsrep.h"
#endif /* WITH_WSREP */
/** Buffered B-tree operation types, introduced as part of delete buffering. */
enum btr_op_t {
@ -2941,7 +2944,8 @@ btr_cur_ins_lock_and_undo(
/* Check if there is predicate or GAP lock preventing the insertion */
if (!(flags & BTR_NO_LOCKING_FLAG)) {
if (dict_index_is_spatial(index)) {
const unsigned type = index->type;
if (UNIV_UNLIKELY(type & DICT_SPATIAL)) {
lock_prdt_t prdt;
rtr_mbr_t mbr;
@ -2958,9 +2962,30 @@ btr_cur_ins_lock_and_undo(
index, thr, mtr, &prdt);
*inherit = false;
} else {
#ifdef WITH_WSREP
trx_t* trx= thr_get_trx(thr);
/* If transaction scanning an unique secondary
key is wsrep high priority thread (brute
force) this scanning may involve GAP-locking
in the index. As this locking happens also
when applying replication events in high
priority applier threads, there is a
probability for lock conflicts between two
wsrep high priority threads. To avoid this
GAP-locking we mark that this transaction
is using unique key scan here. */
if ((type & (DICT_CLUSTERED | DICT_UNIQUE)) == DICT_UNIQUE
&& trx->is_wsrep()
&& wsrep_thd_is_BF(trx->mysql_thd, false)) {
trx->wsrep_UK_scan= true;
}
#endif /* WITH_WSREP */
err = lock_rec_insert_check_and_lock(
flags, rec, btr_cur_get_block(cursor),
index, thr, mtr, inherit);
#ifdef WITH_WSREP
trx->wsrep_UK_scan= false;
#endif /* WITH_WSREP */
}
}