Skip to content

Commit 9b439ee

Browse files
committed
Add option to exclude tables from gap lock check
Summary: Similar to what was done in D49035, a new sysvar was added called `gap_lock_exceptions` that takes a comma separated list of regexes that specify which tables should skip gap lock detection. If it is skipped, then we do not raise errors, and we do not log to the gaplock log. In order to do this, I've moved some logic from the collations check to `sql/handler.cc`. I've also made changes the mutex that protected the table list into a rwlock. Fixes #232 Test Plan: mtr Reviewers: yoshinorim, hermanlee4, jkedgar Reviewed By: jkedgar Subscribers: webscalesql-eng Differential Revision: https://reviews.facebook.net/D56937
1 parent 292d546 commit 9b439ee

15 files changed

+401
-93
lines changed

mysql-test/r/mysqld--help-notwin-profiling.result

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ The following options may be given as the first argument:
290290
Number of best matches to use for query expansion
291291
--ft-stopword-file=name
292292
Use stopwords from this file instead of built-in list
293+
--gap-lock-exceptions[=name]
294+
List of tables (using regex) that are excluded from gap
295+
lock detection.
293296
--gap-lock-log-file=name
294297
Log file path where queries using Gap Lock are written.
295298
gap_lock_write_log needs to be turned on to write logs
@@ -1563,6 +1566,7 @@ ft-max-word-len 84
15631566
ft-min-word-len 4
15641567
ft-query-expansion-limit 20
15651568
ft-stopword-file (No default value)
1569+
gap-lock-exceptions (No default value)
15661570
gap-lock-raise-error FALSE
15671571
gap-lock-write-log FALSE
15681572
gdb FALSE

mysql-test/r/mysqld--help-notwin.result

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ The following options may be given as the first argument:
290290
Number of best matches to use for query expansion
291291
--ft-stopword-file=name
292292
Use stopwords from this file instead of built-in list
293+
--gap-lock-exceptions[=name]
294+
List of tables (using regex) that are excluded from gap
295+
lock detection.
293296
--gap-lock-log-file=name
294297
Log file path where queries using Gap Lock are written.
295298
gap_lock_write_log needs to be turned on to write logs
@@ -1561,6 +1564,7 @@ ft-max-word-len 84
15611564
ft-min-word-len 4
15621565
ft-query-expansion-limit 20
15631566
ft-stopword-file (No default value)
1567+
gap-lock-exceptions (No default value)
15641568
gap-lock-raise-error FALSE
15651569
gap-lock-write-log FALSE
15661570
gdb FALSE

