summaryrefslogtreecommitdiff
path: root/storage/xtradb/lock/lock0lock.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/xtradb/lock/lock0lock.cc')
-rw-r--r--storage/xtradb/lock/lock0lock.cc256
1 files changed, 253 insertions, 3 deletions
diff --git a/storage/xtradb/lock/lock0lock.cc b/storage/xtradb/lock/lock0lock.cc
index 3e60680882a..559b0398147 100644
--- a/storage/xtradb/lock/lock0lock.cc
+++ b/storage/xtradb/lock/lock0lock.cc
@@ -50,6 +50,12 @@ Created 5/7/1996 Heikki Tuuri
#include "dict0boot.h"
#include <set>
+#ifdef WITH_WSREP
+extern my_bool wsrep_debug;
+extern my_bool wsrep_log_conflicts;
+#include "ha_prototypes.h"
+#endif
+
/* Restricts the length of search we will do in the waits-for
graph of transactions */
#define LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK 1000000
@@ -973,6 +979,15 @@ lock_rec_has_to_wait(
LOCK_MODE_MASK & type_mode),
lock_get_mode(lock2))) {
+#ifdef WITH_WSREP
+ if ((type_mode & WSREP_BF) && (lock2->type_mode & WSREP_BF)) {
+ if (wsrep_debug)
+ fprintf(stderr, "BF locks in conflict: " TRX_ID_FMT " " TRX_ID_FMT "\n",
+ trx->id, lock2->trx->id);
+ // not in return FALSE;
+ }
+#endif /* WITH_WSREP */
+
/* We have somewhat complex rules when gap type record locks
cause waits */
@@ -1520,6 +1535,12 @@ lock_rec_has_expl(
return(NULL);
}
+#ifdef WITH_WSREP
+static
+void
+lock_rec_discard(lock_t* in_lock);
+#endif /* WITH_WSREP */
+
#ifdef UNIV_DEBUG
/*********************************************************************//**
Checks if some other transaction has a lock request in the queue.
@@ -1568,6 +1589,57 @@ lock_rec_other_has_expl_req(
}
#endif /* UNIV_DEBUG */
+#ifdef WITH_WSREP
+static void
+wsrep_kill_victim(trx_t *trx, lock_t *lock) {
+ int bf_this = wsrep_thd_is_brute_force(trx->mysql_thd);
+ int bf_other =
+ wsrep_thd_is_brute_force(lock->trx->mysql_thd);
+ if ((bf_this && !bf_other) ||
+ (bf_this && bf_other && wsrep_trx_order_before(
+ trx->mysql_thd, lock->trx->mysql_thd))) {
+
+ if (lock->trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
+ if (wsrep_debug)
+ fprintf(stderr, "WSREP: BF victim waiting\n");
+ /* cannot release lock, until our lock
+ is in the queue*/
+ } else if (lock->trx != trx) {
+ if (wsrep_log_conflicts) {
+ mutex_enter(&trx_sys->mutex);
+ if (bf_this)
+ fputs("\n*** Priority TRANSACTION:\n",
+ stderr);
+ else
+ fputs("\n*** Victim TRANSACTION:\n",
+ stderr);
+ trx_print_latched(stderr, trx, 3000);
+
+ if (bf_other)
+ fputs("\n*** Priority TRANSACTION:\n",
+ stderr);
+ else
+ fputs("\n*** Victim TRANSACTION:\n",
+ stderr);
+ trx_print_latched(stderr, lock->trx, 3000);
+
+ mutex_exit(&trx_sys->mutex);
+ fputs("*** WAITING FOR THIS LOCK TO BE GRANTED:\n",
+ stderr);
+
+ if (lock_get_type(lock) == LOCK_REC) {
+ lock_rec_print(stderr, lock);
+ } else {
+ lock_table_print(stderr, lock);
+ }
+ }
+ wsrep_innobase_kill_one_trx(trx->mysql_thd,
+ (const trx_t*)trx, lock->trx, TRUE);
+ }
+ }
+}
+#endif /* WITH_WSREP */
+
/*********************************************************************//**
Checks if some other transaction has a conflicting explicit lock request
in the queue, so that we have to wait.
@@ -1597,6 +1669,11 @@ lock_rec_other_has_conflicting(
lock = lock_rec_get_next_const(heap_no, lock)) {
if (lock_rec_has_to_wait(trx, mode, lock, is_supremum)) {
+#ifdef WITH_WSREP
+ trx_mutex_enter(lock->trx);
+ wsrep_kill_victim((trx_t*)trx, (ib_lock_t*)lock);
+ trx_mutex_exit(lock->trx);
+#endif
return(lock);
}
}
@@ -1734,6 +1811,10 @@ static
lock_t*
lock_rec_create(
/*============*/
+#ifdef WITH_WSREP
+ lock_t* const c_lock, /* conflicting lock */
+ que_thr_t* thr,
+#endif
ulint type_mode,/*!< in: lock mode and wait
flag, type is ignored and
replaced by LOCK_REC */
@@ -1787,8 +1868,13 @@ lock_rec_create(
lock->trx = trx;
lock->type_mode = (type_mode & ~LOCK_TYPE_MASK) | LOCK_REC;
- lock->index = index;
+#ifdef WITH_WSREP
+ if (wsrep_thd_is_brute_force(trx->mysql_thd)) {
+ lock->type_mode |= WSREP_BF;
+ }
+#endif /* WITH_WSREP */
+ lock->index = index;
lock->un_member.rec_lock.space = space;
lock->un_member.rec_lock.page_no = page_no;
lock->un_member.rec_lock.n_bits = n_bytes * 8;
@@ -1805,8 +1891,84 @@ lock_rec_create(
ut_ad(index->table->n_ref_count > 0 || !index->table->can_be_evicted);
+#ifdef WITH_WSREP
+ if (c_lock && wsrep_thd_is_brute_force(trx->mysql_thd)) {
+ lock_t *hash = (lock_t *)c_lock->hash;
+ lock_t *prev = NULL;
+
+ while (hash &&
+ wsrep_thd_is_brute_force(
+ ((lock_t *)hash)->trx->mysql_thd) &&
+ wsrep_trx_order_before(
+ ((lock_t *)hash)->trx->mysql_thd,
+ trx->mysql_thd)) {
+ prev = hash;
+ hash = (lock_t *)hash->hash;
+ }
+ lock->hash = hash;
+ if (prev) {
+ prev->hash = lock;
+ } else {
+ c_lock->hash = lock;
+ }
+ /*
+ * delayed conflict resolution '...kill_one_trx' was not called,
+ * if victim was waiting for some other lock
+ */
+ trx_mutex_enter(c_lock->trx);
+ if (c_lock->trx->lock.que_state == TRX_QUE_LOCK_WAIT) {
+
+ c_lock->trx->lock.was_chosen_as_deadlock_victim = TRUE;
+
+ if (wsrep_debug &&
+ c_lock->trx->lock.wait_lock != c_lock) {
+ fprintf(stderr, "WSREP: c_lock != wait lock\n");
+ lock_rec_print(stderr, c_lock);
+ lock_rec_print(stderr, c_lock->trx->lock.wait_lock);
+ }
+
+ trx->lock.que_state = TRX_QUE_LOCK_WAIT;
+ lock_set_lock_and_trx_wait(lock, trx);
+ UT_LIST_ADD_LAST(trx_locks, trx->lock.trx_locks, lock);
+
+ ut_ad(thr != NULL);
+ trx->lock.wait_thr = thr;
+ thr->state = QUE_THR_LOCK_WAIT;
+
+ /* have to release trx mutex for the duration of
+ victim lock release. This will eventually call
+ lock_grant, which wants to grant trx mutex again
+ */
+ if (caller_owns_trx_mutex) trx_mutex_exit(trx);
+ lock_cancel_waiting_and_release(
+ c_lock->trx->lock.wait_lock);
+ if (caller_owns_trx_mutex) trx_mutex_enter(trx);
+
+ /* trx might not wait for c_lock, but some other lock
+ does not matter if wait_lock was released above
+ */
+ if (c_lock->trx->lock.wait_lock == c_lock) {
+ lock_reset_lock_and_trx_wait(lock);
+ }
+ trx_mutex_exit(c_lock->trx);
+
+ if (wsrep_debug) fprintf(
+ stderr,
+ "WSREP: c_lock canceled %llu\n",
+ (ulonglong) c_lock->trx->id);
+
+ /* have to bail out here to avoid lock_set_lock... */
+ return(lock);
+ }
+ trx_mutex_exit(c_lock->trx);
+ } else {
+ HASH_INSERT(lock_t, hash, lock_sys->rec_hash,
+ lock_rec_fold(space, page_no), lock);
+ }
+#else
HASH_INSERT(lock_t, hash, lock_sys->rec_hash,
lock_rec_fold(space, page_no), lock);
+#endif /* WITH_WSREP */
lock_sys->rec_num++;
@@ -1843,6 +2005,9 @@ static
dberr_t
lock_rec_enqueue_waiting(
/*=====================*/
+#ifdef WITH_WSREP
+ lock_t* c_lock, /* conflicting lock */
+#endif
ulint type_mode,/*!< in: lock mode this
transaction is requesting:
LOCK_S or LOCK_X, possibly
@@ -1905,9 +2070,20 @@ lock_rec_enqueue_waiting(
/* Enqueue the lock request that will wait
to be granted, note that we already own
the trx mutex. */
+#ifdef WITH_WSREP
+ if (wsrep_on(trx->mysql_thd) &&
+ trx->lock.was_chosen_as_deadlock_victim) {
+ return(DB_DEADLOCK);
+ }
+ lock = lock_rec_create(
+ c_lock, thr,
+ type_mode | LOCK_WAIT, block, heap_no,
+ index, trx, TRUE);
+#else
lock = lock_rec_create(
type_mode | LOCK_WAIT, block, heap_no,
index, trx, TRUE);
+#endif /* WITH_WSREP */
} else {
ut_ad(lock->type_mode & LOCK_WAIT);
ut_ad(lock->type_mode & LOCK_CONV_BY_OTHER);
@@ -2019,7 +2195,19 @@ lock_rec_add_to_queue(
const lock_t* other_lock
= lock_rec_other_has_expl_req(mode, 0, LOCK_WAIT,
block, heap_no, trx);
+#ifdef WITH_WSREP
+ /* this can potentionally assert with wsrep */
+ if (wsrep_on(trx->mysql_thd)) {
+ if (wsrep_debug && other_lock) {
+ fprintf(stderr,
+ "WSREP: InnoDB assert ignored\n");
+ }
+ } else {
+ ut_a(!other_lock);
+ }
+#else
ut_a(!other_lock);
+#endif /* WITH_WSREP */
}
#endif /* UNIV_DEBUG */
@@ -2070,9 +2258,15 @@ lock_rec_add_to_queue(
}
somebody_waits:
+#ifdef WITH_WSREP
+ return(lock_rec_create(NULL, NULL,
+ type_mode, block, heap_no, index, trx,
+ caller_owns_trx_mutex));
+#else
return(lock_rec_create(
type_mode, block, heap_no, index, trx,
caller_owns_trx_mutex));
+#endif /* WITH_WSREP */
}
/** Record locking request status */
@@ -2123,6 +2317,11 @@ lock_rec_lock_fast(
|| (LOCK_MODE_MASK & mode) == LOCK_X);
ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP
|| mode - (LOCK_MODE_MASK & mode) == 0
+#ifdef WITH_WSREP
+ || mode - (LOCK_MODE_MASK & mode) - WSREP_BF == 0
+ || mode - (LOCK_MODE_MASK & mode) - WSREP_BF == LOCK_GAP
+ || mode - (LOCK_MODE_MASK & mode) - WSREP_BF == LOCK_REC_NOT_GAP
+#endif /* WITH_WSREP */
|| mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP);
ut_ad(dict_index_is_clust(index) || !dict_index_is_online_ddl(index));
@@ -2135,9 +2334,13 @@ lock_rec_lock_fast(
if (lock == NULL) {
if (!impl) {
/* Note that we don't own the trx mutex. */
+#ifdef WITH_WSREP
+ lock = lock_rec_create(NULL, thr,
+ mode, block, heap_no, index, trx, FALSE);
+#else
lock = lock_rec_create(
mode, block, heap_no, index, trx, FALSE);
-
+#endif /* WITH_WSREP */
}
status = LOCK_REC_SUCCESS_CREATED;
} else {
@@ -2192,6 +2395,9 @@ lock_rec_lock_slow(
trx_t* trx;
lock_t* lock;
dberr_t err = DB_SUCCESS;
+#ifdef WITH_WSREP
+ lock_t* c_lock = NULL;
+#endif
ut_ad(lock_mutex_own());
ut_ad((LOCK_MODE_MASK & mode) != LOCK_S
@@ -2202,6 +2408,11 @@ lock_rec_lock_slow(
|| (LOCK_MODE_MASK & mode) == LOCK_X);
ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP
|| mode - (LOCK_MODE_MASK & mode) == 0
+#ifdef WITH_WSREP
+ || mode - (LOCK_MODE_MASK & mode) - WSREP_BF == 0
+ || mode - (LOCK_MODE_MASK & mode) - WSREP_BF == LOCK_GAP
+ || mode - (LOCK_MODE_MASK & mode) - WSREP_BF == LOCK_REC_NOT_GAP
+#endif /* WITH_WSREP */
|| mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP);
ut_ad(dict_index_is_clust(index) || !dict_index_is_online_ddl(index));
@@ -2234,9 +2445,15 @@ lock_rec_lock_slow(
/* The trx already has a strong enough lock on rec: do
nothing */
+#ifdef WITH_WSREP
+ } else if ((c_lock = (lock_t *)lock_rec_other_has_conflicting(
+ static_cast<enum lock_mode>(mode),
+ block, heap_no, trx))) {
+#else
} else if (lock_rec_other_has_conflicting(
static_cast<enum lock_mode>(mode),
block, heap_no, trx)) {
+#endif /* WITH_WSREP */
/* If another transaction has a non-gap conflicting
request in the queue, as this transaction does not
@@ -2245,9 +2462,16 @@ lock_rec_lock_slow(
ut_ad(lock == NULL);
enqueue_waiting:
+#ifdef WITH_WSREP
+ /* c_lock is NULL here if jump to enqueue_waiting happened
+ but it's ok because lock is not NULL in that case and c_lock
+ is not used. */
+ err = lock_rec_enqueue_waiting(c_lock,
+ mode, block, heap_no, lock, index, thr);
+#else
err = lock_rec_enqueue_waiting(
mode, block, heap_no, lock, index, thr);
-
+#endif /* WITH_WSREP */
} else if (!impl) {
/* Set the requested lock on the record, note that
we already own the transaction mutex. */
@@ -2297,6 +2521,11 @@ lock_rec_lock(
|| (LOCK_MODE_MASK & mode) == LOCK_X);
ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP
|| mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP
+#ifdef WITH_WSREP
+ || mode - (LOCK_MODE_MASK & mode) - WSREP_BF == 0
+ || mode - (LOCK_MODE_MASK & mode) - WSREP_BF == LOCK_GAP
+ || mode - (LOCK_MODE_MASK & mode) - WSREP_BF == LOCK_REC_NOT_GAP
+#endif /* WITH_WSREP */
|| mode - (LOCK_MODE_MASK & mode) == 0);
ut_ad(dict_index_is_clust(index) || !dict_index_is_online_ddl(index));
@@ -3744,6 +3973,11 @@ lock_deadlock_select_victim(
/* The joining transaction is 'smaller',
choose it as the victim and roll it back. */
+#ifdef WITH_WSREP
+ if (wsrep_thd_is_brute_force(ctx->start->mysql_thd))
+ return(ctx->wait_lock->trx);
+ else
+#endif /* WITH_WSREP */
return(ctx->start);
}
@@ -5930,6 +6164,9 @@ lock_rec_insert_check_and_lock(
lock_t* lock;
dberr_t err;
ulint next_rec_heap_no;
+#ifdef WITH_WSREP
+ lock_t* c_lock=NULL;
+#endif
ut_ad(block->frame == page_align(rec));
ut_ad(!dict_index_is_online_ddl(index)
@@ -5991,17 +6228,30 @@ lock_rec_insert_check_and_lock(
had to wait for their insert. Both had waiting gap type lock requests
on the successor, which produced an unnecessary deadlock. */
+#ifdef WITH_WSREP
+ if ((c_lock = (ib_lock_t*)lock_rec_other_has_conflicting(
+ static_cast<enum lock_mode>(
+ LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION | WSREP_BF),
+ block, next_rec_heap_no, trx))) {
+#else
if (lock_rec_other_has_conflicting(
static_cast<enum lock_mode>(
LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION),
block, next_rec_heap_no, trx)) {
+#endif
/* Note that we may get DB_SUCCESS also here! */
trx_mutex_enter(trx);
+#ifdef WITH_WSREP
+ err = lock_rec_enqueue_waiting(c_lock,
+ LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION,
+ block, next_rec_heap_no, NULL, index, thr);
+#else
err = lock_rec_enqueue_waiting(
LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION,
block, next_rec_heap_no, NULL, index, thr);
+#endif
trx_mutex_exit(trx);
} else {