diff options
author | Yasufumi Kinoshita <yasufumi.kinoshita@oracle.com> | 2012-11-28 17:05:23 +0900 |
---|---|---|
committer | Yasufumi Kinoshita <yasufumi.kinoshita@oracle.com> | 2012-11-28 17:05:23 +0900 |
commit | 8dd87e690d15d15be77f41ad2174a0334a8666d6 (patch) | |
tree | 5a304c58fefb420c25d9f3ac0e1bdd7f9d3e21e4 /storage | |
parent | 112a93a7c863fe591c8983d15b29c9b4c6aec53b (diff) | |
download | mariadb-git-8dd87e690d15d15be77f41ad2174a0334a8666d6.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 | 7 | ||||
-rw-r--r-- | storage/innobase/include/srv0srv.h | 5 | ||||
-rw-r--r-- | storage/innobase/lock/lock0lock.c | 85 | ||||
-rw-r--r-- | storage/innobase/row/row0ins.c | 5 | ||||
-rw-r--r-- | storage/innobase/srv/srv0srv.c | 18 | ||||
-rw-r--r-- | storage/innobase/trx/trx0purge.c | 11 | ||||
-rw-r--r-- | storage/innodb_plugin/handler/ha_innodb.cc | 10 | ||||
-rw-r--r-- | storage/innodb_plugin/include/lock0lock.h | 12 | ||||
-rw-r--r-- | storage/innodb_plugin/include/srv0srv.h | 6 | ||||
-rw-r--r-- | storage/innodb_plugin/lock/lock0lock.c | 87 | ||||
-rw-r--r-- | storage/innodb_plugin/row/row0ins.c | 5 | ||||
-rw-r--r-- | storage/innodb_plugin/srv/srv0srv.c | 11 | ||||
-rw-r--r-- | storage/innodb_plugin/trx/trx0purge.c | 11 |
14 files changed, 250 insertions, 33 deletions
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 28262f02551..194a7eea795 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -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 }; diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h index 70b141eafeb..476a2c8accb 100644 --- a/storage/innobase/include/lock0lock.h +++ b/storage/innobase/include/lock0lock.h @@ -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 diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index e79d352d1e6..e1dbc4d11fa 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -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 */ }; diff --git a/storage/innobase/lock/lock0lock.c b/storage/innobase/lock/lock0lock.c index 57df99fb401..d36e38df4af 100644 --- a/storage/innobase/lock/lock0lock.c +++ b/storage/innobase/lock/lock0lock.c @@ -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 */ - (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(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); } - /* Enqueue the lock request that will wait to be granted */ - lock = lock_rec_create(type_mode | LOCK_WAIT, rec, index, trx); + 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); } } } diff --git a/storage/innobase/row/row0ins.c b/storage/innobase/row/row0ins.c index 785a0426195..678293b492b 100644 --- a/storage/innobase/row/row0ins.c +++ b/storage/innobase/row/row0ins.c @@ -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); } diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index 46d5b6ad274..3240b7515f8 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -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); diff --git a/storage/innobase/trx/trx0purge.c b/storage/innobase/trx/trx0purge.c index e783f14c1ee..1e540c96536 100644 --- a/storage/innobase/trx/trx0purge.c +++ b/storage/innobase/trx/trx0purge.c @@ -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 */ diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index 9ed452d9809..fadcefec023 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -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 }; diff --git a/storage/innodb_plugin/include/lock0lock.h b/storage/innodb_plugin/include/lock0lock.h index b3e1e5c4537..1b472e7b059 100644 --- a/storage/innodb_plugin/include/lock0lock.h +++ b/storage/innodb_plugin/include/lock0lock.h @@ -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 */ diff --git a/storage/innodb_plugin/include/srv0srv.h b/storage/innodb_plugin/include/srv0srv.h index 089a4c1c777..322c15ffe45 100644 --- a/storage/innodb_plugin/include/srv0srv.h +++ b/storage/innodb_plugin/include/srv0srv.h @@ -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 */ }; diff --git a/storage/innodb_plugin/lock/lock0lock.c b/storage/innodb_plugin/lock/lock0lock.c index 49f732da175..f302e9b1011 100644 --- a/storage/innodb_plugin/lock/lock0lock.c +++ b/storage/innodb_plugin/lock/lock0lock.c @@ -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 */ - (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; } @@ -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); } - /* 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 */ @@ -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); } } } diff --git a/storage/innodb_plugin/row/row0ins.c b/storage/innodb_plugin/row/row0ins.c index 92ce04774ea..7e81cf6ab2c 100644 --- a/storage/innodb_plugin/row/row0ins.c +++ b/storage/innodb_plugin/row/row0ins.c @@ -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); } diff --git a/storage/innodb_plugin/srv/srv0srv.c b/storage/innodb_plugin/srv/srv0srv.c index da5861d8c87..4ddd0a4603d 100644 --- a/storage/innodb_plugin/srv/srv0srv.c +++ b/storage/innodb_plugin/srv/srv0srv.c @@ -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); diff --git a/storage/innodb_plugin/trx/trx0purge.c b/storage/innodb_plugin/trx/trx0purge.c index ea508c1003c..4e79187b5c5 100644 --- a/storage/innodb_plugin/trx/trx0purge.c +++ b/storage/innodb_plugin/trx/trx0purge.c @@ -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 */ |