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( ...@@ -117,6 +117,7 @@ que_thr_stop(
/************************************************************************** /**************************************************************************
Moves a thread from another state to the QUE_THR_RUNNING state. Increments Moves a thread from another state to the QUE_THR_RUNNING state. Increments
the n_active_thrs counters of the query graph and transaction. */ the n_active_thrs counters of the query graph and transaction. */
void void
que_thr_move_to_run_state_for_mysql( que_thr_move_to_run_state_for_mysql(
/*================================*/ /*================================*/
...@@ -125,14 +126,17 @@ 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 A patch for MySQL used to 'stop' a dummy query thread used in MySQL
select, when there is no error or lock wait. */ select, when there is no error or lock wait. */
void void
que_thr_stop_for_mysql_no_error( que_thr_stop_for_mysql_no_error(
/*============================*/ /*============================*/
que_thr_t* thr, /* in: query thread */ que_thr_t* thr, /* in: query thread */
trx_t* trx); /* in: transaction */ trx_t* trx); /* in: transaction */
/************************************************************************** /**************************************************************************
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
select. */ 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 void
que_thr_stop_for_mysql( que_thr_stop_for_mysql(
......
...@@ -429,7 +429,10 @@ struct trx_struct{ ...@@ -429,7 +429,10 @@ struct trx_struct{
MySQL */ MySQL */
/*------------------------------*/ /*------------------------------*/
ulint error_state; /* 0 if no error, otherwise error 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 void* error_info; /* if the error number indicates a
duplicate key error, a pointer to duplicate key error, a pointer to
the problematic index is stored here */ the problematic index is stored here */
...@@ -466,6 +469,12 @@ struct trx_struct{ ...@@ -466,6 +469,12 @@ struct trx_struct{
TRX_QUE_LOCK_WAIT, this points to TRX_QUE_LOCK_WAIT, this points to
the lock request, otherwise this is the lock request, otherwise this is
NULL */ 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 */ time_t wait_started; /* lock wait started at this time */
UT_LIST_BASE_NODE_T(que_thr_t) UT_LIST_BASE_NODE_T(que_thr_t)
wait_thrs; /* query threads belonging to this wait_thrs; /* query threads belonging to this
......
...@@ -10,7 +10,6 @@ Created 1/20/1994 Heikki Tuuri ...@@ -10,7 +10,6 @@ Created 1/20/1994 Heikki Tuuri
#define ut0ut_h #define ut0ut_h
#include "univ.i" #include "univ.i"
#include <string.h>
#include <time.h> #include <time.h>
#ifndef MYSQL_SERVER #ifndef MYSQL_SERVER
#include <ctype.h> #include <ctype.h>
......
...@@ -1727,6 +1727,7 @@ index->table_name); ...@@ -1727,6 +1727,7 @@ index->table_name);
} }
trx->que_state = TRX_QUE_LOCK_WAIT; trx->que_state = TRX_QUE_LOCK_WAIT;
trx->was_chosen_as_deadlock_victim = FALSE;
trx->wait_started = time(NULL); trx->wait_started = time(NULL);
ut_a(que_thr_stop(thr)); ut_a(que_thr_stop(thr));
...@@ -3173,7 +3174,8 @@ lock_deadlock_recursive( ...@@ -3173,7 +3174,8 @@ lock_deadlock_recursive(
err_buf += sprintf(err_buf, err_buf += sprintf(err_buf,
"*** WE ROLL BACK TRANSACTION (1)\n"); "*** 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); lock_cancel_waiting_and_release(wait_lock);
...@@ -3353,6 +3355,7 @@ table->name); ...@@ -3353,6 +3355,7 @@ table->name);
} }
trx->que_state = TRX_QUE_LOCK_WAIT; trx->que_state = TRX_QUE_LOCK_WAIT;
trx->was_chosen_as_deadlock_victim = FALSE;
trx->wait_started = time(NULL); trx->wait_started = time(NULL);
ut_a(que_thr_stop(thr)); ut_a(que_thr_stop(thr));
......
...@@ -1654,8 +1654,8 @@ log_reset_first_header_and_checkpoint( ...@@ -1654,8 +1654,8 @@ log_reset_first_header_and_checkpoint(
lsn = ut_dulint_add(start, LOG_BLOCK_HDR_SIZE); lsn = ut_dulint_add(start, LOG_BLOCK_HDR_SIZE);
/* Write the label of ibbackup --restore */ /* Write the label of ibbackup --restore */
sprintf((char*) hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, "ibbackup "); sprintf(hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, "ibbackup ");
ut_sprintf_timestamp((char*) hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP ut_sprintf_timestamp(hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP
+ strlen("ibbackup ")); + strlen("ibbackup "));
buf = hdr_buf + LOG_CHECKPOINT_1; buf = hdr_buf + LOG_CHECKPOINT_1;
......
...@@ -1046,14 +1046,16 @@ que_thr_stop( ...@@ -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 void
que_thr_stop_for_mysql( que_thr_stop_for_mysql(
/*===================*/ /*===================*/
que_thr_t* thr) /* in: query thread */ que_thr_t* thr) /* in: query thread */
{ {
ibool stopped = FALSE;
trx_t* trx; trx_t* trx;
trx = thr_get_trx(thr); trx = thr_get_trx(thr);
...@@ -1067,13 +1069,10 @@ que_thr_stop_for_mysql( ...@@ -1067,13 +1069,10 @@ que_thr_stop_for_mysql(
/* Error handling built for the MySQL interface */ /* Error handling built for the MySQL interface */
thr->state = QUE_THR_COMPLETED; thr->state = QUE_THR_COMPLETED;
} else {
stopped = TRUE; /* 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 */
if (!stopped) {
/* It must have been a lock wait but the
lock was already released */
mutex_exit(&kernel_mutex); mutex_exit(&kernel_mutex);
...@@ -1081,6 +1080,10 @@ que_thr_stop_for_mysql( ...@@ -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->is_active = FALSE;
(thr->graph)->n_active_thrs--; (thr->graph)->n_active_thrs--;
...@@ -1132,6 +1135,9 @@ que_thr_stop_for_mysql_no_error( ...@@ -1132,6 +1135,9 @@ que_thr_stop_for_mysql_no_error(
trx_t* trx) /* in: transaction */ trx_t* trx) /* in: transaction */
{ {
ut_ad(thr->state == QUE_THR_RUNNING); 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) { if (thr->magic_n != QUE_THR_MAGIC_N) {
fprintf(stderr, fprintf(stderr,
......
...@@ -198,8 +198,9 @@ row_mysql_handle_errors( ...@@ -198,8 +198,9 @@ row_mysql_handle_errors(
/* out: TRUE if it was a lock wait and /* out: TRUE if it was a lock wait and
we should continue running the query thread */ we should continue running the query thread */
ulint* new_err,/* out: possible new error encountered in ulint* new_err,/* out: possible new error encountered in
rollback, or the old error which was lock wait, or if no new error, the value
during the function entry */ of trx->error_state at the entry of this
function */
trx_t* trx, /* in: transaction */ trx_t* trx, /* in: transaction */
que_thr_t* thr, /* in: query thread */ que_thr_t* thr, /* in: query thread */
trx_savept_t* savept) /* in: savepoint or NULL */ trx_savept_t* savept) /* in: savepoint or NULL */
...@@ -1010,10 +1011,27 @@ run_again: ...@@ -1010,10 +1011,27 @@ run_again:
err = trx->error_state; 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) { if (err == DB_LOCK_WAIT) {
/* Handle lock wait here */
que_thr_stop_for_mysql(thr); que_thr_stop_for_mysql(thr);
row_mysql_handle_errors(&err, trx, thr, NULL); 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; goto run_again;
} }
......
...@@ -2082,13 +2082,24 @@ srv_suspend_mysql_thread( ...@@ -2082,13 +2082,24 @@ srv_suspend_mysql_thread(
if (thr->state == QUE_THR_RUNNING) { 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); mutex_exit(&kernel_mutex);
return; return;
} }
ut_ad(thr->is_active == FALSE);
slot = srv_table_reserve_slot_for_mysql(); slot = srv_table_reserve_slot_for_mysql();
event = slot->event; event = slot->event;
...@@ -2142,6 +2153,12 @@ srv_suspend_mysql_thread( ...@@ -2142,6 +2153,12 @@ srv_suspend_mysql_thread(
wait_time = ut_difftime(ut_time(), slot->suspend_time); 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); mutex_exit(&kernel_mutex);
if (srv_lock_wait_timeout < 100000000 && if (srv_lock_wait_timeout < 100000000 &&
......
...@@ -474,7 +474,7 @@ trx_sys_update_mysql_binlog_offset( ...@@ -474,7 +474,7 @@ trx_sys_update_mysql_binlog_offset(
mlog_write_string(sys_header + field mlog_write_string(sys_header + field
+ TRX_SYS_MYSQL_LOG_NAME, + 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 if (mach_read_from_4(sys_header + field
......
...@@ -129,6 +129,7 @@ trx_create( ...@@ -129,6 +129,7 @@ trx_create(
trx->graph = NULL; trx->graph = NULL;
trx->wait_lock = NULL; trx->wait_lock = NULL;
trx->was_chosen_as_deadlock_victim = FALSE;
UT_LIST_INIT(trx->wait_thrs); UT_LIST_INIT(trx->wait_thrs);
trx->lock_heap = mem_heap_create_in_buffer(256); 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