diff --git a/libmariadb b/libmariadb new file mode 160000 index 00000000000..c8dd0899d48 --- /dev/null +++ b/libmariadb @@ -0,0 +1 @@ +Subproject commit c8dd0899d484ad698ec2da5bc8e3d19ff8b623b9 diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result index ee596b7f40e..f8eacfb3c6c 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result +++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result @@ -1335,6 +1335,20 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST OFF,ON READ_ONLY YES COMMAND_LINE_ARGUMENT NONE +VARIABLE_NAME INNODB_LOCK_SCHEDULE_ALGORITHM +SESSION_VALUE NULL +GLOBAL_VALUE fcfs +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE fcfs +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE ENUM +VARIABLE_COMMENT The algorithm Innodb uses for deciding which locks to grant next when a lock is released. Possible values are FCFS grant the locks in First-Come-First-Served order; VATS use the Variance-Aware-Transaction-Scheduling algorithm, which uses an Eldest-Transaction-First heuristic. +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST fcfs,vats +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME INNODB_LOCK_WAIT_TIMEOUT SESSION_VALUE 50 GLOBAL_VALUE 50 diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index a436d079d03..42c80b82d40 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1553,7 +1553,7 @@ thd_is_replication_slave_thread( /*============================*/ THD* thd) /*!< in: thread handle */ { - return((ibool) thd_slave_thread(thd)); + return thd && ((ibool) thd_slave_thread(thd)); } /******************************************************************//** diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h index 2b1158eddf8..c00b5450036 100644 --- a/storage/innobase/include/lock0lock.h +++ b/storage/innobase/include/lock0lock.h @@ -44,10 +44,10 @@ extern ibool lock_print_waits; #endif /* UNIV_DEBUG */ /** Alternatives for innodb_lock_schedule_algorithm, which can be changed by - setting innodb_lock_schedule_algorithm. */ + setting innodb_lock_schedule_algorithm. */ enum innodb_lock_schedule_algorithm_t { - INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS, /*!< First Come First Served */ - INNODB_LOCK_SCHEDULE_ALGORITHM_VATS /*!< Variance-Aware-Transaction-Scheduling */ + INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS, /*!< First Come First Served */ + INNODB_LOCK_SCHEDULE_ALGORITHM_VATS /*!< Variance-Aware-Transaction-Scheduling */ }; extern ulong innodb_lock_schedule_algorithm; diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index add5e311957..3cc61de8e76 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -844,6 +844,8 @@ struct trx_t{ time_t start_time; /*!< time the trx state last time became TRX_STATE_ACTIVE */ + clock_t start_time_micro; /*!< start time of transaction in + microseconds */ trx_id_t id; /*!< transaction id */ XID xid; /*!< X/Open XA transaction identification to identify a diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index 572dc0e1dc4..198e4ebf6f2 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -383,6 +383,25 @@ struct lock_stack_t { ulint heap_no; /*!< heap number if rec lock */ }; +/*********************************************************************//** +Checks if a waiting record lock request still has to wait in a queue. +@return lock that is causing the wait */ +static +const lock_t* +lock_rec_has_to_wait_in_queue( +/*==========================*/ + const lock_t* wait_lock); /*!< in: waiting record lock */ + +/*************************************************************//** +Grants a lock to a waiting lock request and releases the waiting transaction. +The caller must hold lock_sys->mutex. */ +static +void +lock_grant( +/*=======*/ + lock_t* lock, /*!< in/out: waiting lock request */ + bool owns_trx_mutex); /*!< in: whether lock->trx->mutex is owned */ + extern "C" void thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd); extern "C" int thd_need_wait_for(const MYSQL_THD thd); extern "C" @@ -1988,82 +2007,26 @@ wsrep_print_wait_locks( } #endif /* WITH_WSREP */ -/*********************************************************************//** -Check if lock1 has higher priority than lock2. -NULL has lowest priority. -Respect the preference of the upper server layer to reduce conflict -during in-order parallel replication. -If neither of them is wait lock, the first one has higher priority. -If only one of them is a wait lock, it has lower priority. -Otherwise, the one with an older transaction has higher priority. -@returns true if lock1 has higher priority, false otherwise. */ -bool -has_higher_priority( - lock_t *lock1, - lock_t *lock2) -{ - if (lock1 == NULL) { - return false; - } else if (lock2 == NULL) { - return true; - } - // Ask the upper server layer if any of the two trx should be prefered. - int preference = thd_deadlock_victim_preference(lock1->trx->mysql_thd, lock2->trx->mysql_thd); - if (preference == -1) { - // lock1 is preferred as a victim, so lock2 has higher priority - return false; - } else if (preference == 1) { - // lock2 is preferred as a victim, so lock1 has higher priority - return true; - } - // No preference. Compre them by wait mode and trx age. - if (!lock_get_wait(lock1)) { - return true; - } else if (!lock_get_wait(lock2)) { - return false; - } - return lock1->trx->start_time < lock2->trx->start_time; -} - -/*********************************************************************//** -Insert a lock to the hash list according to the mode (whether it is a wait lock) -and the age of the transaction the it is associated with. -If the lock is not a wait lock, insert it to the head of the hash list. -Otherwise, insert it to the middle of the wait locks according to the age of the -transaciton. -*/ static void -lock_rec_insert_by_trx_age( - lock_t *in_lock, /*!< in: lock to be insert */ - bool wait) /*!< in: whether it's a wait lock */ +lock_rec_insert_to_head( + lock_t *in_lock, /*!< in: lock to be insert */ + ulint rec_fold) /*!< in: rec_fold of the page */ { - ulint space; - ulint page_no; - ulint rec_fold; - hash_cell_t* cell; + hash_cell_t* cell; lock_t* node; - lock_t* next; - space = in_lock->un_member.rec_lock.space; - page_no = in_lock->un_member.rec_lock.page_no; - rec_fold = lock_rec_fold(space, page_no); - cell = hash_get_nth_cell(lock_sys->rec_hash, - hash_calc_hash(rec_fold, lock_sys->rec_hash)); - - node = (lock_t *) cell->node; - // If in_lock is not a wait lock, we insert it to the head of the list. - if (node == NULL || !wait || has_higher_priority(in_lock, node)) { - cell->node = in_lock; - in_lock->hash = node; + if (in_lock == NULL) { return; } - while (node != NULL && has_higher_priority((lock_t *) node->hash, in_lock)) { - node = (lock_t *) node->hash; + + cell = hash_get_nth_cell(lock_sys->rec_hash, + hash_calc_hash(rec_fold, lock_sys->rec_hash)); + node = (lock_t *) cell->node; + if (node != in_lock) { + cell->node = in_lock; + in_lock->hash = node; } - next = (lock_t *) node->hash; - node->hash = in_lock; - in_lock->hash = next; } /*********************************************************************//** @@ -2093,8 +2056,10 @@ lock_rec_create( lock_t* lock; ulint page_no; ulint space; + ulint rec_fold; ulint n_bits; ulint n_bytes; + bool wait_lock; const page_t* page; ut_ad(lock_mutex_own()); @@ -2121,6 +2086,8 @@ lock_rec_create( type_mode = type_mode & ~(LOCK_GAP | LOCK_REC_NOT_GAP); } + wait_lock = type_mode & LOCK_WAIT; + /* Make lock bitmap bigger by a safety margin */ n_bits = page_dir_get_n_heap(page) + LOCK_PAGE_BITMAP_MARGIN; n_bytes = 1 + n_bits / 8; @@ -2136,6 +2103,7 @@ lock_rec_create( lock->un_member.rec_lock.space = space; lock->un_member.rec_lock.page_no = page_no; lock->un_member.rec_lock.n_bits = n_bytes * 8; + rec_fold = lock_rec_fold(space, page_no); /* Reset to zero the bitmap which resides immediately after the lock struct */ @@ -2228,13 +2196,27 @@ lock_rec_create( return(lock); } trx_mutex_exit(c_lock->trx); + } else if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS + && !thd_is_replication_slave_thread(lock->trx->mysql_thd)) { + if (wait_lock) { + HASH_INSERT(lock_t, hash, lock_sys->rec_hash, rec_fold, lock); + } else { + lock_rec_insert_to_head(lock, rec_fold); + } } else { - HASH_INSERT(lock_t, hash, lock_sys->rec_hash, - lock_rec_fold(space, page_no), lock); + HASH_INSERT(lock_t, hash, lock_sys->rec_hash, rec_fold, lock); } #else - HASH_INSERT(lock_t, hash, lock_sys->rec_hash, - lock_rec_fold(space, page_no), lock); + if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS + && !thd_is_replication_slave_thread(lock->trx->mysql_thd)) { + if (wait_lock) { + HASH_INSERT(lock_t, hash, lock_sys->rec_hash, rec_fold, lock); + } else { + lock_rec_insert_to_head(lock, rec_fold); + } + } else { + HASH_INSERT(lock_t, hash, lock_sys->rec_hash, rec_fold, lock); + } #endif /* WITH_WSREP */ if (!caller_owns_trx_mutex) { @@ -2257,6 +2239,135 @@ lock_rec_create( return(lock); } +/*********************************************************************//** +Check if lock1 has higher priority than lock2. +NULL has lowest priority. +Respect the preference of the upper server layer to reduce conflict +during in-order parallel replication. +If neither of them is wait lock, the first one has higher priority. +If only one of them is a wait lock, it has lower priority. +Otherwise, the one with an older transaction has higher priority. +@returns true if lock1 has higher priority, false otherwise. */ +bool +has_higher_priority( + lock_t *lock1, + lock_t *lock2) +{ + if (lock1 == NULL) { + return false; + } else if (lock2 == NULL) { + return true; + } + // Ask the upper server layer if any of the two trx should be prefered. + int preference = thd_deadlock_victim_preference(lock1->trx->mysql_thd, lock2->trx->mysql_thd); + if (preference == -1) { + // lock1 is preferred as a victim, so lock2 has higher priority + return false; + } else if (preference == 1) { + // lock2 is preferred as a victim, so lock1 has higher priority + return true; + } + // No preference. Compre them by wait mode and trx age. + if (!lock_get_wait(lock1)) { + return true; + } else if (!lock_get_wait(lock2)) { + return false; + } + return lock1->trx->start_time_micro <= lock2->trx->start_time_micro; +} + +/*********************************************************************//** +Insert a lock to the hash list according to the mode (whether it is a wait +lock) and the age of the transaction the it is associated with. +If the lock is not a wait lock, insert it to the head of the hash list. +Otherwise, insert it to the middle of the wait locks according to the age of +the transaciton. */ +static +dberr_t +lock_rec_insert_by_trx_age( + lock_t *in_lock) /*!< in: lock to be insert */{ + ulint space; + ulint page_no; + ulint rec_fold; + lock_t* node; + lock_t* next; + hash_cell_t* cell; + + space = in_lock->un_member.rec_lock.space; + page_no = in_lock->un_member.rec_lock.page_no; + rec_fold = lock_rec_fold(space, page_no); + cell = hash_get_nth_cell(lock_sys->rec_hash, + hash_calc_hash(rec_fold, lock_sys->rec_hash)); + + node = (lock_t *) cell->node; + // If in_lock is not a wait lock, we insert it to the head of the list. + if (node == NULL || !lock_get_wait(in_lock) || has_higher_priority(in_lock, node)) { + cell->node = in_lock; + in_lock->hash = node; + if (lock_get_wait(in_lock)) { + lock_grant(in_lock, true); + return DB_SUCCESS_LOCKED_REC; + } + return DB_SUCCESS; + } + while (node != NULL && has_higher_priority((lock_t *) node->hash, + in_lock)) { + node = (lock_t *) node->hash; + } + next = (lock_t *) node->hash; + node->hash = in_lock; + in_lock->hash = next; + + if (lock_get_wait(in_lock) && !lock_rec_has_to_wait_in_queue(in_lock)) { + lock_grant(in_lock, true); + if (cell->node != in_lock) { + // Move it to the front of the queue + node->hash = in_lock->hash; + next = (lock_t *) cell->node; + cell->node = in_lock; + in_lock->hash = next; + } + return DB_SUCCESS_LOCKED_REC; + } + + return DB_SUCCESS; +} + +static +bool +lock_queue_validate( + const lock_t *in_lock) /*!< in: lock whose hash list is to be validated */ +{ + ulint space; + ulint page_no; + ulint rec_fold; + hash_table_t* hash; + hash_cell_t* cell; + lock_t* next; + bool wait_lock = false; + + if (in_lock == NULL) { + return true; + } + + space = in_lock->un_member.rec_lock.space; + page_no = in_lock->un_member.rec_lock.page_no; + rec_fold = lock_rec_fold(space, page_no); + cell = hash_get_nth_cell(lock_sys->rec_hash, + hash_calc_hash(rec_fold, lock_sys->rec_hash)); + next = (lock_t *) cell->node; + while (next != NULL) { + // If this is a granted lock, check that there's no wait lock before it. + if (!lock_get_wait(next)) { + ut_ad(!wait_lock); + } else { + wait_lock = true; + } + next = (lock_t *) next->hash; + } + return true; +} + /*********************************************************************//** Enqueues a waiting request for a lock which cannot be granted immediately. Checks for deadlocks. @@ -2289,6 +2400,9 @@ lock_rec_enqueue_waiting( trx_t* trx; lock_t* lock; trx_id_t victim_trx_id; + ulint space; + ulint page_no; + dberr_t err; ut_ad(lock_mutex_own()); ut_ad(!srv_read_only_mode); @@ -2362,36 +2476,46 @@ lock_rec_enqueue_waiting( transaction as a victim, it is possible that we already have the lock now granted! */ - return(DB_SUCCESS_LOCKED_REC); - } + err = DB_SUCCESS_LOCKED_REC; + } else { + trx->lock.que_state = TRX_QUE_LOCK_WAIT; - // Move it only when it does not cause a deadlock. - if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS) { - HASH_DELETE(lock_t, hash, lock_sys->rec_hash, - lock_rec_fold(buf_block_get_space(block), buf_block_get_page_no(block)), lock); - lock_rec_insert_by_trx_age(lock, true); - } + trx->lock.was_chosen_as_deadlock_victim = FALSE; + trx->lock.wait_started = ut_time(); - trx->lock.que_state = TRX_QUE_LOCK_WAIT; - - trx->lock.was_chosen_as_deadlock_victim = FALSE; - trx->lock.wait_started = ut_time(); - - ut_a(que_thr_stop(thr)); + ut_a(que_thr_stop(thr)); #ifdef UNIV_DEBUG - if (lock_print_waits) { - fprintf(stderr, "Lock wait for trx " TRX_ID_FMT " in index ", - trx->id); - ut_print_name(stderr, trx, FALSE, index->name); - } + if (lock_print_waits) { + fprintf(stderr, "Lock wait for trx " TRX_ID_FMT " in index ", + trx->id); + ut_print_name(stderr, trx, FALSE, index->name); + } #endif /* UNIV_DEBUG */ - MONITOR_INC(MONITOR_LOCKREC_WAIT); + MONITOR_INC(MONITOR_LOCKREC_WAIT); - trx->n_rec_lock_waits++; + trx->n_rec_lock_waits++; - return(DB_LOCK_WAIT); + err = DB_LOCK_WAIT; + } + + // Move it only when it does not cause a deadlock. + if (err != DB_DEADLOCK + && innodb_lock_schedule_algorithm + == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS + && !thd_is_replication_slave_thread(lock->trx->mysql_thd)) { + space = buf_block_get_space(block); + page_no = buf_block_get_page_no(block); + HASH_DELETE(lock_t, hash, lock_sys->rec_hash, + lock_rec_fold(space, page_no), lock); + dberr_t res = lock_rec_insert_by_trx_age(lock); + if (res != DB_SUCCESS) { + return res; + } + } + + return err; } /*********************************************************************//** @@ -2820,13 +2944,16 @@ static void lock_grant( /*=======*/ - lock_t* lock) /*!< in/out: waiting lock request */ + lock_t* lock, /*!< in/out: waiting lock request */ + bool owns_trx_mutex) /*!< in: whether lock->trx->mutex is owned */ { ut_ad(lock_mutex_own()); lock_reset_lock_and_trx_wait(lock); - trx_mutex_enter(lock->trx); + if (!owns_trx_mutex) { + trx_mutex_enter(lock->trx); + } if (lock_get_mode(lock) == LOCK_AUTO_INC) { dict_table_t* table = lock->un_member.tab_lock.table; @@ -2875,7 +3002,9 @@ lock_grant( lock->wait_time = (ulint)difftime(ut_time(), lock->requested_time); - trx_mutex_exit(lock->trx); + if (!owns_trx_mutex) { + trx_mutex_exit(lock->trx); + } } /*************************************************************//** @@ -2913,23 +3042,62 @@ lock_rec_cancel( trx_mutex_exit(lock->trx); } -/*************************************************************//** -Move the lock to the head of the hash list. */ static void -lock_rec_move_to_front( - lock_t *lock_to_move, /*!< in: lock to be moved */ - ulint rec_fold) /*!< in: rec fold of the lock */ +lock_grant_and_move_on_page( + ulint space, + ulint page_no) { - if (lock_to_move != NULL) - { - // Move the target lock to the head of the list - hash_cell_t* cell = hash_get_nth_cell(lock_sys->rec_hash, - hash_calc_hash(rec_fold, lock_sys->rec_hash)); - if (lock_to_move != cell->node) { - lock_t *next = (lock_t *) cell->node; - cell->node = lock_to_move; - lock_to_move->hash = next; + lock_t* lock; + lock_t* next; + lock_t* previous; + ulint rec_fold = lock_rec_fold(space, page_no); + + previous = (lock_t *) hash_get_nth_cell(lock_sys->rec_hash, + hash_calc_hash(rec_fold, lock_sys->rec_hash))->node; + if (previous == NULL) { + return; + } + if (previous->un_member.rec_lock.space == space && + previous->un_member.rec_lock.page_no == page_no) { + lock = previous; + } + else { + next = (lock_t *) previous->hash; + while (next && + (next->un_member.rec_lock.space != space || + next->un_member.rec_lock.page_no != page_no)) { + previous = next; + next = (lock_t *) previous->hash; + } + lock = (lock_t *) previous->hash; + } + + ut_ad(previous->hash == lock || previous == lock); + /* Grant locks if there are no conflicting locks ahead. + Move granted locks to the head of the list. */ + for (;lock != NULL;) { + /* If the lock is a wait lock on this page, and it does not need to wait. */ + if ((lock->un_member.rec_lock.space == space) + && (lock->un_member.rec_lock.page_no == page_no) + && lock_get_wait(lock) + && !lock_rec_has_to_wait_in_queue(lock)) { + + lock_grant(lock, false); + + if (previous != NULL) { + /* Move the lock to the head of the list. */ + HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock); + lock_rec_insert_to_head(lock, rec_fold); + } else { + /* Already at the head of the list. */ + previous = lock; + } + /* Move on to the next lock. */ + lock = static_cast(HASH_GET_NEXT(hash, previous)); + } else { + previous = lock; + lock = static_cast(HASH_GET_NEXT(hash, lock)); } } } @@ -2951,9 +3119,7 @@ lock_rec_dequeue_from_page( { ulint space; ulint page_no; - ulint rec_fold; lock_t* lock; - lock_t* previous = NULL; trx_lock_t* trx_lock; ut_ad(lock_mutex_own()); @@ -2964,7 +3130,6 @@ lock_rec_dequeue_from_page( space = in_lock->un_member.rec_lock.space; page_no = in_lock->un_member.rec_lock.page_no; - rec_fold = lock_rec_fold(space, page_no); in_lock->index->table->n_rec_locks--; @@ -2976,7 +3141,9 @@ lock_rec_dequeue_from_page( MONITOR_INC(MONITOR_RECLOCK_REMOVED); MONITOR_DEC(MONITOR_NUM_RECLOCK); - if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS) { + if (innodb_lock_schedule_algorithm + == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS || + thd_is_replication_slave_thread(in_lock->trx->mysql_thd)) { /* Check if waiting locks in the queue can now be granted: grant locks if there are no conflicting locks ahead. Stop at the first X lock that is waiting or has been granted. */ @@ -2990,38 +3157,11 @@ lock_rec_dequeue_from_page( /* Grant the lock */ ut_ad(lock->trx != in_lock->trx); - lock_grant(lock); + lock_grant(lock, false); } } } else { - /* Grant locks if there are no conflicting locks ahead. - Move granted locks to the head of the list. */ - for (lock = lock_rec_get_first_on_page_addr(space, page_no); - lock != NULL;) { - - /* If the lock is a wait lock on this page, and it does not need to wait. */ - if ((lock->un_member.rec_lock.space == space) - && (lock->un_member.rec_lock.page_no == page_no) - && lock_get_wait(lock) - && !lock_rec_has_to_wait_in_queue(lock)) { - - lock_grant(lock); - - if (previous != NULL) { - /* Move the lock to the head of the list. */ - HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock); - lock_rec_move_to_front(lock, rec_fold); - } else { - /* Already at the head of the list. */ - previous = lock; - } - /* Move on to the next lock. */ - lock = static_cast(HASH_GET_NEXT(hash, previous)); - } else { - previous = lock; - lock = static_cast(HASH_GET_NEXT(hash, lock)); - } - } + lock_grant_and_move_on_page(space, page_no); } } @@ -5319,12 +5459,71 @@ lock_table_dequeue( /* Grant the lock */ ut_ad(in_lock->trx != lock->trx); - lock_grant(lock); + lock_grant(lock, false); } } } /*=========================== LOCK RELEASE ==============================*/ +static +void +lock_grant_and_move_on_rec( + lock_t* first_lock, + ulint heap_no) +{ + lock_t* lock; + lock_t* previous; + ulint space; + ulint page_no; + ulint rec_fold; + + space = first_lock->un_member.rec_lock.space; + page_no = first_lock->un_member.rec_lock.page_no; + rec_fold = lock_rec_fold(space, page_no); + + previous = (lock_t *) hash_get_nth_cell(lock_sys->rec_hash, + hash_calc_hash(rec_fold, lock_sys->rec_hash))->node; + if (previous == NULL) { + return; + } + if (previous == first_lock) { + lock = previous; + } else { + while (previous->hash && + previous->hash != first_lock) { + previous = (lock_t *) previous->hash; + } + lock = (lock_t *) previous->hash; + } + /* Grant locks if there are no conflicting locks ahead. + Move granted locks to the head of the list. */ + for (;lock != NULL;) { + + /* If the lock is a wait lock on this page, and it does not need to wait. */ + if (lock->un_member.rec_lock.space == space + && lock->un_member.rec_lock.page_no == page_no + && lock_rec_get_nth_bit(lock, heap_no) + && lock_get_wait(lock) + && !lock_rec_has_to_wait_in_queue(lock)) { + + lock_grant(lock, false); + + if (previous != NULL) { + /* Move the lock to the head of the list. */ + HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock); + lock_rec_insert_to_head(lock, rec_fold); + } else { + /* Already at the head of the list. */ + previous = lock; + } + /* Move on to the next lock. */ + lock = static_cast(HASH_GET_NEXT(hash, previous)); + } else { + previous = lock; + lock = static_cast(HASH_GET_NEXT(hash, lock)); + } + } +} /*************************************************************//** Removes a granted record lock of a transaction from the queue and grants @@ -5388,17 +5587,24 @@ released: ut_a(!lock_get_wait(lock)); lock_rec_reset_nth_bit(lock, heap_no); - /* Check if we can now grant waiting lock requests */ + if (innodb_lock_schedule_algorithm + == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS || + thd_is_replication_slave_thread(lock->trx->mysql_thd)) { - for (lock = first_lock; lock != NULL; - lock = lock_rec_get_next(heap_no, lock)) { - if (lock_get_wait(lock) - && !lock_rec_has_to_wait_in_queue(lock)) { + /* Check if we can now grant waiting lock requests */ - /* Grant the lock */ - ut_ad(trx != lock->trx); - lock_grant(lock); + for (lock = first_lock; lock != NULL; + lock = lock_rec_get_next(heap_no, lock)) { + if (lock_get_wait(lock) + && !lock_rec_has_to_wait_in_queue(lock)) { + + /* Grant the lock */ + ut_ad(trx != lock->trx); + lock_grant(lock, false); + } } + } else { + lock_grant_and_move_on_rec(first_lock, heap_no); } lock_mutex_exit(); @@ -6442,6 +6648,9 @@ lock_rec_queue_validate( } } + ut_ad(innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS || + lock_queue_validate(lock)); + func_exit: if (!locked_lock_trx_sys) { lock_mutex_exit(); diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 1a99e159d41..cde32448769 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -919,6 +919,8 @@ trx_start_low( trx->start_time = ut_time(); + trx->start_time_micro = clock(); + MONITOR_INC(MONITOR_TRX_ACTIVE); } diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc index 7ce65e5bc6f..4ddf8f3c50d 100644 --- a/storage/xtradb/handler/ha_innodb.cc +++ b/storage/xtradb/handler/ha_innodb.cc @@ -1826,7 +1826,7 @@ thd_is_replication_slave_thread( /*============================*/ THD* thd) /*!< in: thread handle */ { - return((ibool) thd_slave_thread(thd)); + return thd && ((ibool) thd_slave_thread(thd)); } /******************************************************************//** diff --git a/storage/xtradb/include/lock0lock.h b/storage/xtradb/include/lock0lock.h index 742da74f1b6..a12ca1d85e6 100644 --- a/storage/xtradb/include/lock0lock.h +++ b/storage/xtradb/include/lock0lock.h @@ -46,10 +46,10 @@ extern ibool lock_print_waits; #endif /* UNIV_DEBUG */ /** Alternatives for innodb_lock_schedule_algorithm, which can be changed by - setting innodb_lock_schedule_algorithm. */ + setting innodb_lock_schedule_algorithm. */ enum innodb_lock_schedule_algorithm_t { - INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS, /*!< First Come First Served */ - INNODB_LOCK_SCHEDULE_ALGORITHM_VATS /*!< Variance-Aware-Transaction-Scheduling */ + INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS, /*!< First Come First Served */ + INNODB_LOCK_SCHEDULE_ALGORITHM_VATS /*!< Variance-Aware-Transaction-Scheduling */ }; extern ulong innodb_lock_schedule_algorithm; diff --git a/storage/xtradb/include/trx0trx.h b/storage/xtradb/include/trx0trx.h index ec810405b5a..59d6d961544 100644 --- a/storage/xtradb/include/trx0trx.h +++ b/storage/xtradb/include/trx0trx.h @@ -877,6 +877,8 @@ struct trx_t{ time_t start_time; /*!< time the trx state last time became TRX_STATE_ACTIVE */ + clock_t start_time_micro; /*!< start time of transaction in + microseconds */ trx_id_t id; /*!< transaction id */ XID xid; /*!< X/Open XA transaction identification to identify a diff --git a/storage/xtradb/lock/lock0lock.cc b/storage/xtradb/lock/lock0lock.cc index 2091f926153..e626821ed7e 100644 --- a/storage/xtradb/lock/lock0lock.cc +++ b/storage/xtradb/lock/lock0lock.cc @@ -383,6 +383,25 @@ struct lock_stack_t { ulint heap_no; /*!< heap number if rec lock */ }; +/*********************************************************************//** +Checks if a waiting record lock request still has to wait in a queue. +@return lock that is causing the wait */ +static +const lock_t* +lock_rec_has_to_wait_in_queue( +/*==========================*/ + const lock_t* wait_lock); /*!< in: waiting record lock */ + +/*************************************************************//** +Grants a lock to a waiting lock request and releases the waiting transaction. +The caller must hold lock_sys->mutex. */ +static +void +lock_grant( +/*=======*/ + lock_t* lock, /*!< in/out: waiting lock request */ + bool owns_trx_mutex); /*!< in: whether lock->trx->mutex is owned */ + extern "C" void thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd); extern "C" int thd_need_wait_for(const MYSQL_THD thd); extern "C" @@ -2045,48 +2064,121 @@ has_higher_priority( } else if (!lock_get_wait(lock2)) { return false; } - return lock1->trx->start_time < lock2->trx->start_time; + return lock1->trx->start_time_micro <= lock2->trx->start_time_micro; } /*********************************************************************//** -Insert a lock to the hash list according to the mode (whether it is a wait lock) -and the age of the transaction the it is associated with. +Insert a lock to the hash list according to the mode (whether it is a wait +lock) and the age of the transaction the it is associated with. If the lock is not a wait lock, insert it to the head of the hash list. -Otherwise, insert it to the middle of the wait locks according to the age of the -transaciton. -*/ +Otherwise, insert it to the middle of the wait locks according to the age of +the transaciton. */ static -void +dberr_t lock_rec_insert_by_trx_age( - lock_t *in_lock, /*!< in: lock to be insert */ - bool wait) /*!< in: whether it's a wait lock */ -{ - ulint space; - ulint page_no; - ulint rec_fold; - hash_cell_t* cell; + lock_t *in_lock) /*!< in: lock to be insert */{ + ulint space; + ulint page_no; + ulint rec_fold; lock_t* node; lock_t* next; + hash_cell_t* cell; space = in_lock->un_member.rec_lock.space; page_no = in_lock->un_member.rec_lock.page_no; rec_fold = lock_rec_fold(space, page_no); cell = hash_get_nth_cell(lock_sys->rec_hash, - hash_calc_hash(rec_fold, lock_sys->rec_hash)); + hash_calc_hash(rec_fold, lock_sys->rec_hash)); node = (lock_t *) cell->node; // If in_lock is not a wait lock, we insert it to the head of the list. - if (node == NULL || !wait || has_higher_priority(in_lock, node)) { + if (node == NULL || !lock_get_wait(in_lock) || has_higher_priority(in_lock, node)) { cell->node = in_lock; in_lock->hash = node; - return; + if (lock_get_wait(in_lock)) { + lock_grant(in_lock, true); + return DB_SUCCESS_LOCKED_REC; + } + return DB_SUCCESS; } - while (node != NULL && has_higher_priority((lock_t *) node->hash, in_lock)) { + while (node != NULL && has_higher_priority((lock_t *) node->hash, + in_lock)) { node = (lock_t *) node->hash; } next = (lock_t *) node->hash; node->hash = in_lock; in_lock->hash = next; + + if (lock_get_wait(in_lock) && !lock_rec_has_to_wait_in_queue(in_lock)) { + lock_grant(in_lock, true); + if (cell->node != in_lock) { + // Move it to the front of the queue + node->hash = in_lock->hash; + next = (lock_t *) cell->node; + cell->node = in_lock; + in_lock->hash = next; + } + return DB_SUCCESS_LOCKED_REC; + } + + return DB_SUCCESS; +} + +static +bool +lock_queue_validate( + const lock_t *in_lock) /*!< in: lock whose hash list is to be validated */ +{ + ulint space; + ulint page_no; + ulint rec_fold; + hash_table_t* hash; + hash_cell_t* cell; + lock_t* next; + bool wait_lock = false; + + if (in_lock == NULL) { + return true; + } + + space = in_lock->un_member.rec_lock.space; + page_no = in_lock->un_member.rec_lock.page_no; + rec_fold = lock_rec_fold(space, page_no); + cell = hash_get_nth_cell(lock_sys->rec_hash, + hash_calc_hash(rec_fold, lock_sys->rec_hash)); + next = (lock_t *) cell->node; + while (next != NULL) { + // If this is a granted lock, check that there's no wait lock before it. + if (!lock_get_wait(next)) { + ut_ad(!wait_lock); + } else { + wait_lock = true; + } + next = (lock_t *) next->hash; + } + return true; +} + +static +void +lock_rec_insert_to_head( + lock_t *in_lock, /*!< in: lock to be insert */ + ulint rec_fold) /*!< in: rec_fold of the page */ +{ + hash_cell_t* cell; + lock_t* node; + + if (in_lock == NULL) { + return; + } + + cell = hash_get_nth_cell(lock_sys->rec_hash, + hash_calc_hash(rec_fold, lock_sys->rec_hash)); + node = (lock_t *) cell->node; + if (node != in_lock) { + cell->node = in_lock; + in_lock->hash = node; + } } /*********************************************************************//** @@ -2116,8 +2208,10 @@ lock_rec_create( lock_t* lock; ulint page_no; ulint space; + ulint rec_fold; ulint n_bits; ulint n_bytes; + bool wait_lock; const page_t* page; ut_ad(lock_mutex_own()); @@ -2144,6 +2238,8 @@ lock_rec_create( type_mode = type_mode & ~(LOCK_GAP | LOCK_REC_NOT_GAP); } + wait_lock = type_mode & LOCK_WAIT; + /* Make lock bitmap bigger by a safety margin */ n_bits = page_dir_get_n_heap(page) + LOCK_PAGE_BITMAP_MARGIN; n_bytes = 1 + n_bits / 8; @@ -2159,6 +2255,7 @@ lock_rec_create( lock->un_member.rec_lock.space = space; lock->un_member.rec_lock.page_no = page_no; lock->un_member.rec_lock.n_bits = n_bytes * 8; + rec_fold = lock_rec_fold(space, page_no); /* Reset to zero the bitmap which resides immediately after the lock struct */ @@ -2251,13 +2348,27 @@ lock_rec_create( return(lock); } trx_mutex_exit(c_lock->trx); + } else if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS + && !thd_is_replication_slave_thread(lock->trx->mysql_thd)) { + if (wait_lock) { + HASH_INSERT(lock_t, hash, lock_sys->rec_hash, rec_fold, lock); + } else { + lock_rec_insert_to_head(lock, rec_fold); + } } else { - HASH_INSERT(lock_t, hash, lock_sys->rec_hash, - lock_rec_fold(space, page_no), lock); + HASH_INSERT(lock_t, hash, lock_sys->rec_hash, rec_fold, lock); } #else - HASH_INSERT(lock_t, hash, lock_sys->rec_hash, - lock_rec_fold(space, page_no), lock); + if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS + && !thd_is_replication_slave_thread(lock->trx->mysql_thd)) { + if (wait_lock) { + HASH_INSERT(lock_t, hash, lock_sys->rec_hash, rec_fold, lock); + } else { + lock_rec_insert_to_head(lock, rec_fold); + } + } else { + HASH_INSERT(lock_t, hash, lock_sys->rec_hash, rec_fold, lock); + } #endif /* WITH_WSREP */ lock_sys->rec_num++; @@ -2316,6 +2427,9 @@ lock_rec_enqueue_waiting( trx_id_t victim_trx_id; ulint sec; ulint ms; + ulint space; + ulint page_no; + dberr_t err; ut_ad(lock_mutex_own()); @@ -2390,41 +2504,51 @@ lock_rec_enqueue_waiting( transaction as a victim, it is possible that we already have the lock now granted! */ - return(DB_SUCCESS_LOCKED_REC); - } + err = DB_SUCCESS_LOCKED_REC; + } else { + trx->lock.que_state = TRX_QUE_LOCK_WAIT; - // Move it only when it does not cause a deadlock. - if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS) { - HASH_DELETE(lock_t, hash, lock_sys->rec_hash, - lock_rec_fold(buf_block_get_space(block), buf_block_get_page_no(block)), lock); - lock_rec_insert_by_trx_age(lock, true); - } + trx->lock.was_chosen_as_deadlock_victim = FALSE; + trx->lock.wait_started = ut_time(); - trx->lock.que_state = TRX_QUE_LOCK_WAIT; + if (UNIV_UNLIKELY(trx->take_stats)) { + ut_usectime(&sec, &ms); + trx->lock_que_wait_ustarted = (ib_uint64_t)sec * 1000000 + ms; + } - trx->lock.was_chosen_as_deadlock_victim = FALSE; - trx->lock.wait_started = ut_time(); - - if (UNIV_UNLIKELY(trx->take_stats)) { - ut_usectime(&sec, &ms); - trx->lock_que_wait_ustarted = (ib_uint64_t)sec * 1000000 + ms; - } - - ut_a(que_thr_stop(thr)); + ut_a(que_thr_stop(thr)); #ifdef UNIV_DEBUG - if (lock_print_waits) { - fprintf(stderr, "Lock wait for trx " TRX_ID_FMT " in index ", - trx->id); - ut_print_name(stderr, trx, FALSE, index->name); - } + if (lock_print_waits) { + fprintf(stderr, "Lock wait for trx " TRX_ID_FMT " in index ", + trx->id); + ut_print_name(stderr, trx, FALSE, index->name); + } #endif /* UNIV_DEBUG */ - MONITOR_INC(MONITOR_LOCKREC_WAIT); + MONITOR_INC(MONITOR_LOCKREC_WAIT); - trx->n_rec_lock_waits++; + trx->n_rec_lock_waits++; - return(DB_LOCK_WAIT); + err = DB_LOCK_WAIT; + } + + // Move it only when it does not cause a deadlock. + if (err != DB_DEADLOCK + && innodb_lock_schedule_algorithm + == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS + && !thd_is_replication_slave_thread(lock->trx->mysql_thd)) { + space = buf_block_get_space(block); + page_no = buf_block_get_page_no(block); + HASH_DELETE(lock_t, hash, lock_sys->rec_hash, + lock_rec_fold(space, page_no), lock); + dberr_t res = lock_rec_insert_by_trx_age(lock); + if (res != DB_SUCCESS) { + return res; + } + } + + return err; } /*********************************************************************//** @@ -2856,13 +2980,16 @@ static void lock_grant( /*=======*/ - lock_t* lock) /*!< in/out: waiting lock request */ + lock_t* lock, /*!< in/out: waiting lock request */ + bool owns_trx_mutex) /*!< in: whether lock->trx->mutex is owned */ { ut_ad(lock_mutex_own()); lock_reset_lock_and_trx_wait(lock); - trx_mutex_enter(lock->trx); + if (!owns_trx_mutex) { + trx_mutex_enter(lock->trx); + } if (lock_get_mode(lock) == LOCK_AUTO_INC) { dict_table_t* table = lock->un_member.tab_lock.table; @@ -2911,7 +3038,9 @@ lock_grant( lock->wait_time = (ulint)difftime(ut_time(), lock->requested_time); - trx_mutex_exit(lock->trx); + if (!owns_trx_mutex) { + trx_mutex_exit(lock->trx); + } } /*************************************************************//** @@ -2949,23 +3078,62 @@ lock_rec_cancel( trx_mutex_exit(lock->trx); } -/*************************************************************//** -Move the lock to the head of the hash list. */ static void -lock_rec_move_to_front( - lock_t *lock_to_move, /*!< in: lock to be moved */ - ulint rec_fold) /*!< in: rec fold of the lock */ +lock_grant_and_move_on_page( + ulint space, + ulint page_no) { - if (lock_to_move != NULL) - { - // Move the target lock to the head of the list - hash_cell_t* cell = hash_get_nth_cell(lock_sys->rec_hash, - hash_calc_hash(rec_fold, lock_sys->rec_hash)); - if (lock_to_move != cell->node) { - lock_t *next = (lock_t *) cell->node; - cell->node = lock_to_move; - lock_to_move->hash = next; + lock_t* lock; + lock_t* next; + lock_t* previous; + ulint rec_fold = lock_rec_fold(space, page_no); + + previous = (lock_t *) hash_get_nth_cell(lock_sys->rec_hash, + hash_calc_hash(rec_fold, lock_sys->rec_hash))->node; + if (previous == NULL) { + return; + } + if (previous->un_member.rec_lock.space == space && + previous->un_member.rec_lock.page_no == page_no) { + lock = previous; + } + else { + next = (lock_t *) previous->hash; + while (next && + (next->un_member.rec_lock.space != space || + next->un_member.rec_lock.page_no != page_no)) { + previous = next; + next = (lock_t *) previous->hash; + } + lock = (lock_t *) previous->hash; + } + + ut_ad(previous->hash == lock || previous == lock); + /* Grant locks if there are no conflicting locks ahead. + Move granted locks to the head of the list. */ + for (;lock != NULL;) { + /* If the lock is a wait lock on this page, and it does not need to wait. */ + if ((lock->un_member.rec_lock.space == space) + && (lock->un_member.rec_lock.page_no == page_no) + && lock_get_wait(lock) + && !lock_rec_has_to_wait_in_queue(lock)) { + + lock_grant(lock, false); + + if (previous != NULL) { + /* Move the lock to the head of the list. */ + HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock); + lock_rec_insert_to_head(lock, rec_fold); + } else { + /* Already at the head of the list. */ + previous = lock; + } + /* Move on to the next lock. */ + lock = static_cast(HASH_GET_NEXT(hash, previous)); + } else { + previous = lock; + lock = static_cast(HASH_GET_NEXT(hash, lock)); } } } @@ -2987,9 +3155,7 @@ lock_rec_dequeue_from_page( { ulint space; ulint page_no; - ulint rec_fold; lock_t* lock; - lock_t* previous = NULL; trx_lock_t* trx_lock; ut_ad(lock_mutex_own()); @@ -3000,7 +3166,6 @@ lock_rec_dequeue_from_page( space = in_lock->un_member.rec_lock.space; page_no = in_lock->un_member.rec_lock.page_no; - rec_fold = lock_rec_fold(space, page_no); in_lock->index->table->n_rec_locks--; @@ -3013,7 +3178,9 @@ lock_rec_dequeue_from_page( MONITOR_INC(MONITOR_RECLOCK_REMOVED); MONITOR_DEC(MONITOR_NUM_RECLOCK); - if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS) { + if (innodb_lock_schedule_algorithm + == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS || + thd_is_replication_slave_thread(in_lock->trx->mysql_thd)) { /* Check if waiting locks in the queue can now be granted: grant locks if there are no conflicting locks ahead. Stop at the first X lock that is waiting or has been granted. */ @@ -3027,38 +3194,11 @@ lock_rec_dequeue_from_page( /* Grant the lock */ ut_ad(lock->trx != in_lock->trx); - lock_grant(lock); + lock_grant(lock, false); } } } else { - /* Grant locks if there are no conflicting locks ahead. - Move granted locks to the head of the list. */ - for (lock = lock_rec_get_first_on_page_addr(space, page_no); - lock != NULL;) { - - /* If the lock is a wait lock on this page, and it does not need to wait. */ - if ((lock->un_member.rec_lock.space == space) - && (lock->un_member.rec_lock.page_no == page_no) - && lock_get_wait(lock) - && !lock_rec_has_to_wait_in_queue(lock)) { - - lock_grant(lock); - - if (previous != NULL) { - /* Move the lock to the head of the list. */ - HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock); - lock_rec_move_to_front(lock, rec_fold); - } else { - /* Already at the head of the list. */ - previous = lock; - } - /* Move on to the next lock. */ - lock = static_cast(HASH_GET_NEXT(hash, previous)); - } else { - previous = lock; - lock = static_cast(HASH_GET_NEXT(hash, lock)); - } - } + lock_grant_and_move_on_page(space, page_no); } } @@ -5370,12 +5510,71 @@ lock_table_dequeue( /* Grant the lock */ ut_ad(in_lock->trx != lock->trx); - lock_grant(lock); + lock_grant(lock, false); } } } /*=========================== LOCK RELEASE ==============================*/ +static +void +lock_grant_and_move_on_rec( + lock_t* first_lock, + ulint heap_no) +{ + lock_t* lock; + lock_t* previous; + ulint space; + ulint page_no; + ulint rec_fold; + + space = first_lock->un_member.rec_lock.space; + page_no = first_lock->un_member.rec_lock.page_no; + rec_fold = lock_rec_fold(space, page_no); + + previous = (lock_t *) hash_get_nth_cell(lock_sys->rec_hash, + hash_calc_hash(rec_fold, lock_sys->rec_hash))->node; + if (previous == NULL) { + return; + } + if (previous == first_lock) { + lock = previous; + } else { + while (previous->hash && + previous->hash != first_lock) { + previous = (lock_t *) previous->hash; + } + lock = (lock_t *) previous->hash; + } + /* Grant locks if there are no conflicting locks ahead. + Move granted locks to the head of the list. */ + for (;lock != NULL;) { + + /* If the lock is a wait lock on this page, and it does not need to wait. */ + if (lock->un_member.rec_lock.space == space + && lock->un_member.rec_lock.page_no == page_no + && lock_rec_get_nth_bit(lock, heap_no) + && lock_get_wait(lock) + && !lock_rec_has_to_wait_in_queue(lock)) { + + lock_grant(lock, false); + + if (previous != NULL) { + /* Move the lock to the head of the list. */ + HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock); + lock_rec_insert_to_head(lock, rec_fold); + } else { + /* Already at the head of the list. */ + previous = lock; + } + /* Move on to the next lock. */ + lock = static_cast(HASH_GET_NEXT(hash, previous)); + } else { + previous = lock; + lock = static_cast(HASH_GET_NEXT(hash, lock)); + } + } +} /*************************************************************//** Removes a granted record lock of a transaction from the queue and grants @@ -5439,17 +5638,24 @@ released: ut_a(!lock_get_wait(lock)); lock_rec_reset_nth_bit(lock, heap_no); - /* Check if we can now grant waiting lock requests */ + if (innodb_lock_schedule_algorithm + == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS || + thd_is_replication_slave_thread(lock->trx->mysql_thd)) { - for (lock = first_lock; lock != NULL; - lock = lock_rec_get_next(heap_no, lock)) { - if (lock_get_wait(lock) - && !lock_rec_has_to_wait_in_queue(lock)) { + /* Check if we can now grant waiting lock requests */ - /* Grant the lock */ - ut_ad(trx != lock->trx); - lock_grant(lock); + for (lock = first_lock; lock != NULL; + lock = lock_rec_get_next(heap_no, lock)) { + if (lock_get_wait(lock) + && !lock_rec_has_to_wait_in_queue(lock)) { + + /* Grant the lock */ + ut_ad(trx != lock->trx); + lock_grant(lock, false); + } } + } else { + lock_grant_and_move_on_rec(first_lock, heap_no); } lock_mutex_exit(); @@ -6505,6 +6711,9 @@ lock_rec_queue_validate( } } + ut_ad(innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS || + lock_queue_validate(lock)); + func_exit: if (!locked_lock_trx_sys) { lock_mutex_exit(); diff --git a/storage/xtradb/trx/trx0trx.cc b/storage/xtradb/trx/trx0trx.cc index 41f8c166190..ec57a8e5c54 100644 --- a/storage/xtradb/trx/trx0trx.cc +++ b/storage/xtradb/trx/trx0trx.cc @@ -1117,6 +1117,8 @@ trx_start_low( trx->start_time = ut_time(); + trx->start_time_micro = clock(); + MONITOR_INC(MONITOR_TRX_ACTIVE); }