mysql-test/suite/innodb/r/gap_lock_raise_error.result

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,3 +400,77 @@ DROP DATABASE mysqlslap;
400400
SET GLOBAL gap_lock_log_file='<GAP_LOCK_ORIG>';
401401
SET GLOBAL gap_lock_log_file='<GAP_LOCK>';
402402
flush general logs;
403+
SET @save_gap_lock_exceptions = @@global.gap_lock_exceptions;
404+
SET GLOBAL gap_lock_exceptions="t.*";
405+
drop table if exists gap1,gap2,gap3;
406+
CREATE DATABASE mysqlslap;
407+
CREATE TABLE gap1 (id1 INT, id2 INT, id3 INT, c1 INT, value INT,
408+
PRIMARY KEY (id1, id2, id3),
409+
INDEX i (c1)) ENGINE=innodb;
410+
CREATE TABLE gap2 like gap1;
411+
CREATE TABLE gap3 (id INT, value INT,
412+
PRIMARY KEY (id),
413+
UNIQUE KEY ui(value)) ENGINE=innodb;
414+
insert into gap3 values (1,1), (2,2),(3,3),(4,4),(5,5);
415+
create table gap4 (
416+
pk int primary key,
417+
a int,
418+
b int,
419+
key(a)
420+
) ENGINE=innodb;
421+
insert into gap4 values (1,1,1), (2,2,2), (3,3,3), (4,4,4);
422+
create table gap5 like gap4;
423+
insert into gap5 values (1,1,1), (2,2,2), (3,3,3), (4,4,4);
424+
set session gap_lock_raise_error=1;
425+
set session gap_lock_write_log=1;
426+
set session autocommit=0;
427+
select * from gap1 limit 1 for update;
428+
ERROR HY000: Using Gap Lock without full primary key in multi-table or multi-statement transactions is not allowed. You need either 1: Execute 'SET SESSION gap_lock_raise_error=0' if you are sure that your application does not rely on Gap Lock. 2: Rewrite queries to use all primary key columns in WHERE equal conditions. 3: Rewrite to single-table, single-statement transaction. Query: select * from gap1 limit 1 for update
429+
select * from gap1 where value != 100 limit 1 for update;
430+
ERROR HY000: Using Gap Lock without full primary key in multi-table or multi-statement transactions is not allowed. You need either 1: Execute 'SET SESSION gap_lock_raise_error=0' if you are sure that your application does not rely on Gap Lock. 2: Rewrite queries to use all primary key columns in WHERE equal conditions. 3: Rewrite to single-table, single-statement transaction. Query: select * from gap1 where value != 100 limit 1 for update
431+
set global gap_lock_write_log= 0;
432+
set global gap_lock_raise_error= 0;
433+
drop table if exists gap1, gap2, gap3, gap4, gap5;
434+
DROP DATABASE mysqlslap;
435+
0
436+
SET GLOBAL gap_lock_log_file='<GAP_LOCK_ORIG>';
437+
SET GLOBAL gap_lock_log_file='<GAP_LOCK>';
438+
flush general logs;
439+
SET GLOBAL gap_lock_exceptions="gap.*";
440+
drop table if exists gap1,gap2,gap3;
441+
CREATE DATABASE mysqlslap;
442+
CREATE TABLE gap1 (id1 INT, id2 INT, id3 INT, c1 INT, value INT,
443+
PRIMARY KEY (id1, id2, id3),
444+
INDEX i (c1)) ENGINE=innodb;
445+
CREATE TABLE gap2 like gap1;
446+
CREATE TABLE gap3 (id INT, value INT,
447+
PRIMARY KEY (id),
448+
UNIQUE KEY ui(value)) ENGINE=innodb;
449+
insert into gap3 values (1,1), (2,2),(3,3),(4,4),(5,5);
450+
create table gap4 (
451+
pk int primary key,
452+
a int,
453+
b int,
454+
key(a)
455+
) ENGINE=innodb;
456+
insert into gap4 values (1,1,1), (2,2,2), (3,3,3), (4,4,4);
457+
create table gap5 like gap4;
458+
insert into gap5 values (1,1,1), (2,2,2), (3,3,3), (4,4,4);
459+
set session gap_lock_raise_error=1;
460+
set session gap_lock_write_log=1;
461+
set session autocommit=0;
462+
select * from gap1 limit 1 for update;
463+
id1 id2 id3 c1 value
464+
0 0 1 1 1
465+
select * from gap1 where value != 100 limit 1 for update;
466+
id1 id2 id3 c1 value
467+
0 0 1 1 1
468+
set global gap_lock_write_log= 0;
469+
set global gap_lock_raise_error= 0;
470+
drop table if exists gap1, gap2, gap3, gap4, gap5;
471+
DROP DATABASE mysqlslap;
472+
0
473+
SET GLOBAL gap_lock_log_file='<GAP_LOCK_ORIG>';
474+
SET GLOBAL gap_lock_log_file='<GAP_LOCK>';
475+
flush general logs;
476+
SET GLOBAL gap_lock_exceptions=@save_gap_lock_exceptions;

mysql-test/suite/innodb/t/gap_lock_raise_error.test

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,26 @@
33
let $engine=innodb;
44
--source include/gap_lock_raise_error_all.inc
55

6+
SET @save_gap_lock_exceptions = @@global.gap_lock_exceptions;
67

