diff options
author | Yasufumi Kinoshita <yasufumi.kinoshita@oracle.com> | 2012-11-28 17:07:02 +0900 |
---|---|---|
committer | Yasufumi Kinoshita <yasufumi.kinoshita@oracle.com> | 2012-11-28 17:07:02 +0900 |
commit | ddf518c567ec7bb24890a1347784d2b37d095a9e (patch) | |
tree | 3302a36a8fd12501ec8db104473cddcfd0cfea5d /storage | |
parent | cfebb6459d035104aad8cef596285b6b07f5e403 (diff) | |
parent | 8dd87e690d15d15be77f41ad2174a0334a8666d6 (diff) | |
download | mariadb-git-ddf518c567ec7bb24890a1347784d2b37d095a9e.tar.gz |
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
Diffstat (limited to 'storage')
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 10 | ||||
-rw-r--r-- | storage/innobase/include/lock0lock.h | 12 | ||||
-rw-r--r-- | storage/innobase/include/srv0srv.h | 6 | ||||
-rw-r--r-- | storage/innobase/lock/lock0lock.c | 87 | ||||
-rw-r--r-- | storage/innobase/row/row0ins.c | 5 | ||||
-rw-r--r-- | storage/innobase/srv/srv0srv.c | 8 | ||||
-rw-r--r-- | storage/innobase/trx/trx0purge.c | 10 |
7 files changed, 120 insertions, 18 deletions
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 2de6a30d78d..8a7d5d50c39 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -696,6 +696,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} }; @@ -11689,6 +11691,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 MYSQL_SYSVAR_BOOL(print_all_deadlocks, srv_print_all_deadlocks, @@ -11768,6 +11777,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 */ MYSQL_SYSVAR(print_all_deadlocks), NULL diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h index 29fdc3bbe97..b2a622296e0 100644 --- a/storage/innobase/include/lock0lock.h +++ b/storage/innobase/include/lock0lock.h @@ -797,14 +797,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 */ diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index 13510f1ed62..7d934365fb6 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -265,6 +265,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 @@ -747,6 +751,8 @@ struct export_var_struct{ ulint innodb_truncated_status_writes; /*!< srv_truncated_status_writes */ #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 */ }; diff --git a/storage/innobase/lock/lock0lock.c b/storage/innobase/lock/lock0lock.c index e0c6d698406..a72be540c1b 100644 --- a/storage/innobase/lock/lock0lock.c +++ b/storage/innobase/lock/lock0lock.c @@ -789,12 +789,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 */ - (lock->trx)->wait_lock = NULL; + 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; } @@ -1430,9 +1434,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) @@ -1730,7 +1734,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); } @@ -1761,10 +1765,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)); @@ -1799,9 +1804,17 @@ lock_rec_enqueue_waiting( ut_ad(0); } - /* Enqueue the lock request that will wait to be granted */ - lock = lock_rec_create(type_mode | LOCK_WAIT, - block, heap_no, index, trx); + 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 */ @@ -2046,6 +2059,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 @@ -2060,7 +2074,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 */ @@ -2070,8 +2104,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 */ @@ -2213,7 +2249,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); } } @@ -2230,6 +2267,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)); @@ -2372,8 +2410,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); } @@ -3646,6 +3688,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; @@ -4199,6 +4242,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) { @@ -5240,7 +5284,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; } @@ -5316,10 +5360,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); } } } diff --git a/storage/innobase/row/row0ins.c b/storage/innobase/row/row0ins.c index 18486514267..21afa9eff0d 100644 --- a/storage/innobase/row/row0ins.c +++ b/storage/innobase/row/row0ins.c @@ -2254,7 +2254,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); } diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index d5806342409..3b493e4e115 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -2103,6 +2103,14 @@ srv_export_innodb_status(void) export_vars.innodb_purge_trx_id_age = trx_sys->max_trx_id - purge_sys->done_trx_no; } + + if (!purge_sys->view + || trx_sys->max_trx_id < purge_sys->view->up_limit_id) { + export_vars.innodb_purge_view_trx_id_age = 0; + } else { + export_vars.innodb_purge_view_trx_id_age = + trx_sys->max_trx_id - purge_sys->view->up_limit_id; + } #endif /* UNIV_DEBUG */ mutex_exit(&srv_innodb_monitor_mutex); diff --git a/storage/innobase/trx/trx0purge.c b/storage/innobase/trx/trx0purge.c index 57fdd341d19..719851218c3 100644 --- a/storage/innobase/trx/trx0purge.c +++ b/storage/innobase/trx/trx0purge.c @@ -61,6 +61,10 @@ UNIV_INTERN mysql_pfs_key_t trx_purge_latch_key; UNIV_INTERN mysql_pfs_key_t purge_sys_bh_mutex_key; #endif /* UNIV_PFS_MUTEX */ +#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. @@ -1180,6 +1184,12 @@ trx_purge( rw_lock_x_unlock(&(purge_sys->latch)); +#ifdef UNIV_DEBUG + if (srv_purge_view_update_only_debug) { + return(0); + } +#endif + purge_sys->state = TRX_PURGE_ON; purge_sys->handle_limit = purge_sys->n_pages_handled + limit; |