diff options
author | unknown <heikki@hundin.mysql.fi> | 2002-08-22 13:22:04 +0300 |
---|---|---|
committer | unknown <heikki@hundin.mysql.fi> | 2002-08-22 13:22:04 +0300 |
commit | 1bde81589ce7b1f62c7d1213873413e76edbfb32 (patch) | |
tree | fb044a2a44fd08d0c90c1091989d4e7ad9877555 /innobase/lock | |
parent | b3ff14369be62f670d6dac8eb7c9caa29bf1b5c2 (diff) | |
download | mariadb-git-1bde81589ce7b1f62c7d1213873413e76edbfb32.tar.gz |
lock0lock.h, lock0lock.c:
Remove a spurious deadlock when >= 2 insert transactions have to wait because there is a next-key lock set
innobase/lock/lock0lock.c:
Remove a spurious deadlock when >= 2 insert transactions have to wait because there is a next-key lock set
innobase/include/lock0lock.h:
Remove a spurious deadlock when >= 2 insert transactions have to wait because there is a next-key lock set
Diffstat (limited to 'innobase/lock')
-rw-r--r-- | innobase/lock/lock0lock.c | 135 |
1 files changed, 108 insertions, 27 deletions
diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c index 7030dbf9130..867fce04aea 100644 --- a/innobase/lock/lock0lock.c +++ b/innobase/lock/lock0lock.c @@ -96,7 +96,9 @@ is only performed when a transaction itself requests a lock! An insert is allowed to a gap if there are no explicit lock requests by other transactions on the next record. It does not matter if these lock -requests are granted or waiting, gap bit set or not. On the other hand, an +requests are granted or waiting, gap bit set or not, with the exception +that a gap type request set by another transaction to wait for +its turn to do an insert is ignored. On the other hand, an implicit x-lock by another transaction does not prevent an insert, which allows for more concurrency when using an Oracle-style sequence number generator for the primary key with many transactions doing inserts @@ -544,8 +546,7 @@ lock_sec_rec_cons_read_sees( index record */ rec_t* rec, /* in: user record which should be read or passed over by a read cursor */ - dict_index_t* index __attribute__((unused)), - /* in: non-clustered index */ + dict_index_t* index, /* in: non-clustered index */ read_view_t* view) /* in: consistent read view */ { dulint max_trx_id; @@ -616,7 +617,7 @@ UNIV_INLINE ulint lock_get_type( /*==========*/ - /* out: LOCK_TABLE or LOCK_RECa */ + /* out: LOCK_TABLE or LOCK_REC */ lock_t* lock) /* in: lock */ { ut_ad(lock); @@ -718,6 +719,46 @@ lock_rec_set_gap( } /************************************************************************* +Gets the waiting insert flag of a record lock. */ +UNIV_INLINE +ibool +lock_rec_get_insert_intention( +/*==========================*/ + /* out: TRUE if gap flag set */ + lock_t* lock) /* in: record lock */ +{ + ut_ad(lock); + ut_ad(lock_get_type(lock) == LOCK_REC); + + if (lock->type_mode & LOCK_INSERT_INTENTION) { + + return(TRUE); + } + + return(FALSE); +} + +/************************************************************************* +Sets the waiting insert flag of a record lock. */ +UNIV_INLINE +void +lock_rec_set_insert_intention( +/*==========================*/ + lock_t* lock, /* in: record lock */ + ibool val) /* in: value to set: TRUE or FALSE */ +{ + ut_ad(lock); + ut_ad((val == TRUE) || (val == FALSE)); + ut_ad(lock_get_type(lock) == LOCK_REC); + + if (val) { + lock->type_mode = lock->type_mode | LOCK_INSERT_INTENTION; + } else { + lock->type_mode = lock->type_mode & ~LOCK_INSERT_INTENTION; + } +} + +/************************************************************************* Calculates if lock mode 1 is stronger or equal to lock mode 2. */ UNIV_INLINE ibool @@ -1331,6 +1372,12 @@ lock_rec_other_has_expl_req( into account, or 0 if not */ ulint wait, /* in: LOCK_WAIT if also waiting locks are taken into account, or 0 if not */ + ibool ignore_insert_intention,/* in: FALSE if also locks where the + LOCK_INSERT_INTENTION + flag is set should be taken into account; we can + use this option to prevent unnecessary deadlocks + between inserts which have needed to set a wait + lock to wait for some next-key lock to be released */ rec_t* rec, /* in: record to look at */ trx_t* trx) /* in: transaction, or NULL if requests by any transaction are wanted */ @@ -1338,8 +1385,11 @@ lock_rec_other_has_expl_req( lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); - ut_ad((mode == LOCK_X) || (mode == LOCK_S)); - + ut_ad(mode == LOCK_X || mode == LOCK_S); + ut_ad(gap == 0 || gap == LOCK_GAP); + ut_ad(wait == 0 || wait == LOCK_WAIT); + ut_ad(ignore_insert_intention == TRUE + || ignore_insert_intention == FALSE); lock = lock_rec_get_first(rec); while (lock) { @@ -1349,7 +1399,14 @@ lock_rec_other_has_expl_req( && (wait || !lock_get_wait(lock)) && lock_mode_stronger_or_eq(lock_get_mode(lock), mode)) { - return(lock); + if (ignore_insert_intention + && lock_rec_get_insert_intention(lock)) { + + /* We can ignore this lock */ + } else { + + return(lock); + } } lock = lock_rec_get_next(rec, lock); @@ -1526,7 +1583,10 @@ lock_rec_enqueue_waiting( DB_QUE_THR_SUSPENDED */ ulint type_mode,/* in: lock mode this transaction is requesting: LOCK_S or LOCK_X, ORed with - LOCK_GAP if a gap lock is requested */ + LOCK_GAP if a gap lock is requested, ORed + with LOCK_INSERT_INTENTION if the waiting + lock request is set when performing an + insert of an index record */ rec_t* rec, /* in: record */ dict_index_t* index, /* in: index of record */ que_thr_t* thr) /* in: query thread */ @@ -1613,11 +1673,12 @@ lock_rec_add_to_queue( ut_ad(mutex_own(&kernel_mutex)); ut_ad((type_mode & (LOCK_WAIT | LOCK_GAP)) || ((type_mode & LOCK_MODE_MASK) != LOCK_S) - || !lock_rec_other_has_expl_req(LOCK_X, 0, LOCK_WAIT, rec, trx)); + || !lock_rec_other_has_expl_req(LOCK_X, 0, LOCK_WAIT, + FALSE, rec, trx)); ut_ad((type_mode & (LOCK_WAIT | LOCK_GAP)) || ((type_mode & LOCK_MODE_MASK) != LOCK_X) - || !lock_rec_other_has_expl_req(LOCK_S, 0, LOCK_WAIT, rec, trx)); - + || !lock_rec_other_has_expl_req(LOCK_S, 0, LOCK_WAIT, + FALSE,rec, trx)); type_mode = type_mode | LOCK_REC; page = buf_frame_align(rec); @@ -1752,8 +1813,8 @@ lock_rec_lock_slow( nothing */ err = DB_SUCCESS; - } else if (lock_rec_other_has_expl_req(confl_mode, 0, LOCK_WAIT, rec, - trx)) { + } else if (lock_rec_other_has_expl_req(confl_mode, 0, LOCK_WAIT, + FALSE, rec, trx)) { /* If another transaction has a non-gap conflicting request in the queue, as this transaction does not have a lock strong enough already granted on the record, we have to wait. */ @@ -2056,7 +2117,8 @@ lock_rec_inherit_to_gap( lock = lock_rec_get_first(rec); while (lock != NULL) { - lock_rec_add_to_queue((lock->type_mode | LOCK_GAP) & ~LOCK_WAIT, + lock_rec_add_to_queue(((lock->type_mode | LOCK_GAP) + & ~LOCK_WAIT), heir, lock->index, lock->trx); lock = lock_rec_get_next(rec, lock); } @@ -2312,7 +2374,7 @@ list start is moved to another page. */ void lock_move_rec_list_start( /*=====================*/ - page_t* new_page __attribute__((unused)),/*in: index page to move to */ + page_t* new_page, /* in: index page to move to */ page_t* page, /* in: index page */ rec_t* rec, /* in: record on page: this is the first record NOT copied */ @@ -2782,7 +2844,7 @@ lock_deadlock_recursive( we return TRUE */ { lock_t* lock; - ulint bit_no = 0; /* remove warning */ + ulint bit_no; trx_t* lock_trx; char* err_buf; @@ -3486,6 +3548,10 @@ lock_rec_print( buf += sprintf(buf, " gap type lock"); } + if (lock_rec_get_insert_intention(lock)) { + buf += sprintf(buf, " insert intention"); + } + if (lock_get_wait(lock)) { buf += sprintf(buf, " waiting"); } @@ -3503,6 +3569,15 @@ lock_rec_print( IB__FILE__, __LINE__, &mtr); if (page) { page = buf_page_get_nowait(space, page_no, RW_S_LATCH, &mtr); + + if (!page) { + /* Let us try to get an X-latch. If the current thread + is holding an X-latch on the page, we cannot get an + S-latch. */ + + page = buf_page_get_nowait(space, page_no, RW_X_LATCH, + &mtr); + } } if (page) { @@ -3869,7 +3944,7 @@ lock_rec_queue_validate( impl_trx = lock_clust_rec_some_has_impl(rec, index); if (impl_trx && lock_rec_other_has_expl_req(LOCK_S, 0, - LOCK_WAIT, rec, impl_trx)) { + LOCK_WAIT, FALSE, rec, impl_trx)) { ut_a(lock_rec_has_expl(LOCK_X, rec, impl_trx)); } @@ -3884,7 +3959,7 @@ lock_rec_queue_validate( impl_trx = lock_sec_rec_some_has_impl_off_kernel(rec, index); if (impl_trx && lock_rec_other_has_expl_req(LOCK_S, 0, - LOCK_WAIT, rec, impl_trx)) { + LOCK_WAIT, FALSE, rec, impl_trx)) { ut_a(lock_rec_has_expl(LOCK_X, rec, impl_trx)); } @@ -3909,12 +3984,10 @@ lock_rec_queue_validate( if (lock_get_mode(lock) == LOCK_S) { ut_a(!lock_rec_other_has_expl_req(LOCK_X, - 0, 0, rec, - lock->trx)); + 0, 0, FALSE, rec, lock->trx)); } else { ut_a(!lock_rec_other_has_expl_req(LOCK_S, - 0, 0, rec, - lock->trx)); + 0, 0, FALSE, rec, lock->trx)); } } else if (lock_get_wait(lock) && !lock_rec_get_gap(lock)) { @@ -4153,11 +4226,19 @@ lock_rec_insert_check_and_lock( *inherit = TRUE; /* If another transaction has an explicit lock request, gap or not, - waiting or granted, on the successor, the insert has to wait */ - - if (lock_rec_other_has_expl_req(LOCK_S, LOCK_GAP, LOCK_WAIT, next_rec, - trx)) { - err = lock_rec_enqueue_waiting(LOCK_X | LOCK_GAP, next_rec, + waiting or granted, on the successor, the insert has to wait. + + An exception is the case where the lock by the another transaction + is a gap type lock which it placed to wait for its turn to insert. We + do not consider that kind of a lock conflicting with our insert. This + eliminates an unnecessary deadlock which resulted when 2 transactions + had to wait for their insert. Both had waiting gap type lock requests + on the successor, which produced an unnecessary deadlock. */ + + if (lock_rec_other_has_expl_req(LOCK_S, LOCK_GAP, LOCK_WAIT, + TRUE, next_rec, trx)) { + err = lock_rec_enqueue_waiting(LOCK_X | LOCK_GAP + | LOCK_INSERT_INTENTION, next_rec, index, thr); } else { err = DB_SUCCESS; |