8+
SET GLOBAL gap_lock_exceptions="t.*";
9+
--source include/gap_lock_raise_error_init.inc
10+
11+
set session autocommit=0;
12+
--error ER_UNKNOWN_ERROR
13+
select * from gap1 limit 1 for update;
14+
--error ER_UNKNOWN_ERROR
15+
select * from gap1 where value != 100 limit 1 for update;
16+
17+
--source include/gap_lock_raise_error_cleanup.inc
18+
19+
SET GLOBAL gap_lock_exceptions="gap.*";
20+
--source include/gap_lock_raise_error_init.inc
21+
22+
set session autocommit=0;
23+
select * from gap1 limit 1 for update;
24+
select * from gap1 where value != 100 limit 1 for update;
25+
26+
--source include/gap_lock_raise_error_cleanup.inc
27+
28+
SET GLOBAL gap_lock_exceptions=@save_gap_lock_exceptions;

mysql-test/suite/perfschema/r/dml_setup_instruments.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ wait/synch/rwlock/sql/Binlog_storage_delegate::lock YES YES
2525
wait/synch/rwlock/sql/Binlog_transmit_delegate::lock YES YES
2626
wait/synch/rwlock/sql/gtid_commit_rollback YES YES
2727
wait/synch/rwlock/sql/LOCK_dboptions YES YES
28+
wait/synch/rwlock/sql/LOCK_gap_lock_exceptions YES YES
2829
wait/synch/rwlock/sql/LOCK_grant YES YES
2930
wait/synch/rwlock/sql/LOCK_system_variables_hash YES YES
3031
wait/synch/rwlock/sql/LOCK_sys_init_connect YES YES
31-
wait/synch/rwlock/sql/LOCK_sys_init_slave YES YES
3232
select * from performance_schema.setup_instruments
3333
where name like 'Wait/Synch/Cond/sql/%'
3434
and name not in (

mysql-test/suite/rocksdb/r/gap_lock_raise_error.result

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,3 +400,77 @@ DROP DATABASE mysqlslap;
400400
SET GLOBAL gap_lock_log_file='<GAP_LOCK_ORIG>';
401401
SET GLOBAL gap_lock_log_file='<GAP_LOCK>';
402402
flush general logs;
403+
SET @save_gap_lock_exceptions = @@global.gap_lock_exceptions;
404+
SET GLOBAL gap_lock_exceptions="t.*";
405+
drop table if exists gap1,gap2,gap3;
406+
CREATE DATABASE mysqlslap;
407+
CREATE TABLE gap1 (id1 INT, id2 INT, id3 INT, c1 INT, value INT,
408+
PRIMARY KEY (id1, id2, id3),
409+
INDEX i (c1)) ENGINE=rocksdb;
410+
CREATE TABLE gap2 like gap1;
411+
CREATE TABLE gap3 (id INT, value INT,
412+
PRIMARY KEY (id),
413+
UNIQUE KEY ui(value)) ENGINE=rocksdb;
414+
insert into gap3 values (1,1), (2,2),(3,3),(4,4),(5,5);
415+
create table gap4 (
416+
pk int primary key,
417+
a int,
418+
b int,
419+
key(a)
420+
) ENGINE=rocksdb;
421+
insert into gap4 values (1,1,1), (2,2,2), (3,3,3), (4,4,4);
422+
create table gap5 like gap4;
423+
insert into gap5 values (1,1,1), (2,2,2), (3,3,3), (4,4,4);
424+
set session gap_lock_raise_error=1;
425+
set session gap_lock_write_log=1;
426+
set session autocommit=0;
427+
select * from gap1 limit 1 for update;
428+
ERROR HY000: Using Gap Lock without full primary key in multi-table or multi-statement transactions is not allowed. You need either 1: Execute 'SET SESSION gap_lock_raise_error=0' if you are sure that your application does not rely on Gap Lock. 2: Rewrite queries to use all primary key columns in WHERE equal conditions. 3: Rewrite to single-table, single-statement transaction. Query: select * from gap1 limit 1 for update
429+
select * from gap1 where value != 100 limit 1 for update;
430+
ERROR HY000: Using Gap Lock without full primary key in multi-table or multi-statement transactions is not allowed. You need either 1: Execute 'SET SESSION gap_lock_raise_error=0' if you are sure that your application does not rely on Gap Lock. 2: Rewrite queries to use all primary key columns in WHERE equal conditions. 3: Rewrite to single-table, single-statement transaction. Query: select * from gap1 where value != 100 limit 1 for update
431+
set global gap_lock_write_log= 0;
432+
set global gap_lock_raise_error= 0;
433+
drop table if exists gap1, gap2, gap3, gap4, gap5;
434+
DROP DATABASE mysqlslap;
435+
0
436+
SET GLOBAL gap_lock_log_file='<GAP_LOCK_ORIG>';
437+
SET GLOBAL gap_lock_log_file='<GAP_LOCK>';
438+
flush general logs;
439+
SET GLOBAL gap_lock_exceptions="gap.*";
440+
drop table if exists gap1,gap2,gap3;
441+
CREATE DATABASE mysqlslap;
442+
CREATE TABLE gap1 (id1 INT, id2 INT, id3 INT, c1 INT, value INT,
443+
PRIMARY KEY (id1, id2, id3),
444+
INDEX i (c1)) ENGINE=rocksdb;
445+
CREATE TABLE gap2 like gap1;
446+
CREATE TABLE gap3 (id INT, value INT,
447+
PRIMARY KEY (id),
448+
UNIQUE KEY ui(value)) ENGINE=rocksdb;
449+
insert into gap3 values (1,1), (2,2),(3,3),(4,4),(5,5);
450+
create table gap4 (
451+
pk int primary key,
452+
a int,
453+
b int,
454+
key(a)
455+
) ENGINE=rocksdb;
456+
insert into gap4 values (1,1,1), (2,2,2), (3,3,3), (4,4,4);
457+
create table gap5 like gap4;
458+
insert into gap5 values (1,1,1), (2,2,2), (3,3,3), (4,4,4);
459+
set session gap_lock_raise_error=1;
460+
set session gap_lock_write_log=1;
461+
set session autocommit=0;
462+
select * from gap1 limit 1 for update;
463+
id1 id2 id3 c1 value
464+
0 0 1 1 1
465+
select * from gap1 where value != 100 limit 1 for update;
466+
id1 id2 id3 c1 value
467+
0 0 1 1 1
468+
set global gap_lock_write_log= 0;
469+
set global gap_lock_raise_error= 0;
470+
drop table if exists gap1, gap2, gap3, gap4, gap5;
471+
DROP DATABASE mysqlslap;
472+
0
473+
SET GLOBAL gap_lock_log_file='<GAP_LOCK_ORIG>';
474+
SET GLOBAL gap_lock_log_file='<GAP_LOCK>';
475+
flush general logs;
476+
SET GLOBAL gap_lock_exceptions=@save_gap_lock_exceptions;

mysql-test/suite/rocksdb/t/gap_lock_raise_error.test

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,26 @@
33
let $engine=rocksdb;
44
--source include/gap_lock_raise_error_all.inc
55

6+
SET @save_gap_lock_exceptions = @@global.gap_lock_exceptions;
7+
8+
SET GLOBAL gap_lock_exceptions="t.*";
9+
--source include/gap_lock_raise_error_init.inc
610

11+
set session autocommit=0;
12+
--error ER_UNKNOWN_ERROR
13+
select * from gap1 limit 1 for update;
14+
--error ER_UNKNOWN_ERROR
15+
select * from gap1 where value != 100 limit 1 for update;
16+
17+
--source include/gap_lock_raise_error_cleanup.inc
18+
19+
SET GLOBAL gap_lock_exceptions="gap.*";
20+
--source include/gap_lock_raise_error_init.inc
21+
22+
set session autocommit=0;
23+
select * from gap1 limit 1 for update;
24+
select * from gap1 where value != 100 limit 1 for update;
25+
26+
--source include/gap_lock_raise_error_cleanup.inc
27+
28+
SET GLOBAL gap_lock_exceptions=@save_gap_lock_exceptions;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
SET @start_global_value = @@global.GAP_LOCK_EXCEPTIONS;
2+
SELECT @start_global_value;
3+
@start_global_value
4+
NULL
5+
"Trying to set @session.GAP_LOCK_EXCEPTIONS to simple table name."
6+
SET @@global.GAP_LOCK_EXCEPTIONS = mytable;
7+
SELECT @@global.GAP_LOCK_EXCEPTIONS;
8+
@@global.GAP_LOCK_EXCEPTIONS
9+
mytable
10+
"Trying to set @session.GAP_LOCK_EXCEPTIONS to regex table name(s)."
11+
SET @@global.GAP_LOCK_EXCEPTIONS = "t.*";
12+
SELECT @@global.GAP_LOCK_EXCEPTIONS;
13+
@@global.GAP_LOCK_EXCEPTIONS
14+
t.*
15+
"Trying to set @session.GAP_LOCK_EXCEPTIONS to multiple regex table names."
16+
SET @@global.GAP_LOCK_EXCEPTIONS = "s.*,t.*";
17+
SELECT @@global.GAP_LOCK_EXCEPTIONS;
18+
@@global.GAP_LOCK_EXCEPTIONS
19+
s.*,t.*
20+
"Trying to set @session.GAP_LOCK_EXCEPTIONS to empty."
21+
SET @@global.GAP_LOCK_EXCEPTIONS = "";
22+
SELECT @@global.GAP_LOCK_EXCEPTIONS;
23+
@@global.GAP_LOCK_EXCEPTIONS
24+
25+
"Trying to set @session.GAP_LOCK_EXCEPTIONS to default."
26+
SET @@global.GAP_LOCK_EXCEPTIONS = DEFAULT;
27+
SELECT @@global.GAP_LOCK_EXCEPTIONS;
28+
@@global.GAP_LOCK_EXCEPTIONS
29+
NULL
30+
"Trying to set @session.GAP_LOCK_EXCEPTIONS to 444. It should fail because it is not session."
31+
SET @@session.GAP_LOCK_EXCEPTIONS = 444;
32+
ERROR HY000: Variable 'gap_lock_exceptions' is a GLOBAL variable and should be set with SET GLOBAL
33+
SET @@global.GAP_LOCK_EXCEPTIONS = @start_global_value;
34+
SELECT @@global.GAP_LOCK_EXCEPTIONS;
35+
@@global.GAP_LOCK_EXCEPTIONS
36+
NULL
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# We cannot use the rocskdb_sys_var.inc script as some of the strings we set
2+
# need to be quoted and that doesn't work with this script. Run through valid
3+
# options by hand.
4+
5+
SET @start_global_value = @@global.GAP_LOCK_EXCEPTIONS;
6+
SELECT @start_global_value;
7+
8+
--echo "Trying to set @session.GAP_LOCK_EXCEPTIONS to simple table name."
9+
SET @@global.GAP_LOCK_EXCEPTIONS = mytable;
10+
SELECT @@global.GAP_LOCK_EXCEPTIONS;
11+
12+
--echo "Trying to set @session.GAP_LOCK_EXCEPTIONS to regex table name(s)."
13+
SET @@global.GAP_LOCK_EXCEPTIONS = "t.*";
14+
SELECT @@global.GAP_LOCK_EXCEPTIONS;
15+
16+
--echo "Trying to set @session.GAP_LOCK_EXCEPTIONS to multiple regex table names."
17+
SET @@global.GAP_LOCK_EXCEPTIONS = "s.*,t.*";
18+
SELECT @@global.GAP_LOCK_EXCEPTIONS;
19+
20+
--echo "Trying to set @session.GAP_LOCK_EXCEPTIONS to empty."
21+
SET @@global.GAP_LOCK_EXCEPTIONS = "";
22+
SELECT @@global.GAP_LOCK_EXCEPTIONS;
23+
24+
--echo "Trying to set @session.GAP_LOCK_EXCEPTIONS to default."
25+
SET @@global.GAP_LOCK_EXCEPTIONS = DEFAULT;
26+
SELECT @@global.GAP_LOCK_EXCEPTIONS;
27+
28+
--echo "Trying to set @session.GAP_LOCK_EXCEPTIONS to 444. It should fail because it is not session."
29+
--Error ER_GLOBAL_VARIABLE
30+
SET @@session.GAP_LOCK_EXCEPTIONS = 444;
31+
32+
SET @@global.GAP_LOCK_EXCEPTIONS = @start_global_value;
33+
SELECT @@global.GAP_LOCK_EXCEPTIONS;

0 commit comments

Comments
 (0)