mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Bug#59354 : Bug #12659252 : ASSERT !OTHER_LOCK AT LOCK_REC_ADD_TO_QUEUE DURING A DELETE OPERATION
The converted implicit lock should wait for the prior conflicting lock if found. rb://1437 approved by Marko
This commit is contained in:
@ -498,6 +498,8 @@ static SHOW_VAR innodb_status_variables[]= {
|
||||
#ifdef UNIV_DEBUG
|
||||
{"purge_trx_id_age",
|
||||
(char*) &export_vars.innodb_purge_trx_id_age, SHOW_LONG},
|
||||
{"purge_view_trx_id_age",
|
||||
(char*) &export_vars.innodb_purge_view_trx_id_age, SHOW_LONG},
|
||||
#endif /* UNIV_DEBUG */
|
||||
{NullS, NullS, SHOW_LONG}
|
||||
};
|
||||
@ -9283,6 +9285,13 @@ static MYSQL_SYSVAR_UINT(limit_optimistic_insert_debug,
|
||||
btr_cur_limit_optimistic_insert_debug, PLUGIN_VAR_RQCMDARG,
|
||||
"Artificially limit the number of records per B-tree page (0=unlimited).",
|
||||
NULL, NULL, 0, 0, UINT_MAX32, 0);
|
||||
|
||||
static MYSQL_SYSVAR_BOOL(trx_purge_view_update_only_debug,
|
||||
srv_purge_view_update_only_debug, PLUGIN_VAR_NOCMDARG,
|
||||
"Pause actual purging any delete-marked records, but merely update the purge view. "
|
||||
"It is to create artificially the situation the purge view have been updated "
|
||||
"but the each purges were not done yet.",
|
||||
NULL, NULL, FALSE);
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
static struct st_mysql_sys_var* innobase_system_variables[]= {
|
||||
@ -9333,6 +9342,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
|
||||
#ifdef UNIV_DEBUG
|
||||
MYSQL_SYSVAR(trx_rseg_n_slots_debug),
|
||||
MYSQL_SYSVAR(limit_optimistic_insert_debug),
|
||||
MYSQL_SYSVAR(trx_purge_view_update_only_debug),
|
||||
#endif /* UNIV_DEBUG */
|
||||
NULL
|
||||
};
|
||||
|
@ -685,6 +685,13 @@ extern lock_sys_t* lock_sys;
|
||||
remains set when the waiting lock is granted,
|
||||
or if the lock is inherited to a neighboring
|
||||
record */
|
||||
#define LOCK_CONV_BY_OTHER 4096 /* this bit is set when the lock is created
|
||||
by other transaction */
|
||||
/* Checks if this is a waiting lock created by lock->trx itself.
|
||||
@param type_mode lock->type_mode
|
||||
@return whether it is a waiting lock belonging to lock->trx */
|
||||
#define lock_is_wait_not_by_other(type_mode) \
|
||||
((type_mode & (LOCK_CONV_BY_OTHER | LOCK_WAIT)) == LOCK_WAIT)
|
||||
|
||||
/* When lock bits are reset, the following flags are available: */
|
||||
#define LOCK_RELEASE_WAIT 1
|
||||
|
@ -176,6 +176,10 @@ extern ulint srv_fatal_semaphore_wait_threshold;
|
||||
#define SRV_SEMAPHORE_WAIT_EXTENSION 7200
|
||||
extern ulint srv_dml_needed_delay;
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
extern my_bool srv_purge_view_update_only_debug;
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
extern mutex_t* kernel_mutex_temp;/* mutex protecting the server, trx structs,
|
||||
query threads, and lock table: we allocate
|
||||
it from dynamic memory to get it to the
|
||||
@ -571,6 +575,7 @@ struct export_var_struct{
|
||||
ulint innodb_rows_deleted;
|
||||
#ifdef UNIV_DEBUG
|
||||
ulint innodb_purge_trx_id_age;
|
||||
ulint innodb_purge_view_trx_id_age;
|
||||
#endif /* UNIV_DEBUG */
|
||||
};
|
||||
|
||||
|
@ -725,12 +725,16 @@ lock_reset_lock_and_trx_wait(
|
||||
/*=========================*/
|
||||
lock_t* lock) /* in: record lock */
|
||||
{
|
||||
ut_ad((lock->trx)->wait_lock == lock);
|
||||
ut_ad(lock_get_wait(lock));
|
||||
|
||||
/* Reset the back pointer in trx to this waiting lock request */
|
||||
|
||||
if (!(lock->type_mode & LOCK_CONV_BY_OTHER)) {
|
||||
ut_ad((lock->trx)->wait_lock == lock);
|
||||
(lock->trx)->wait_lock = NULL;
|
||||
} else {
|
||||
ut_ad(lock_get_type(lock) == LOCK_REC);
|
||||
}
|
||||
lock->type_mode = lock->type_mode & ~LOCK_WAIT;
|
||||
}
|
||||
|
||||
@ -1437,9 +1441,9 @@ lock_rec_has_expl(
|
||||
|
||||
while (lock) {
|
||||
if (lock->trx == trx
|
||||
&& !lock_is_wait_not_by_other(lock->type_mode)
|
||||
&& lock_mode_stronger_or_eq(lock_get_mode(lock),
|
||||
precise_mode & LOCK_MODE_MASK)
|
||||
&& !lock_get_wait(lock)
|
||||
&& (!lock_rec_get_rec_not_gap(lock)
|
||||
|| (precise_mode & LOCK_REC_NOT_GAP)
|
||||
|| page_rec_is_supremum(rec))
|
||||
@ -1723,7 +1727,7 @@ lock_rec_create(
|
||||
|
||||
HASH_INSERT(lock_t, hash, lock_sys->rec_hash,
|
||||
lock_rec_fold(space, page_no), lock);
|
||||
if (type_mode & LOCK_WAIT) {
|
||||
if (lock_is_wait_not_by_other(type_mode)) {
|
||||
|
||||
lock_set_lock_and_trx_wait(lock, trx);
|
||||
}
|
||||
@ -1752,10 +1756,11 @@ lock_rec_enqueue_waiting(
|
||||
lock request is set when performing an
|
||||
insert of an index record */
|
||||
rec_t* rec, /* in: record */
|
||||
lock_t* lock, /* in: lock object; NULL if a new
|
||||
one should be created. */
|
||||
dict_index_t* index, /* in: index of record */
|
||||
que_thr_t* thr) /* in: query thread */
|
||||
{
|
||||
lock_t* lock;
|
||||
trx_t* trx;
|
||||
|
||||
ut_ad(mutex_own(&kernel_mutex));
|
||||
@ -1785,8 +1790,16 @@ lock_rec_enqueue_waiting(
|
||||
stderr);
|
||||
}
|
||||
|
||||
if (lock == NULL) {
|
||||
/* Enqueue the lock request that will wait to be granted */
|
||||
lock = lock_rec_create(type_mode | LOCK_WAIT, rec, index, trx);
|
||||
} else {
|
||||
ut_ad(lock->type_mode & LOCK_WAIT);
|
||||
ut_ad(lock->type_mode & LOCK_CONV_BY_OTHER);
|
||||
|
||||
lock->type_mode &= ~LOCK_CONV_BY_OTHER;
|
||||
lock_set_lock_and_trx_wait(lock, trx);
|
||||
}
|
||||
|
||||
/* Check if a deadlock occurs: if yes, remove the lock request and
|
||||
return an error code */
|
||||
@ -2011,6 +2024,7 @@ lock_rec_lock_slow(
|
||||
que_thr_t* thr) /* in: query thread */
|
||||
{
|
||||
trx_t* trx;
|
||||
lock_t* lock;
|
||||
|
||||
ut_ad(mutex_own(&kernel_mutex));
|
||||
ut_ad((LOCK_MODE_MASK & mode) != LOCK_S
|
||||
@ -2025,7 +2039,27 @@ lock_rec_lock_slow(
|
||||
|
||||
trx = thr_get_trx(thr);
|
||||
|
||||
if (lock_rec_has_expl(mode, rec, trx)) {
|
||||
lock = lock_rec_has_expl(mode, rec, trx);
|
||||
if (lock) {
|
||||
if (lock->type_mode & LOCK_CONV_BY_OTHER) {
|
||||
/* This lock or lock waiting was created by the other
|
||||
transaction, not by the transaction (trx) itself.
|
||||
So, the transaction (trx) should treat it collectly
|
||||
according as whether granted or not. */
|
||||
|
||||
if (lock->type_mode & LOCK_WAIT) {
|
||||
/* This lock request was not granted yet.
|
||||
Should wait for granted. */
|
||||
|
||||
goto enqueue_waiting;
|
||||
} else {
|
||||
/* This lock request was already granted.
|
||||
Just clearing the flag. */
|
||||
|
||||
lock->type_mode &= ~LOCK_CONV_BY_OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
/* The trx already has a strong enough lock on rec: do
|
||||
nothing */
|
||||
|
||||
@ -2035,7 +2069,9 @@ lock_rec_lock_slow(
|
||||
the queue, as this transaction does not have a lock strong
|
||||
enough already granted on the record, we have to wait. */
|
||||
|
||||
return(lock_rec_enqueue_waiting(mode, rec, index, thr));
|
||||
ut_ad(lock == NULL);
|
||||
enqueue_waiting:
|
||||
return(lock_rec_enqueue_waiting(mode, rec, lock, index, thr));
|
||||
} else if (!impl) {
|
||||
/* Set the requested lock on the record */
|
||||
|
||||
@ -2171,7 +2207,8 @@ lock_grant(
|
||||
TRX_QUE_LOCK_WAIT state, and there is no need to end the lock wait
|
||||
for it */
|
||||
|
||||
if (lock->trx->que_state == TRX_QUE_LOCK_WAIT) {
|
||||
if (!(lock->type_mode & LOCK_CONV_BY_OTHER)
|
||||
&& lock->trx->que_state == TRX_QUE_LOCK_WAIT) {
|
||||
trx_end_lock_wait(lock->trx);
|
||||
}
|
||||
}
|
||||
@ -2188,6 +2225,7 @@ lock_rec_cancel(
|
||||
{
|
||||
ut_ad(mutex_own(&kernel_mutex));
|
||||
ut_ad(lock_get_type(lock) == LOCK_REC);
|
||||
ut_ad(!(lock->type_mode & LOCK_CONV_BY_OTHER));
|
||||
|
||||
/* Reset the bit (there can be only one set bit) in the lock bitmap */
|
||||
lock_rec_reset_nth_bit(lock, lock_rec_find_set_bit(lock));
|
||||
@ -2331,8 +2369,12 @@ lock_rec_reset_and_release_wait(
|
||||
lock = lock_rec_get_first(rec);
|
||||
|
||||
while (lock != NULL) {
|
||||
if (lock_get_wait(lock)) {
|
||||
if (lock_is_wait_not_by_other(lock->type_mode)) {
|
||||
lock_rec_cancel(lock);
|
||||
} else if (lock_get_wait(lock)) {
|
||||
/* just reset LOCK_WAIT */
|
||||
lock_rec_reset_nth_bit(lock, heap_no);
|
||||
lock_reset_lock_and_trx_wait(lock);
|
||||
} else {
|
||||
lock_rec_reset_nth_bit(lock, heap_no);
|
||||
}
|
||||
@ -3383,6 +3425,7 @@ lock_table_create(
|
||||
|
||||
ut_ad(table && trx);
|
||||
ut_ad(mutex_own(&kernel_mutex));
|
||||
ut_ad(!(type_mode & LOCK_CONV_BY_OTHER));
|
||||
|
||||
if ((type_mode & LOCK_MODE_MASK) == LOCK_AUTO_INC) {
|
||||
++table->n_waiting_or_granted_auto_inc_locks;
|
||||
@ -3900,6 +3943,7 @@ lock_cancel_waiting_and_release(
|
||||
lock_t* lock) /* in: waiting lock request */
|
||||
{
|
||||
ut_ad(mutex_own(&kernel_mutex));
|
||||
ut_ad(!(lock->type_mode & LOCK_CONV_BY_OTHER));
|
||||
|
||||
if (lock_get_type(lock) == LOCK_REC) {
|
||||
|
||||
@ -4871,7 +4915,7 @@ lock_rec_insert_check_and_lock(
|
||||
/* Note that we may get DB_SUCCESS also here! */
|
||||
err = lock_rec_enqueue_waiting(LOCK_X | LOCK_GAP
|
||||
| LOCK_INSERT_INTENTION,
|
||||
next_rec, index, thr);
|
||||
next_rec, NULL, index, thr);
|
||||
} else {
|
||||
err = DB_SUCCESS;
|
||||
}
|
||||
@ -4941,10 +4985,23 @@ lock_rec_convert_impl_to_expl(
|
||||
|
||||
if (!lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP, rec,
|
||||
impl_trx)) {
|
||||
ulint type_mode = (LOCK_REC | LOCK_X
|
||||
| LOCK_REC_NOT_GAP);
|
||||
|
||||
/* If the delete-marked record was locked already,
|
||||
we should reserve lock waiting for impl_trx as
|
||||
implicit lock. Because cannot lock at this moment.*/
|
||||
|
||||
if (rec_get_deleted_flag(rec, rec_offs_comp(offsets))
|
||||
&& lock_rec_other_has_conflicting(
|
||||
LOCK_X | LOCK_REC_NOT_GAP,
|
||||
rec, impl_trx)) {
|
||||
|
||||
type_mode |= (LOCK_WAIT | LOCK_CONV_BY_OTHER);
|
||||
}
|
||||
|
||||
lock_rec_add_to_queue(
|
||||
LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP,
|
||||
rec, index, impl_trx);
|
||||
type_mode, rec, index, impl_trx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2195,7 +2195,10 @@ row_ins_index_entry(
|
||||
err = row_ins_index_entry_low(BTR_MODIFY_LEAF, index, entry,
|
||||
ext_vec, n_ext_vec, thr);
|
||||
if (err != DB_FAIL) {
|
||||
|
||||
if (index == dict_table_get_first_index(index->table)
|
||||
&& thr_get_trx(thr)->mysql_thd != 0) {
|
||||
DEBUG_SYNC_C("row_ins_clust_index_entry_leaf_after");
|
||||
}
|
||||
return(err);
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,10 @@ Created 10/8/1995 Heikki Tuuri
|
||||
#include "srv0start.h"
|
||||
#include "row0mysql.h"
|
||||
#include "ha_prototypes.h"
|
||||
#include "read0read.h"
|
||||
|
||||
#include "m_string.h" /* for my_sys.h */
|
||||
#include "my_sys.h" /* DEBUG_SYNC_C */
|
||||
|
||||
/* This is set to TRUE if the MySQL user has set it in MySQL; currently
|
||||
affects only FOREIGN KEY definition parsing */
|
||||
@ -1435,6 +1439,10 @@ srv_suspend_mysql_thread(
|
||||
|
||||
trx = thr_get_trx(thr);
|
||||
|
||||
if (trx->mysql_thd != 0) {
|
||||
DEBUG_SYNC_C("srv_suspend_mysql_thread_enter");
|
||||
}
|
||||
|
||||
os_event_set(srv_lock_timeout_thread_event);
|
||||
|
||||
mutex_enter(&kernel_mutex);
|
||||
@ -1920,6 +1928,16 @@ srv_export_innodb_status(void)
|
||||
export_vars.innodb_purge_trx_id_age =
|
||||
ut_dulint_minus(trx_sys->max_trx_id, purge_sys->done_trx_no);
|
||||
}
|
||||
|
||||
if (!purge_sys->view
|
||||
|| ut_dulint_cmp(trx_sys->max_trx_id,
|
||||
purge_sys->view->up_limit_id) < 0) {
|
||||
export_vars.innodb_purge_view_trx_id_age = 0;
|
||||
} else {
|
||||
export_vars.innodb_purge_view_trx_id_age =
|
||||
ut_dulint_minus(trx_sys->max_trx_id,
|
||||
purge_sys->view->up_limit_id);
|
||||
}
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
mutex_exit(&srv_innodb_monitor_mutex);
|
||||
|
@ -34,6 +34,10 @@ trx_purge_t* purge_sys = NULL;
|
||||
which needs no purge */
|
||||
trx_undo_rec_t trx_purge_dummy_rec;
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
my_bool srv_purge_view_update_only_debug;
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
/*********************************************************************
|
||||
Checks if trx_id is >= purge_view: then it is guaranteed that its update
|
||||
undo log still exists in the system. */
|
||||
@ -1079,6 +1083,13 @@ trx_purge(void)
|
||||
|
||||
rw_lock_x_unlock(&(purge_sys->latch));
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
if (srv_purge_view_update_only_debug) {
|
||||
mutex_exit(&(purge_sys->mutex));
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
purge_sys->state = TRX_PURGE_ON;
|
||||
|
||||
/* Handle at most 20 undo log pages in one purge batch */
|
||||
|
@ -580,6 +580,8 @@ static SHOW_VAR innodb_status_variables[]= {
|
||||
#ifdef UNIV_DEBUG
|
||||
{"purge_trx_id_age",
|
||||
(char*) &export_vars.innodb_purge_trx_id_age, SHOW_LONG},
|
||||
{"purge_view_trx_id_age",
|
||||
(char*) &export_vars.innodb_purge_view_trx_id_age, SHOW_LONG},
|
||||
#endif /* UNIV_DEBUG */
|
||||
{NullS, NullS, SHOW_LONG}
|
||||
};
|
||||
@ -11271,6 +11273,13 @@ static MYSQL_SYSVAR_UINT(limit_optimistic_insert_debug,
|
||||
btr_cur_limit_optimistic_insert_debug, PLUGIN_VAR_RQCMDARG,
|
||||
"Artificially limit the number of records per B-tree page (0=unlimited).",
|
||||
NULL, NULL, 0, 0, UINT_MAX32, 0);
|
||||
|
||||
static MYSQL_SYSVAR_BOOL(trx_purge_view_update_only_debug,
|
||||
srv_purge_view_update_only_debug, PLUGIN_VAR_NOCMDARG,
|
||||
"Pause actual purging any delete-marked records, but merely update the purge view. "
|
||||
"It is to create artificially the situation the purge view have been updated "
|
||||
"but the each purges were not done yet.",
|
||||
NULL, NULL, FALSE);
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
static struct st_mysql_sys_var* innobase_system_variables[]= {
|
||||
@ -11337,6 +11346,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
|
||||
#ifdef UNIV_DEBUG
|
||||
MYSQL_SYSVAR(trx_rseg_n_slots_debug),
|
||||
MYSQL_SYSVAR(limit_optimistic_insert_debug),
|
||||
MYSQL_SYSVAR(trx_purge_view_update_only_debug),
|
||||
#endif /* UNIV_DEBUG */
|
||||
NULL
|
||||
};
|
||||
|
@ -796,14 +796,22 @@ lock_rec_get_page_no(
|
||||
remains set when the waiting lock is granted,
|
||||
or if the lock is inherited to a neighboring
|
||||
record */
|
||||
#if (LOCK_WAIT|LOCK_GAP|LOCK_REC_NOT_GAP|LOCK_INSERT_INTENTION)&LOCK_MODE_MASK
|
||||
#define LOCK_CONV_BY_OTHER 4096 /*!< this bit is set when the lock is created
|
||||
by other transaction */
|
||||
#if (LOCK_WAIT|LOCK_GAP|LOCK_REC_NOT_GAP|LOCK_INSERT_INTENTION|LOCK_CONV_BY_OTHER)&LOCK_MODE_MASK
|
||||
# error
|
||||
#endif
|
||||
#if (LOCK_WAIT|LOCK_GAP|LOCK_REC_NOT_GAP|LOCK_INSERT_INTENTION)&LOCK_TYPE_MASK
|
||||
#if (LOCK_WAIT|LOCK_GAP|LOCK_REC_NOT_GAP|LOCK_INSERT_INTENTION|LOCK_CONV_BY_OTHER)&LOCK_TYPE_MASK
|
||||
# error
|
||||
#endif
|
||||
/* @} */
|
||||
|
||||
/** Checks if this is a waiting lock created by lock->trx itself.
|
||||
@param type_mode lock->type_mode
|
||||
@return whether it is a waiting lock belonging to lock->trx */
|
||||
#define lock_is_wait_not_by_other(type_mode) \
|
||||
((type_mode & (LOCK_CONV_BY_OTHER | LOCK_WAIT)) == LOCK_WAIT)
|
||||
|
||||
/** Lock operation struct */
|
||||
typedef struct lock_op_struct lock_op_t;
|
||||
/** Lock operation struct */
|
||||
|
@ -247,6 +247,10 @@ extern ulint srv_fatal_semaphore_wait_threshold;
|
||||
#define SRV_SEMAPHORE_WAIT_EXTENSION 7200
|
||||
extern ulint srv_dml_needed_delay;
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
extern my_bool srv_purge_view_update_only_debug;
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
extern mutex_t* kernel_mutex_temp;/* mutex protecting the server, trx structs,
|
||||
query threads, and lock table: we allocate
|
||||
it from dynamic memory to get it to the
|
||||
@ -652,6 +656,8 @@ struct export_var_struct{
|
||||
ulint innodb_rows_deleted; /*!< srv_n_rows_deleted */
|
||||
#ifdef UNIV_DEBUG
|
||||
ulint innodb_purge_trx_id_age; /*!< max_trx_id - purged trx_id */
|
||||
ulint innodb_purge_view_trx_id_age; /*!< rw_max_trx_id
|
||||
- purged view's min trx_id */
|
||||
#endif /* UNIV_DEBUG */
|
||||
};
|
||||
|
||||
|
@ -791,12 +791,16 @@ lock_reset_lock_and_trx_wait(
|
||||
/*=========================*/
|
||||
lock_t* lock) /*!< in: record lock */
|
||||
{
|
||||
ut_ad((lock->trx)->wait_lock == lock);
|
||||
ut_ad(lock_get_wait(lock));
|
||||
|
||||
/* Reset the back pointer in trx to this waiting lock request */
|
||||
|
||||
if (!(lock->type_mode & LOCK_CONV_BY_OTHER)) {
|
||||
ut_ad((lock->trx)->wait_lock == lock);
|
||||
(lock->trx)->wait_lock = NULL;
|
||||
} else {
|
||||
ut_ad(lock_get_type_low(lock) == LOCK_REC);
|
||||
}
|
||||
lock->type_mode &= ~LOCK_WAIT;
|
||||
}
|
||||
|
||||
@ -1420,9 +1424,9 @@ lock_rec_has_expl(
|
||||
|
||||
while (lock) {
|
||||
if (lock->trx == trx
|
||||
&& !lock_is_wait_not_by_other(lock->type_mode)
|
||||
&& lock_mode_stronger_or_eq(lock_get_mode(lock),
|
||||
precise_mode & LOCK_MODE_MASK)
|
||||
&& !lock_get_wait(lock)
|
||||
&& (!lock_rec_get_rec_not_gap(lock)
|
||||
|| (precise_mode & LOCK_REC_NOT_GAP)
|
||||
|| heap_no == PAGE_HEAP_NO_SUPREMUM)
|
||||
@ -1721,7 +1725,7 @@ lock_rec_create(
|
||||
|
||||
HASH_INSERT(lock_t, hash, lock_sys->rec_hash,
|
||||
lock_rec_fold(space, page_no), lock);
|
||||
if (UNIV_UNLIKELY(type_mode & LOCK_WAIT)) {
|
||||
if (lock_is_wait_not_by_other(type_mode)) {
|
||||
|
||||
lock_set_lock_and_trx_wait(lock, trx);
|
||||
}
|
||||
@ -1752,10 +1756,11 @@ lock_rec_enqueue_waiting(
|
||||
const buf_block_t* block, /*!< in: buffer block containing
|
||||
the record */
|
||||
ulint heap_no,/*!< in: heap number of the record */
|
||||
lock_t* lock, /*!< in: lock object; NULL if a new
|
||||
one should be created. */
|
||||
dict_index_t* index, /*!< in: index of record */
|
||||
que_thr_t* thr) /*!< in: query thread */
|
||||
{
|
||||
lock_t* lock;
|
||||
trx_t* trx;
|
||||
|
||||
ut_ad(mutex_own(&kernel_mutex));
|
||||
@ -1789,9 +1794,17 @@ lock_rec_enqueue_waiting(
|
||||
stderr);
|
||||
}
|
||||
|
||||
if (lock == NULL) {
|
||||
/* Enqueue the lock request that will wait to be granted */
|
||||
lock = lock_rec_create(type_mode | LOCK_WAIT,
|
||||
block, heap_no, index, trx);
|
||||
} else {
|
||||
ut_ad(lock->type_mode & LOCK_WAIT);
|
||||
ut_ad(lock->type_mode & LOCK_CONV_BY_OTHER);
|
||||
|
||||
lock->type_mode &= ~LOCK_CONV_BY_OTHER;
|
||||
lock_set_lock_and_trx_wait(lock, trx);
|
||||
}
|
||||
|
||||
/* Check if a deadlock occurs: if yes, remove the lock request and
|
||||
return an error code */
|
||||
@ -2036,6 +2049,7 @@ lock_rec_lock_slow(
|
||||
que_thr_t* thr) /*!< in: query thread */
|
||||
{
|
||||
trx_t* trx;
|
||||
lock_t* lock;
|
||||
|
||||
ut_ad(mutex_own(&kernel_mutex));
|
||||
ut_ad((LOCK_MODE_MASK & mode) != LOCK_S
|
||||
@ -2050,7 +2064,27 @@ lock_rec_lock_slow(
|
||||
|
||||
trx = thr_get_trx(thr);
|
||||
|
||||
if (lock_rec_has_expl(mode, block, heap_no, trx)) {
|
||||
lock = lock_rec_has_expl(mode, block, heap_no, trx);
|
||||
if (lock) {
|
||||
if (lock->type_mode & LOCK_CONV_BY_OTHER) {
|
||||
/* This lock or lock waiting was created by the other
|
||||
transaction, not by the transaction (trx) itself.
|
||||
So, the transaction (trx) should treat it collectly
|
||||
according as whether granted or not. */
|
||||
|
||||
if (lock->type_mode & LOCK_WAIT) {
|
||||
/* This lock request was not granted yet.
|
||||
Should wait for granted. */
|
||||
|
||||
goto enqueue_waiting;
|
||||
} else {
|
||||
/* This lock request was already granted.
|
||||
Just clearing the flag. */
|
||||
|
||||
lock->type_mode &= ~LOCK_CONV_BY_OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
/* The trx already has a strong enough lock on rec: do
|
||||
nothing */
|
||||
|
||||
@ -2060,8 +2094,10 @@ lock_rec_lock_slow(
|
||||
the queue, as this transaction does not have a lock strong
|
||||
enough already granted on the record, we have to wait. */
|
||||
|
||||
ut_ad(lock == NULL);
|
||||
enqueue_waiting:
|
||||
return(lock_rec_enqueue_waiting(mode, block, heap_no,
|
||||
index, thr));
|
||||
lock, index, thr));
|
||||
} else if (!impl) {
|
||||
/* Set the requested lock on the record */
|
||||
|
||||
@ -2203,7 +2239,8 @@ lock_grant(
|
||||
TRX_QUE_LOCK_WAIT state, and there is no need to end the lock wait
|
||||
for it */
|
||||
|
||||
if (lock->trx->que_state == TRX_QUE_LOCK_WAIT) {
|
||||
if (!(lock->type_mode & LOCK_CONV_BY_OTHER)
|
||||
&& lock->trx->que_state == TRX_QUE_LOCK_WAIT) {
|
||||
trx_end_lock_wait(lock->trx);
|
||||
}
|
||||
}
|
||||
@ -2220,6 +2257,7 @@ lock_rec_cancel(
|
||||
{
|
||||
ut_ad(mutex_own(&kernel_mutex));
|
||||
ut_ad(lock_get_type_low(lock) == LOCK_REC);
|
||||
ut_ad(!(lock->type_mode & LOCK_CONV_BY_OTHER));
|
||||
|
||||
/* Reset the bit (there can be only one set bit) in the lock bitmap */
|
||||
lock_rec_reset_nth_bit(lock, lock_rec_find_set_bit(lock));
|
||||
@ -2362,8 +2400,12 @@ lock_rec_reset_and_release_wait(
|
||||
lock = lock_rec_get_first(block, heap_no);
|
||||
|
||||
while (lock != NULL) {
|
||||
if (lock_get_wait(lock)) {
|
||||
if (lock_is_wait_not_by_other(lock->type_mode)) {
|
||||
lock_rec_cancel(lock);
|
||||
} else if (lock_get_wait(lock)) {
|
||||
/* just reset LOCK_WAIT */
|
||||
lock_rec_reset_nth_bit(lock, heap_no);
|
||||
lock_reset_lock_and_trx_wait(lock);
|
||||
} else {
|
||||
lock_rec_reset_nth_bit(lock, heap_no);
|
||||
}
|
||||
@ -3588,6 +3630,7 @@ lock_table_create(
|
||||
|
||||
ut_ad(table && trx);
|
||||
ut_ad(mutex_own(&kernel_mutex));
|
||||
ut_ad(!(type_mode & LOCK_CONV_BY_OTHER));
|
||||
|
||||
if ((type_mode & LOCK_MODE_MASK) == LOCK_AUTO_INC) {
|
||||
++table->n_waiting_or_granted_auto_inc_locks;
|
||||
@ -4139,6 +4182,7 @@ lock_cancel_waiting_and_release(
|
||||
lock_t* lock) /*!< in: waiting lock request */
|
||||
{
|
||||
ut_ad(mutex_own(&kernel_mutex));
|
||||
ut_ad(!(lock->type_mode & LOCK_CONV_BY_OTHER));
|
||||
|
||||
if (lock_get_type_low(lock) == LOCK_REC) {
|
||||
|
||||
@ -5153,7 +5197,7 @@ lock_rec_insert_check_and_lock(
|
||||
err = lock_rec_enqueue_waiting(LOCK_X | LOCK_GAP
|
||||
| LOCK_INSERT_INTENTION,
|
||||
block, next_rec_heap_no,
|
||||
index, thr);
|
||||
NULL, index, thr);
|
||||
} else {
|
||||
err = DB_SUCCESS;
|
||||
}
|
||||
@ -5229,10 +5273,23 @@ lock_rec_convert_impl_to_expl(
|
||||
|
||||
if (!lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP, block,
|
||||
heap_no, impl_trx)) {
|
||||
ulint type_mode = (LOCK_REC | LOCK_X
|
||||
| LOCK_REC_NOT_GAP);
|
||||
|
||||
/* If the delete-marked record was locked already,
|
||||
we should reserve lock waiting for impl_trx as
|
||||
implicit lock. Because cannot lock at this moment.*/
|
||||
|
||||
if (rec_get_deleted_flag(rec, rec_offs_comp(offsets))
|
||||
&& lock_rec_other_has_conflicting(
|
||||
LOCK_X | LOCK_REC_NOT_GAP, block,
|
||||
heap_no, impl_trx)) {
|
||||
|
||||
type_mode |= (LOCK_WAIT | LOCK_CONV_BY_OTHER);
|
||||
}
|
||||
|
||||
lock_rec_add_to_queue(
|
||||
LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP,
|
||||
block, heap_no, index, impl_trx);
|
||||
type_mode, block, heap_no, index, impl_trx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2265,7 +2265,10 @@ row_ins_index_entry(
|
||||
err = row_ins_index_entry_low(BTR_MODIFY_LEAF, index, entry,
|
||||
n_ext, thr);
|
||||
if (err != DB_FAIL) {
|
||||
|
||||
if (index == dict_table_get_first_index(index->table)
|
||||
&& thr_get_trx(thr)->mysql_thd != 0) {
|
||||
DEBUG_SYNC_C("row_ins_clust_index_entry_leaf_after");
|
||||
}
|
||||
return(err);
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,7 @@ Created 10/8/1995 Heikki Tuuri
|
||||
#include "ha_prototypes.h"
|
||||
#include "trx0i_s.h"
|
||||
#include "os0sync.h" /* for HAVE_ATOMIC_BUILTINS */
|
||||
#include "read0read.h"
|
||||
|
||||
#ifdef __WIN__
|
||||
/* error LNK2001: unresolved external symbol _debug_sync_C_callback_ptr */
|
||||
@ -1983,6 +1984,16 @@ srv_export_innodb_status(void)
|
||||
export_vars.innodb_purge_trx_id_age =
|
||||
ut_dulint_minus(trx_sys->max_trx_id, purge_sys->done_trx_no);
|
||||
}
|
||||
|
||||
if (!purge_sys->view
|
||||
|| ut_dulint_cmp(trx_sys->max_trx_id,
|
||||
purge_sys->view->up_limit_id) < 0) {
|
||||
export_vars.innodb_purge_view_trx_id_age = 0;
|
||||
} else {
|
||||
export_vars.innodb_purge_view_trx_id_age =
|
||||
ut_dulint_minus(trx_sys->max_trx_id,
|
||||
purge_sys->view->up_limit_id);
|
||||
}
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
mutex_exit(&srv_innodb_monitor_mutex);
|
||||
|
@ -51,6 +51,10 @@ UNIV_INTERN trx_purge_t* purge_sys = NULL;
|
||||
which needs no purge */
|
||||
UNIV_INTERN trx_undo_rec_t trx_purge_dummy_rec;
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
UNIV_INTERN my_bool srv_purge_view_update_only_debug;
|
||||
#endif /* UNIV_DEBUG */
|
||||
|
||||
/*****************************************************************//**
|
||||
Checks if trx_id is >= purge_view: then it is guaranteed that its update
|
||||
undo log still exists in the system.
|
||||
@ -1142,6 +1146,13 @@ trx_purge(void)
|
||||
|
||||
rw_lock_x_unlock(&(purge_sys->latch));
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
if (srv_purge_view_update_only_debug) {
|
||||
mutex_exit(&(purge_sys->mutex));
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
purge_sys->state = TRX_PURGE_ON;
|
||||
|
||||
/* Handle at most 20 undo log pages in one purge batch */
|
||||
|
Reference in New Issue
Block a user