Commit b4fb15cc authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-16664: Remove innodb_lock_schedule_algorithm

The setting innodb_lock_schedule_algorithm=VATS that was introduced
in MDEV-11039 (commit 021212b5)
causes conflicting exclusive locks to be incorrectly granted to
two transactions. Specifically, in lock_rec_insert_by_trx_age()
the predicate !lock_rec_has_to_wait_in_queue(in_lock) would hold even
though an active transaction is already holding an exclusive lock.
This was observed between two DELETE of the same clustered index record.
The HASH_DELETE invocation in lock_rec_enqueue_waiting() may be related.

Due to lack of progress in diagnosing the problem, we will remove the
option. The unsafe option was enabled by default between
commit 0c15d1a6 (MariaDB 10.2.3)
and the parent of
commit 1cc1d042 (MariaDB 10.2.17, 10.3.9),
and it was deprecated in
commit 295e2d50 (MariaDB 10.2.34).
parent 3b72b35a
......@@ -10,9 +10,6 @@ wsrep-provider=@ENV.WSREP_PROVIDER
wsrep_node_address=127.0.0.1
# enforce read-committed characteristics across the cluster
wsrep-sync-wait=15
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.1]
loose-innodb
......@@ -28,9 +25,6 @@ wsrep_sst_receive_address='127.0.0.1:@mysqld.1.#sst_port'
# enforce read-committed characteristics across the cluster
wsrep_causal_reads=ON
wsrep_sync_wait = 15
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.2]
loose-innodb
......@@ -54,9 +48,6 @@ wsrep_sst_receive_address='127.0.0.2:@mysqld.2.#sst_port'
# enforce read-committed characteristics across the cluster
wsrep_causal_reads=ON
wsrep_sync_wait = 15
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[ENV]
NODE_MYPORT_1= @mysqld.1.port
......
......@@ -11,9 +11,6 @@ log-bin=mysqld-bin
binlog-format=row
innodb-autoinc-lock-mode=2
default-storage-engine=innodb
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.1]
#galera_port=@OPT.port
......@@ -33,9 +30,6 @@ wsrep_node_incoming_address=127.0.0.1:@mysqld.1.port
# enforce read-committed characteristics across the cluster
wsrep-causal-reads=ON
wsrep-sync-wait=15
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.2]
#galera_port=@OPT.port
......@@ -55,15 +49,9 @@ wsrep_node_incoming_address=127.0.0.1:@mysqld.2.port
# enforce read-committed characteristics across the cluster
wsrep-causal-reads=ON
wsrep-sync-wait=15
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.3]
server-id=3
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[ENV]
NODE_MYPORT_1= @mysqld.1.port
......
......@@ -31,9 +31,6 @@ wsrep_sst_receive_address='127.0.0.1:@mysqld.1.#sst_port'
wsrep-causal-reads=ON
wsrep-sync-wait=15
server-id=1
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.2]
#galera_port=@OPT.port
......
......@@ -8,9 +8,6 @@
[mysqld]
log-bin
binlog-format=row
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.1]
#galera_port=@OPT.port
......@@ -34,9 +31,6 @@ wsrep_sst_receive_address='127.0.0.1:@mysqld.1.#sst_port'
wsrep-causal-reads=ON
wsrep-sync-wait=15
server-id=1
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.2]
#galera_port=@OPT.port
......@@ -60,9 +54,6 @@ wsrep_sst_receive_address='127.0.0.1:@mysqld.2.#sst_port'
wsrep-causal-reads=ON
wsrep-sync-wait=15
server-id=2
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.3]
#galera_port=@OPT.port
......@@ -86,15 +77,9 @@ wsrep_sst_receive_address='127.0.0.1:@mysqld.3.#sst_port'
wsrep-causal-reads=ON
wsrep-sync-wait=15
server-id=3
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.4]
server-id=4
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[ENV]
NODE_MYPORT_1= @mysqld.1.port
......
......@@ -10,9 +10,6 @@ wsrep_node_address=127.0.0.1
# enforce read-committed characteristics across the cluster
wsrep-causal-reads=ON
wsrep-sync-wait=15
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.1]
#galera_port=@OPT.port
......@@ -23,9 +20,6 @@ wsrep-cluster-address=gcomm://
wsrep_provider_options='base_port=@mysqld.1.#galera_port;gcache.size=10M'
wsrep_node_incoming_address=127.0.0.1:@mysqld.1.port
wsrep_sst_receive_address='127.0.0.1:@mysqld.1.#sst_port'
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.2]
#galera_port=@OPT.port
......@@ -36,9 +30,6 @@ wsrep_cluster_address='gcomm://127.0.0.1:@mysqld.1.#galera_port'
wsrep_provider_options='base_port=@mysqld.2.#galera_port;gcache.size=10M'
wsrep_node_incoming_address=127.0.0.1:@mysqld.2.port
wsrep_sst_receive_address='127.0.0.1:@mysqld.2.#sst_port'
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.3]
......@@ -50,9 +41,6 @@ wsrep_cluster_address='gcomm://127.0.0.1:@mysqld.1.#galera_port'
wsrep_provider_options='base_port=@mysqld.3.#galera_port;gcache.size=10M'
wsrep_node_incoming_address=127.0.0.1:@mysqld.3.port
wsrep_sst_receive_address='127.0.0.1:@mysqld.3.#sst_port'
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[mysqld.4]
......@@ -64,9 +52,6 @@ wsrep_cluster_address='gcomm://127.0.0.1:@mysqld.1.#galera_port'
wsrep_provider_options='base_port=@mysqld.4.#galera_port;gcache.size=10M'
wsrep_node_incoming_address=127.0.0.1:@mysqld.4.port
wsrep_sst_receive_address='127.0.0.1:@mysqld.4.#sst_port'
# lock schedule alg appears to be VATS by default, and it is not
# yet compatible with galera
innodb_lock_schedule_algorithm=FCFS
[ENV]
NODE_MYPORT_1= @mysqld.1.port
......
CREATE TABLE t1 (i1 INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2);
CREATE TABLE t2 (i2 int) ENGINE=MyISAM;
BEGIN;
DELETE FROM t1;
connect con1,localhost,root,,test;
connection con1;
BEGIN;
INSERT INTO t2 VALUES (1),(2);
SELECT * from t1;
i1
1
2
UPDATE t1 SET i1 = 1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
COMMIT;
connection default;
COMMIT;
SELECT * FROM t1;
i1
SELECT * FROM t2;
i2
1
2
DROP TABLE t1, t2;
disconnect con1;
CREATE TABLE t1 (i1 INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2);
CREATE TABLE t2 (i2 int) ENGINE=MyISAM;
BEGIN;
DELETE FROM t1;
connect con1,localhost,root,,test;
connection con1;
BEGIN;
INSERT INTO t2 VALUES (1),(2);
SELECT * FROM t1;
i1
1
2
UPDATE t1 SET i1 = 1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
connection default;
COMMIT;
connection con1;
COMMIT;
connection default;
SELECT * FROM t1;
i1
SELECT * FROM t2;
i2
1
2
DROP TABLE t1, t2;
disconnect con1;
# "restart: --loose-innodb-lock-schedule-algorithm=FCFS"
# restart: --loose_innodb_lock_schedule_algorithm=FCFS
CREATE TABLE t1 (i1 INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2);
CREATE TABLE t2 (i2 int) ENGINE=MyISAM;
BEGIN;
DELETE FROM t1;
connect con1,localhost,root,,test;
connection con1;
BEGIN;
INSERT INTO t2 VALUES (1),(2);
SELECT * from t1;
i1
1
2
UPDATE t1 SET i1 = 1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
COMMIT;
connection default;
COMMIT;
SELECT * FROM t1;
i1
SELECT * FROM t2;
i2
1
2
DROP TABLE t1, t2;
disconnect con1;
CREATE TABLE t1 (i1 INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2);
CREATE TABLE t2 (i2 int) ENGINE=MyISAM;
BEGIN;
DELETE FROM t1;
connect con1,localhost,root,,test;
connection con1;
BEGIN;
INSERT INTO t2 VALUES (1),(2);
SELECT * FROM t1;
i1
1
2
UPDATE t1 SET i1 = 1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
connection default;
COMMIT;
connection con1;
COMMIT;
connection default;
SELECT * FROM t1;
i1
SELECT * FROM t2;
i2
1
2
DROP TABLE t1, t2;
disconnect con1;
--loose-innodb-lock-wait-timeout=1
--loose-innodb-lock-schedule-algorithm=VATS
--source include/have_innodb.inc
CREATE TABLE t1 (i1 INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2);
CREATE TABLE t2 (i2 int) ENGINE=MyISAM;
BEGIN;
DELETE FROM t1;
--connect (con1,localhost,root,,test)
connection con1;
BEGIN;
INSERT INTO t2 VALUES (1),(2);
SELECT * from t1;
--error 1205
UPDATE t1 SET i1 = 1;
COMMIT;
connection default;
COMMIT;
SELECT * FROM t1;
SELECT * FROM t2;
DROP TABLE t1, t2;
disconnect con1;
CREATE TABLE t1 (i1 INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2);
CREATE TABLE t2 (i2 int) ENGINE=MyISAM;
BEGIN;
DELETE FROM t1;
--connect (con1,localhost,root,,test)
connection con1;
BEGIN;
INSERT INTO t2 VALUES (1),(2);
SELECT * FROM t1;
--error 1205
UPDATE t1 SET i1 = 1;
connection default;
COMMIT;
connection con1;
COMMIT;
connection default;
SELECT * FROM t1;
SELECT * FROM t2;
DROP TABLE t1, t2;
disconnect con1;
--echo # "restart: --loose-innodb-lock-schedule-algorithm=FCFS"
--let $restart_parameters=--loose_innodb_lock_schedule_algorithm=FCFS
-- source include/restart_mysqld.inc
CREATE TABLE t1 (i1 INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2);
CREATE TABLE t2 (i2 int) ENGINE=MyISAM;
BEGIN;
DELETE FROM t1;
--connect (con1,localhost,root,,test)
connection con1;
BEGIN;
INSERT INTO t2 VALUES (1),(2);
SELECT * from t1;
--error 1205
UPDATE t1 SET i1 = 1;
COMMIT;
connection default;
COMMIT;
SELECT * FROM t1;
SELECT * FROM t2;
DROP TABLE t1, t2;
disconnect con1;
CREATE TABLE t1 (i1 INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1),(2);
CREATE TABLE t2 (i2 int) ENGINE=MyISAM;
BEGIN;
DELETE FROM t1;
--connect (con1,localhost,root,,test)
connection con1;
BEGIN;
INSERT INTO t2 VALUES (1),(2);
SELECT * FROM t1;
--error 1205
UPDATE t1 SET i1 = 1;
connection default;
COMMIT;
connection con1;
COMMIT;
connection default;
SELECT * FROM t1;
SELECT * FROM t2;
DROP TABLE t1, t2;
disconnect con1;
--loose-innodb-lock-schedule-algorithm=FCFS
......@@ -1029,18 +1029,6 @@ NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME INNODB_LOCK_SCHEDULE_ALGORITHM
SESSION_VALUE NULL
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 YES
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME INNODB_LOCK_WAIT_TIMEOUT
SESSION_VALUE 50
DEFAULT_VALUE 50
......
......@@ -5043,6 +5043,7 @@ static int init_server_components()
MARIADB_REMOVED_OPTION("innodb-concurrency-tickets"),
MARIADB_REMOVED_OPTION("innodb-file-format"),
MARIADB_REMOVED_OPTION("innodb-large-prefix"),
MARIADB_REMOVED_OPTION("innodb-lock-schedule-algorithm"),
MARIADB_REMOVED_OPTION("innodb-log-checksums"),
MARIADB_REMOVED_OPTION("innodb-log-compressed-pages"),
MARIADB_REMOVED_OPTION("innodb-log-files-in-group"),
......
......@@ -5037,15 +5037,6 @@ extern "C" enum enum_server_command thd_current_command(MYSQL_THD thd)
return thd->get_command();
}
extern "C" int thd_slave_thread(const MYSQL_THD thd)
{
return(thd->slave_thread);
}
/* Returns high resolution timestamp for the start
of the current query. */
extern "C" unsigned long long thd_start_utime(const MYSQL_THD thd)
......
......@@ -97,12 +97,6 @@ static void wsrep_set_wsrep_on()
strcmp(wsrep_provider, WSREP_NONE);
}
/* This is intentionally declared as a weak global symbol, so that
linking will succeed even if the server is built with a dynamically
linked InnoDB. */
ulong innodb_lock_schedule_algorithm __attribute__((weak));
struct handlerton* innodb_hton_ptr __attribute__((weak));
bool wsrep_on_update (sys_var *self, THD* thd, enum_var_type var_type)
{
if (var_type == OPT_GLOBAL) {
......@@ -138,18 +132,7 @@ bool wsrep_on_update (sys_var *self, THD* thd, enum_var_type var_type)
bool wsrep_on_check(sys_var *self, THD* thd, set_var* var)
{
bool new_wsrep_on= (bool)var->save_result.ulonglong_value;
if (check_has_super(self, thd, var))
return true;
if (new_wsrep_on && innodb_hton_ptr && innodb_lock_schedule_algorithm != 0) {
my_message(ER_WRONG_ARGUMENTS, " WSREP (galera) can't be enabled "
"if innodb_lock_schedule_algorithm=VATS. Please configure"
" innodb_lock_schedule_algorithm=FCFS and restart.", MYF(0));
return true;
}
return false;
return check_has_super(self, thd, var);
}
bool wsrep_causal_reads_update (sys_var *self, THD* thd, enum_var_type var_type)
......
......@@ -326,22 +326,6 @@ static TYPELIB innodb_default_row_format_typelib = {
NULL
};
/** Possible values of the parameter innodb_lock_schedule_algorithm */
static const char* innodb_lock_schedule_algorithm_names[] = {
"fcfs",
"vats",
NullS
};
/** Used to define an enumerate type of the system variable
innodb_lock_schedule_algorithm. */
static TYPELIB innodb_lock_schedule_algorithm_typelib = {
array_elements(innodb_lock_schedule_algorithm_names) - 1,
"innodb_lock_schedule_algorithm_typelib",
innodb_lock_schedule_algorithm_names,
NULL
};
/** Names of allowed values of innodb_flush_method */
const char* innodb_flush_method_names[] = {
"fsync",
......@@ -1400,18 +1384,6 @@ innodb_page_size_validate(
DBUG_RETURN(0);
}
/******************************************************************//**
Returns true if the thread is the replication thread on the slave
server.
@return true if thd is the replication thread */
ibool
thd_is_replication_slave_thread(
/*============================*/
THD* thd) /*!< in: thread handle */
{
return thd && ((ibool) thd_slave_thread(thd));
}
/******************************************************************//**
Returns true if transaction should be flagged as read-only.
@return true if the thd is marked as read-only */
......@@ -3205,15 +3177,6 @@ static int innodb_init_params()
DBUG_RETURN(HA_ERR_INITIALIZATION);
}
#ifdef WITH_WSREP
/* Currently, Galera does not support VATS lock schedule algorithm. */
if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
&& global_system_variables.wsrep_on) {
ib::info() << "For Galera, using innodb_lock_schedule_algorithm=fcfs";
innodb_lock_schedule_algorithm = INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS;
}
#endif /* WITH_WSREP */
#ifndef HAVE_LZ4
if (innodb_compression_algorithm == PAGE_LZ4_ALGORITHM) {
sql_print_error("InnoDB: innodb_compression_algorithm = %lu unsupported.\n"
......@@ -18930,18 +18893,6 @@ static MYSQL_SYSVAR_ULONG(doublewrite_batch_size, srv_doublewrite_batch_size,
NULL, NULL, 120, 1, 127, 0);
#endif /* defined UNIV_DEBUG || defined UNIV_PERF_DEBUG */
static MYSQL_SYSVAR_ENUM(lock_schedule_algorithm, innodb_lock_schedule_algorithm,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
"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.",
NULL, NULL, INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS,
&innodb_lock_schedule_algorithm_typelib);
static MYSQL_SYSVAR_STR(buffer_pool_filename, srv_buf_dump_filename,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
"Filename to/from which to dump/load the InnoDB buffer pool",
......@@ -19639,7 +19590,6 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(ft_num_word_optimize),
MYSQL_SYSVAR(ft_sort_pll_degree),
MYSQL_SYSVAR(force_load_corrupted),
MYSQL_SYSVAR(lock_schedule_algorithm),
MYSQL_SYSVAR(lock_wait_timeout),
MYSQL_SYSVAR(deadlock_detect),
MYSQL_SYSVAR(page_size),
......
......@@ -530,12 +530,6 @@ the definitions are bracketed with #ifdef INNODB_COMPATIBILITY_HOOKS */
extern "C" {
/** Check if a user thread is a replication slave thread
@param thd user thread
@retval 0 the user thread is not a replication slave thread
@retval 1 the user thread is a replication slave thread */
int thd_slave_thread(const MYSQL_THD thd);
/** Check if a user thread is running a non-transactional update
@param thd user thread
@retval 0 the user thread is not running a non-transactional update
......
......@@ -108,15 +108,6 @@ innobase_convert_name(
ulint idlen, /*!< in: length of id, in bytes */
THD* thd); /*!< in: MySQL connection thread, or NULL */
/******************************************************************//**
Returns true if the thread is the replication thread on the slave
server.
@return true if thd is the replication thread */
ibool
thd_is_replication_slave_thread(
/*============================*/
THD* thd); /*!< in: thread handle */
/******************************************************************//**
Returns true if the transaction this thread is processing has edited
non-transactional tables. Used by the deadlock detector when deciding
......
......@@ -39,17 +39,6 @@ Created 5/7/1996 Heikki Tuuri
#include "gis0rtree.h"
#include "lock0prdt.h"
/** Alternatives for innodb_lock_schedule_algorithm, which can be changed by
setting innodb_lock_schedule_algorithm. */
enum innodb_lock_schedule_algorithm_t {
/*!< First Come First Served */
INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS,
/*!< Variance-Aware-Transaction-Scheduling */
INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
};
extern ulong innodb_lock_schedule_algorithm;
// Forward declaration
class ReadView;
......
......@@ -49,9 +49,6 @@ Created 5/7/1996 Heikki Tuuri
#include <mysql/service_wsrep.h>
#endif /* WITH_WSREP */
/** Lock scheduling algorithm */
ulong innodb_lock_schedule_algorithm;
/** The value of innodb_deadlock_detect */
my_bool innobase_deadlock_detect;
......@@ -554,12 +551,6 @@ lock_get_size(void)
return((ulint) sizeof(lock_t));
}
static inline void lock_grant_have_trx_mutex(lock_t* lock)
{
lock_reset_lock_and_trx_wait(lock);
lock_grant_after_reset(lock);
}
/*********************************************************************//**
Gets the gap flag of a record lock.
@return LOCK_GAP or 0 */
......@@ -1396,16 +1387,8 @@ lock_rec_create_low(
trx_mutex_exit(c_lock->trx);
} else
#endif /* WITH_WSREP */
if (!(type_mode & (LOCK_WAIT | LOCK_PREDICATE | LOCK_PRDT_PAGE))
&& innodb_lock_schedule_algorithm
== INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
&& !thd_is_replication_slave_thread(trx->mysql_thd)) {
HASH_PREPEND(lock_t, hash, &lock_sys.rec_hash,
page_id.fold(), lock);
} else {
HASH_INSERT(lock_t, hash, lock_hash_get(type_mode),
page_id.fold(), lock);
}
if (!holds_trx_mutex) {
trx_mutex_enter(trx);
......@@ -1424,139 +1407,6 @@ lock_rec_create_low(
return lock;
}
/*********************************************************************//**
Check if lock1 has higher priority than lock2.
NULL has lowest priority.
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.
If either is a high priority transaction, the lock has higher priority.
Otherwise, the one with an older transaction has higher priority.
@returns true if lock1 has higher priority, false otherwise. */
static bool has_higher_priority(lock_t *lock1, lock_t *lock2)
{
if (lock1 == NULL) {
return false;
} else if (lock2 == NULL) {
return true;
}
// Granted locks has higher priority.
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 */{
lock_t* node;
lock_t* next;
hash_table_t* hash;
hash_cell_t* cell;
ut_ad(!in_lock->trx->is_wsrep());
const page_id_t page_id(in_lock->un_member.rec_lock.page_id);
hash = lock_hash_get(in_lock->type_mode);
cell = &hash->array[hash->calc_hash(page_id.fold())];
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_have_trx_mutex(in_lock);
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_have_trx_mutex(in_lock);
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;
}
#ifdef UNIV_DEBUG
static
bool
lock_queue_validate(
const lock_t *in_lock) /*!< in: lock whose hash list is to be validated */
{
hash_table_t* hash;
hash_cell_t* cell;
lock_t* next;
bool wait_lock __attribute__((unused))= false;
if (in_lock == NULL) {
return true;
}
const page_id_t page_id(in_lock->un_member.rec_lock.page_id);
hash = lock_hash_get(in_lock->type_mode);
cell = &hash->array[hash->calc_hash(page_id.fold())];
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 = next->hash;
}
return true;
}
#endif /* UNIV_DEBUG */
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_table_t* hash;
hash_cell_t* cell;
lock_t* node;
if (in_lock == NULL) {
return;
}
hash = lock_hash_get(in_lock->type_mode);
cell = &hash->array[hash->calc_hash(rec_fold)];
node = (lock_t *) cell->node;
if (node != in_lock) {
cell->node = in_lock;
in_lock->hash = node;
}
}
/** Enqueue a waiting request for a lock which cannot be granted immediately.
Check for deadlocks.
@param[in] type_mode the requested lock mode (LOCK_S or LOCK_X)
......@@ -1661,18 +1511,6 @@ lock_rec_enqueue_waiting(
MONITOR_INC(MONITOR_LOCKREC_WAIT);
if (innodb_lock_schedule_algorithm
== INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
&& !prdt
&& !thd_is_replication_slave_thread(lock->trx->mysql_thd)) {
HASH_DELETE(lock_t, hash, &lock_sys.rec_hash,
lock_rec_lock_fold(lock), lock);
dberr_t res = lock_rec_insert_by_trx_age(lock);
if (res != DB_SUCCESS) {
return res;
}
}
return DB_LOCK_WAIT;
}
......@@ -2062,54 +1900,6 @@ lock_rec_cancel(
trx_mutex_exit(lock->trx);
}
static void lock_grant_and_move_on_page(ulint rec_fold, const page_id_t id)
{
lock_t* lock;
lock_t* previous = static_cast<lock_t*>(
lock_sys.rec_hash.array[lock_sys.rec_hash.calc_hash(rec_fold)].
node);
if (previous == NULL) {
return;
}
if (previous->un_member.rec_lock.page_id == id) {
lock = previous;
}
else {
while (previous->hash &&
(previous->hash->un_member.rec_lock.page_id != id)) {
previous = previous->hash;
}
lock = previous->hash;
}
ut_ad(!lock->trx->is_wsrep());
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. */
while (lock) {
/* If the lock is a wait lock on this page, and it does not need to wait. */
if (lock_get_wait(lock)
&& lock->un_member.rec_lock.page_id == id
&& !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_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));
}
}
}
/** Remove a record lock request, waiting or granted, from the queue and
grant locks to other transactions in the queue if they now are entitled
to a lock. NOTE: all record locks contained in in_lock are removed.
......@@ -2136,34 +1926,27 @@ static void lock_rec_dequeue_from_page(lock_t* in_lock)
MONITOR_INC(MONITOR_RECLOCK_REMOVED);
MONITOR_DEC(MONITOR_NUM_RECLOCK);
if (innodb_lock_schedule_algorithm
== INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS
|| lock_hash != &lock_sys.rec_hash
|| 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. */
/* 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. */
for (lock_t* lock = lock_sys.get_first(*lock_hash, page_id);
lock != NULL;
lock = lock_rec_get_next_on_page(lock)) {
for (lock_t* lock = lock_sys.get_first(*lock_hash, page_id);
lock != NULL;
lock = lock_rec_get_next_on_page(lock)) {
if (!lock_get_wait(lock)) {
continue;
}
const lock_t* c = lock_rec_has_to_wait_in_queue(lock);
if (!c) {
/* Grant the lock */
ut_ad(lock->trx != in_lock->trx);
lock_grant(lock);
if (!lock_get_wait(lock)) {
continue;
}
const lock_t* c = lock_rec_has_to_wait_in_queue(lock);
if (!c) {
/* Grant the lock */
ut_ad(lock->trx != in_lock->trx);
lock_grant(lock);
#ifdef WITH_WSREP
} else {
wsrep_assert_no_bf_bf_wait(c, lock, c->trx);
} else {
wsrep_assert_no_bf_bf_wait(c, lock, c->trx);
#endif /* WITH_WSREP */
}
}
} else {
lock_grant_and_move_on_page(rec_fold, page_id);
}
}
......@@ -3940,59 +3723,6 @@ lock_table_for_trx(
}
/*=========================== LOCK RELEASE ==============================*/
static
void
lock_grant_and_move_on_rec(
lock_t* first_lock,
ulint heap_no)
{
lock_t* lock;
const page_id_t page_id(first_lock->un_member.rec_lock.page_id);
const ulint rec_fold= page_id.fold();
lock_t* previous = static_cast<lock_t*>(
lock_sys.rec_hash.array[lock_sys.hash(page_id)]
.node);
if (previous == NULL) {
return;
}
if (previous == first_lock) {
lock = previous;
} else {
while (previous->hash &&
previous->hash != first_lock) {
previous = previous->hash;
}
lock = previous->hash;
}
ut_ad(!lock->trx->is_wsrep());
/* 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.page_id == page_id
&& lock_rec_get_nth_bit(lock, heap_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_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
......@@ -4055,30 +3785,23 @@ lock_rec_unlock(
ut_a(!lock_get_wait(lock));
lock_rec_reset_nth_bit(lock, heap_no);
if (innodb_lock_schedule_algorithm
== INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS ||
thd_is_replication_slave_thread(lock->trx->mysql_thd)) {
/* Check if we can now grant waiting lock requests */
/* Check if we can now grant waiting lock requests */
for (lock = first_lock; lock != NULL;
lock = lock_rec_get_next(heap_no, lock)) {
if (!lock_get_wait(lock)) {
continue;
}
const lock_t* c = lock_rec_has_to_wait_in_queue(lock);
if (!c) {
/* 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)) {
continue;
}
const lock_t* c = lock_rec_has_to_wait_in_queue(lock);
if (!c) {
/* Grant the lock */
ut_ad(trx != lock->trx);
lock_grant(lock);
#ifdef WITH_WSREP
} else {
wsrep_assert_no_bf_bf_wait(c, lock, c->trx);
} else {
wsrep_assert_no_bf_bf_wait(c, lock, c->trx);
#endif /* WITH_WSREP */
}
}
} else {
lock_grant_and_move_on_rec(first_lock, heap_no);
}
lock_mutex_exit();
......@@ -4810,9 +4533,6 @@ lock_rec_queue_validate(
}
}
ut_ad(innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS ||
lock_queue_validate(lock));
goto func_exit;
}
......@@ -6361,10 +6081,7 @@ DeadlockChecker::get_first_lock(ulint* heap_no) const
/* Must find at least two locks, otherwise there cannot be a
waiting lock, secondly the first lock cannot be the wait_lock. */
ut_a(lock != NULL);
ut_a(lock != m_wait_lock ||
(innodb_lock_schedule_algorithm
== INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
&& !thd_is_replication_slave_thread(lock->trx->mysql_thd)));
ut_a(lock != m_wait_lock);
/* Check that the lock type doesn't change. */
ut_ad(lock_get_type_low(lock) == lock_get_type_low(m_wait_lock));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment