File: lock_granted_before_waiting.test

package info (click to toggle)
mysql-8.0 8.0.43-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,273,924 kB
  • sloc: cpp: 4,684,605; ansic: 412,450; pascal: 108,398; java: 83,641; perl: 30,221; cs: 27,067; sql: 26,594; sh: 24,181; python: 21,816; yacc: 17,169; php: 11,522; xml: 7,388; javascript: 7,076; makefile: 2,194; lex: 1,075; awk: 670; asm: 520; objc: 183; ruby: 97; lisp: 86
file content (108 lines) | stat: -rw-r--r-- 3,228 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
--source include/have_debug_sync.inc
--source include/have_innodb_16k.inc
--source include/count_sessions.inc

# Save the original settings, to be restored at the end of test
SET @innodb_lock_wait_timeout_saved = @@global.innodb_lock_wait_timeout;

# Make sure that transactions will not finish prematurely
SET @@global.innodb_lock_wait_timeout = 20;

--source ../include/prepare_show_locks.inc

# Scenario in which a WAITING lock struct is first "cleared" because locks
# are moved to a different heap_no, and then "reused" for another GRANTED
# lock on a different heap_no, potentially leading to a violation of invariant
# that GRANTED locks should be in front of WAITING locks.

# 1. con1 gets GRANTED GAP+REC S lock on row id=3
# 2. con2 gets WAITING II lock on row id=3
# 3. con3 gets GRANTED X lock on row id=2000
# 4. con4 gets WAITING GAP+REC S lock on row id=2000
# 5. con3 inserts so many rows before row id=2000 that the page has to be split,
#    and the old lock struct of con4 is "cleared" and new WAITING lock for con4
#    is created on the new page
# 6. con3 commits releasing the lock on row id=2000, so con4 can continue now
#    con4 gets GRANTED GAP+REC S lock on row id=3 (and reuses the previously
#    "cleared" struct to achieve that)
#
# The queue for row id=3 contains:
#    <con1,GRANTED,GAP+REC S>, <con2,WAITING,II>, <con3,GRANTED,GAP_REC S>
# so to bring back the invariant that GRANTED are before WAITING, we need to
# move con3's lock to front.

# The CHAR(200) is just to make records large, so page split comes sooner.
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY, val CHAR(200));
INSERT INTO t1 (id,val) VALUES (1, "a"),  (3, "c"), (1998,"x"), (2000, "z");

--connect (con1, localhost, root,,)
  CALL register_connection("con1");
  BEGIN;
  SELECT * FROM t1 WHERE id<=3 FOR SHARE;

--connect (con2, localhost, root,,)
  CALL register_connection("con2");
  BEGIN;
  SET DEBUG_SYNC='lock_wait_will_wait SIGNAL con2_will_wait';
  --send INSERT INTO t1 VALUES (2,"b")

--connect (con3, localhost, root,,)
  SET DEBUG_SYNC='now WAIT_FOR con2_will_wait';
  CALL register_connection("con3");
  BEGIN;
  SELECT * FROM t1 WHERE id=2000 FOR UPDATE;

--connect (con4, localhost, root,,)
  CALL register_connection("con4");
  BEGIN;
  SET DEBUG_SYNC='lock_wait_will_wait SIGNAL con4_will_wait';
  --send SELECT * FROM t1 WHERE 1999<=id FOR SHARE

--connection default
  SET DEBUG_SYNC='now WAIT_FOR con4_will_wait';
  CALL show_locks();


--connection con3
  --let i=10
  # 16k page, each record > 200 bytes, so 80 rows should be enough
  while($i<100){
    --eval INSERT INTO t1 (id,val) VALUES ($i,"x")
    --inc $i
  }

--connection default
  CALL show_locks();

--connection con3
  COMMIT;

--connection con4
  --reap
  SELECT * FROM t1 WHERE id<=3 FOR SHARE;

--connection default
  CALL show_locks();

--connection con1
  COMMIT;

--connection con4
  COMMIT;

--connection con2
  --reap
  COMMIT;

--connection default
--disconnect con1
--disconnect con2
--disconnect con3
--disconnect con4

DROP TABLE t1;
--source ../include/cleanup_show_locks.inc

# Restore saved settings
SET @@global.innodb_lock_wait_timeout = @innodb_lock_wait_timeout_saved ;
--source include/wait_until_count_sessions.inc