mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 02:05:57 +01:00
A few fixes for VATS in 10.1
This commit is contained in:
parent
183c02839f
commit
74961760a4
12 changed files with 709 additions and 268 deletions
1
libmariadb
Submodule
1
libmariadb
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit c8dd0899d484ad698ec2da5bc8e3d19ff8b623b9
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
/******************************************************************//**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<lock_t *>(HASH_GET_NEXT(hash, previous));
|
||||
} else {
|
||||
previous = lock;
|
||||
lock = static_cast<lock_t *>(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<lock_t *>(HASH_GET_NEXT(hash, previous));
|
||||
} else {
|
||||
previous = lock;
|
||||
lock = static_cast<lock_t *>(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<lock_t *>(HASH_GET_NEXT(hash, previous));
|
||||
} else {
|
||||
previous = lock;
|
||||
lock = static_cast<lock_t *>(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();
|
||||
|
|
|
@ -919,6 +919,8 @@ trx_start_low(
|
|||
|
||||
trx->start_time = ut_time();
|
||||
|
||||
trx->start_time_micro = clock();
|
||||
|
||||
MONITOR_INC(MONITOR_TRX_ACTIVE);
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
/******************************************************************//**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<lock_t *>(HASH_GET_NEXT(hash, previous));
|
||||
} else {
|
||||
previous = lock;
|
||||
lock = static_cast<lock_t *>(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<lock_t *>(HASH_GET_NEXT(hash, previous));
|
||||
} else {
|
||||
previous = lock;
|
||||
lock = static_cast<lock_t *>(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<lock_t *>(HASH_GET_NEXT(hash, previous));
|
||||
} else {
|
||||
previous = lock;
|
||||
lock = static_cast<lock_t *>(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();
|
||||
|
|
|
@ -1117,6 +1117,8 @@ trx_start_low(
|
|||
|
||||
trx->start_time = ut_time();
|
||||
|
||||
trx->start_time_micro = clock();
|
||||
|
||||
MONITOR_INC(MONITOR_TRX_ACTIVE);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue