Many files:

  Fix hang introduced by selective deadlock resolution
srv0srv.c, row0mysql.c:
  Fix hang introduced by selective deadlock resolution + corruption caused by lock timeout or sel deadl res in ON DELETE CASCADE
parent aa4e1658
......@@ -117,6 +117,7 @@ que_thr_stop(
/**************************************************************************
Moves a thread from another state to the QUE_THR_RUNNING state. Increments
the n_active_thrs counters of the query graph and transaction. */
void
que_thr_move_to_run_state_for_mysql(
/*================================*/
......@@ -125,14 +126,17 @@ que_thr_move_to_run_state_for_mysql(
/**************************************************************************
A patch for MySQL used to 'stop' a dummy query thread used in MySQL
select, when there is no error or lock wait. */
void
que_thr_stop_for_mysql_no_error(
/*============================*/
que_thr_t* thr, /* in: query thread */
trx_t* trx); /* in: transaction */
/**************************************************************************
A patch for MySQL used to 'stop' a dummy query thread used in MySQL
select. */
A patch for MySQL used to 'stop' a dummy query thread used in MySQL. The
query thread is stopped and made inactive, except in the case where
it was put to the lock wait state in lock0lock.c, but the lock has already
been granted or the transaction chosen as a victim in deadlock resolution. */
void
que_thr_stop_for_mysql(
......
......@@ -429,7 +429,10 @@ struct trx_struct{
MySQL */
/*------------------------------*/
ulint error_state; /* 0 if no error, otherwise error
number */
number; NOTE That ONLY the thread
doing the transaction is allowed to
set this field: this is NOT protected
by the kernel mutex */
void* error_info; /* if the error number indicates a
duplicate key error, a pointer to
the problematic index is stored here */
......@@ -466,6 +469,12 @@ struct trx_struct{
TRX_QUE_LOCK_WAIT, this points to
the lock request, otherwise this is
NULL */
ibool was_chosen_as_deadlock_victim;
/* when the transaction decides to wait
for a lock, this it sets this to FALSE;
if another transaction chooses this
transaction as a victim in deadlock
resolution, it sets this to TRUE */
time_t wait_started; /* lock wait started at this time */
UT_LIST_BASE_NODE_T(que_thr_t)
wait_thrs; /* query threads belonging to this
......
......@@ -10,7 +10,6 @@ Created 1/20/1994 Heikki Tuuri
#define ut0ut_h
#include "univ.i"
#include <string.h>
#include <time.h>
#ifndef MYSQL_SERVER
#include <ctype.h>
......
......@@ -1727,6 +1727,7 @@ index->table_name);
}
trx->que_state = TRX_QUE_LOCK_WAIT;
trx->was_chosen_as_deadlock_victim = FALSE;
trx->wait_started = time(NULL);
ut_a(que_thr_stop(thr));
......@@ -3173,7 +3174,8 @@ lock_deadlock_recursive(
err_buf += sprintf(err_buf,
"*** WE ROLL BACK TRANSACTION (1)\n");
wait_lock->trx->error_state = DB_DEADLOCK;
wait_lock->trx->was_chosen_as_deadlock_victim
= TRUE;
lock_cancel_waiting_and_release(wait_lock);
......@@ -3353,6 +3355,7 @@ table->name);
}
trx->que_state = TRX_QUE_LOCK_WAIT;
trx->was_chosen_as_deadlock_victim = FALSE;
trx->wait_started = time(NULL);
ut_a(que_thr_stop(thr));
......
......@@ -1654,8 +1654,8 @@ log_reset_first_header_and_checkpoint(
lsn = ut_dulint_add(start, LOG_BLOCK_HDR_SIZE);
/* Write the label of ibbackup --restore */
sprintf((char*) hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, "ibbackup ");
ut_sprintf_timestamp((char*) hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP
sprintf(hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, "ibbackup ");
ut_sprintf_timestamp(hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP
+ strlen("ibbackup "));
buf = hdr_buf + LOG_CHECKPOINT_1;
......
......@@ -1046,14 +1046,16 @@ que_thr_stop(
}
/**************************************************************************
A patch for MySQL used to 'stop' a dummy query thread used in MySQL. */
A patch for MySQL used to 'stop' a dummy query thread used in MySQL. The
query thread is stopped and made inactive, except in the case where
it was put to the lock wait state in lock0lock.c, but the lock has already
been granted or the transaction chosen as a victim in deadlock resolution. */
void
que_thr_stop_for_mysql(
/*===================*/
que_thr_t* thr) /* in: query thread */
{
ibool stopped = FALSE;
trx_t* trx;
trx = thr_get_trx(thr);
......@@ -1067,13 +1069,10 @@ que_thr_stop_for_mysql(
/* Error handling built for the MySQL interface */
thr->state = QUE_THR_COMPLETED;
stopped = TRUE;
}
if (!stopped) {
/* It must have been a lock wait but the
lock was already released */
} else {
/* It must have been a lock wait but the lock was
already released, or this transaction was chosen
as a victim in selective deadlock resolution */
mutex_exit(&kernel_mutex);
......@@ -1081,6 +1080,10 @@ que_thr_stop_for_mysql(
}
}
ut_ad(thr->is_active == TRUE);
ut_ad(trx->n_active_thrs == 1);
ut_ad(thr->graph->n_active_thrs == 1);
thr->is_active = FALSE;
(thr->graph)->n_active_thrs--;
......@@ -1132,6 +1135,9 @@ que_thr_stop_for_mysql_no_error(
trx_t* trx) /* in: transaction */
{
ut_ad(thr->state == QUE_THR_RUNNING);
ut_ad(thr->is_active == TRUE);
ut_ad(trx->n_active_thrs == 1);
ut_ad(thr->graph->n_active_thrs == 1);
if (thr->magic_n != QUE_THR_MAGIC_N) {
fprintf(stderr,
......
......@@ -198,8 +198,9 @@ row_mysql_handle_errors(
/* out: TRUE if it was a lock wait and
we should continue running the query thread */
ulint* new_err,/* out: possible new error encountered in
rollback, or the old error which was
during the function entry */
lock wait, or if no new error, the value
of trx->error_state at the entry of this
function */
trx_t* trx, /* in: transaction */
que_thr_t* thr, /* in: query thread */
trx_savept_t* savept) /* in: savepoint or NULL */
......@@ -998,8 +999,8 @@ row_update_cascade_for_mysql(
or set null operation */
dict_table_t* table) /* in: table where we do the operation */
{
ulint err;
trx_t* trx;
ulint err;
trx_t* trx;
trx = thr_get_trx(thr);
run_again:
......@@ -1010,11 +1011,28 @@ row_update_cascade_for_mysql(
err = trx->error_state;
/* Note that the cascade node is a subnode of another InnoDB
query graph node. We do a normal lock wait in this node, but
all errors are handled by the parent node. */
if (err == DB_LOCK_WAIT) {
que_thr_stop_for_mysql(thr);
/* Handle lock wait here */
row_mysql_handle_errors(&err, trx, thr, NULL);
que_thr_stop_for_mysql(thr);
srv_suspend_mysql_thread(thr);
/* Note that a lock wait may also end in a lock wait timeout,
or this transaction is picked as a victim in selective
deadlock resolution */
if (trx->error_state != DB_SUCCESS) {
return(trx->error_state);
}
/* Retry operation after a normal lock wait */
goto run_again;
}
......
......@@ -2082,13 +2082,24 @@ srv_suspend_mysql_thread(
if (thr->state == QUE_THR_RUNNING) {
/* The lock has already been released: no need to suspend */
ut_ad(thr->is_active == TRUE);
/* The lock has already been released or this transaction
was chosen as a deadlock victim: no need to suspend */
if (trx->was_chosen_as_deadlock_victim) {
trx->error_state = DB_DEADLOCK;
trx->was_chosen_as_deadlock_victim = FALSE;
}
mutex_exit(&kernel_mutex);
return;
}
ut_ad(thr->is_active == FALSE);
slot = srv_table_reserve_slot_for_mysql();
event = slot->event;
......@@ -2142,6 +2153,12 @@ srv_suspend_mysql_thread(
wait_time = ut_difftime(ut_time(), slot->suspend_time);
if (trx->was_chosen_as_deadlock_victim) {
trx->error_state = DB_DEADLOCK;
trx->was_chosen_as_deadlock_victim = FALSE;
}
mutex_exit(&kernel_mutex);
if (srv_lock_wait_timeout < 100000000 &&
......
......@@ -474,7 +474,7 @@ trx_sys_update_mysql_binlog_offset(
mlog_write_string(sys_header + field
+ TRX_SYS_MYSQL_LOG_NAME,
(byte*) file_name, 1 + ut_strlen(file_name), mtr);
file_name, 1 + ut_strlen(file_name), mtr);
}
if (mach_read_from_4(sys_header + field
......
......@@ -129,6 +129,7 @@ trx_create(
trx->graph = NULL;
trx->wait_lock = NULL;
trx->was_chosen_as_deadlock_victim = FALSE;
UT_LIST_INIT(trx->wait_thrs);
trx->lock_heap = mem_heap_create_in_buffer(256);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment