Skip to content

Commit 1dd4889

Browse files
MDEV-25039: MDL BF-BF conflict because of foreign key
Issue: The Commit: 0584846 'Add TL_FIRST_WRITE in SQL layer for determining R/W' limits INSERT statements on write nodes to acquire MDL locks on it's all child tables and thereby wsrep certification keys added, but on applier nodes it does acquire MDL locks for all child tables. This can result into MDL BF-BF conflict on applier node when transactions referring to parent and child tables are executed concurrently. For example: Tables with foreign keys: t1<-t2<-t3<-t4 Conflicting transactions: INSERT t1 and DROP TABLE t4 Wsrep certification keys taken on write node: - for INSERT t1: t1 and t2 - for DROP TABLE t4: t4 On applier node MDL BF-BF conflict happened between two transaction because MDL locks on t1, t2, t3 and t4 were taken for INSERT t1, which conflicted with MDL lock on t4 taken by DROP TABLE t4. The Wsrep certification keys helps in resolving this MDL BF-BF conflict by prioritizing and scheduling concurrent transactions. But to generate Wsrep certification keys it needs to open and take MDL locks on all the child tables. The Commit: 0584846 change limits MDL lock to be taken on all child nodes for read-only FK checks (INSERT t1). But this doesn't works on applier nodes because Write_rows_log_event event logged for INSERT is also used to record update (check Write_rows_log_event::get_trg_event_map()), and therefore MDL locks is taken for all the child tables on applier node for update and insert event. Solution: Additional keys for the referenced/foreign table needs to be added to avoid potential MDL conflicts with concurrent update and DDLs.
1 parent 0e322b6 commit 1dd4889

File tree

5 files changed

+891
-13
lines changed

5 files changed

+891
-13
lines changed
Lines changed: 364 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,364 @@
1+
connection node_2;
2+
connection node_1;
3+
#
4+
# 1. BF-BF conflict on MDL locks between: DROP TABLE t4 and INSERT t1
5+
# with foreign key references as below:
6+
# - t1<-t2<-t3<-t4
7+
#
8+
connection node_2;
9+
SET GLOBAL wsrep_slave_threads=2;
10+
CREATE TABLE t1 (
11+
id INTEGER PRIMARY KEY,
12+
f2 INTEGER
13+
);
14+
CREATE TABLE t2 (
15+
id INT PRIMARY KEY,
16+
t1_id INT NOT NULL,
17+
f2 INTEGER NOT NULL,
18+
KEY key_t1_id(t1_id),
19+
CONSTRAINT key_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id) ON UPDATE CASCADE ON DELETE CASCADE
20+
);
21+
CREATE TABLE t3 (
22+
id INT PRIMARY KEY,
23+
t2_id INT NOT NULL,
24+
f2 INTEGER NOT NULL,
25+
KEY key_t2_id(t2_id),
26+
CONSTRAINT key_t2_id FOREIGN KEY (t2_id) REFERENCES t2 (id) ON UPDATE CASCADE ON DELETE CASCADE
27+
);
28+
CREATE TABLE t4 (
29+
id INT PRIMARY KEY,
30+
t3_id INT NOT NULL,
31+
f2 INTEGER NOT NULL,
32+
KEY key_t3_id(t3_id),
33+
CONSTRAINT key_t3_id FOREIGN KEY (t3_id) REFERENCES t3 (id) ON UPDATE CASCADE ON DELETE CASCADE
34+
);
35+
INSERT INTO t1 VALUES (1,0);
36+
INSERT INTO t1 VALUES (2,0);
37+
INSERT INTO t2 VALUES (1,1,1234);
38+
INSERT INTO t2 VALUES (2,2,1234);
39+
INSERT INTO t3 VALUES (1,1,1234);
40+
INSERT INTO t3 VALUES (2,2,1234);
41+
INSERT INTO t4 VALUES (1,1,1234);
42+
INSERT INTO t4 VALUES (2,2,1234);
43+
connection node_2;
44+
SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi';
45+
connection node_1;
46+
DROP TABLE t4;
47+
connection node_2;
48+
SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached";
49+
SET SESSION wsrep_sync_wait = 0;
50+
connection node_1;
51+
SET GLOBAL DEBUG_DBUG = '+d,wsrep_print_foreign_keys_table';
52+
START TRANSACTION;
53+
INSERT INTO t1 VALUES (3,0);
54+
COMMIT;
55+
connection node_2;
56+
SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi';
57+
SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi";
58+
SET DEBUG_SYNC = 'RESET';
59+
SET GLOBAL DEBUG_DBUG = "";
60+
SET GLOBAL wsrep_slave_threads=DEFAULT;
61+
connection node_1;
62+
SET DEBUG_SYNC = 'RESET';
63+
SET GLOBAL DEBUG_DBUG = "";
64+
SET GLOBAL wsrep_slave_threads=DEFAULT;
65+
connection node_1;
66+
include/assert_grep.inc [Foreign key referenced table found: 2 tables]
67+
include/assert_grep.inc [Foreign key referenced table found: test.t2]
68+
include/assert_grep.inc [Foreign key referenced table found: test.t3]
69+
connection node_2;
70+
select * from t1;
71+
id f2
72+
1 0
73+
2 0
74+
3 0
75+
select * from t2;
76+
id t1_id f2
77+
1 1 1234
78+
2 2 1234
79+
select * from t3;
80+
id t2_id f2
81+
1 1 1234
82+
2 2 1234
83+
select * from t4;
84+
ERROR 42S02: Table 'test.t4' doesn't exist
85+
connection node_1;
86+
select * from t1;
87+
id f2
88+
1 0
89+
2 0
90+
3 0
91+
select * from t2;
92+
id t1_id f2
93+
1 1 1234
94+
2 2 1234
95+
select * from t3;
96+
id t2_id f2
97+
1 1 1234
98+
2 2 1234
99+
select * from t4;
100+
ERROR 42S02: Table 'test.t4' doesn't exist
101+
DROP TABLE t3, t2, t1;
102+
#
103+
# 2. BF-BF conflict on MDL locks between:
104+
# ALTER TABLE t4 (whose parent table are t4 -> t3 -> t2 -> t1), and
105+
# INSERT on t1.
106+
#
107+
connection node_2;
108+
SET GLOBAL wsrep_slave_threads=2;
109+
CREATE TABLE t1 (
110+
id INTEGER PRIMARY KEY,
111+
f2 INTEGER
112+
);
113+
CREATE TABLE t2 (
114+
id INT PRIMARY KEY,
115+
t1_id INT NOT NULL,
116+
f2 INTEGER NOT NULL,
117+
KEY key_t1_id(t1_id),
118+
CONSTRAINT key_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id) ON UPDATE CASCADE ON DELETE CASCADE
119+
);
120+
CREATE TABLE t3 (
121+
id INT PRIMARY KEY,
122+
t2_id INT NOT NULL,
123+
f2 INTEGER NOT NULL,
124+
KEY key_t2_id(t2_id),
125+
CONSTRAINT key_t2_id FOREIGN KEY (t2_id) REFERENCES t2 (id) ON UPDATE CASCADE ON DELETE CASCADE
126+
);
127+
CREATE TABLE t4 (
128+
id INT PRIMARY KEY,
129+
t3_id INT NOT NULL,
130+
f2 INTEGER NOT NULL,
131+
KEY key_t3_id(t3_id)
132+
);
133+
INSERT INTO t1 VALUES (1,0);
134+
INSERT INTO t1 VALUES (2,0);
135+
INSERT INTO t2 VALUES (1,1,1234);
136+
INSERT INTO t2 VALUES (2,2,1234);
137+
INSERT INTO t3 VALUES (1,1,1234);
138+
INSERT INTO t3 VALUES (2,2,1234);
139+
INSERT INTO t4 VALUES (1,1,1234);
140+
INSERT INTO t4 VALUES (2,2,1234);
141+
connection node_2;
142+
SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi';
143+
connection node_1;
144+
ALTER TABLE t4 ADD CONSTRAINT key_t3_id FOREIGN KEY (t3_id) REFERENCES t3 (id) ON UPDATE CASCADE ON DELETE CASCADE;
145+
connection node_2;
146+
SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached";
147+
SET SESSION wsrep_sync_wait = 0;
148+
connection node_1;
149+
SET GLOBAL DEBUG_DBUG = '+d,wsrep_print_foreign_keys_table';
150+
START TRANSACTION;
151+
INSERT INTO t1 VALUES (3,0);
152+
COMMIT;
153+
connection node_2;
154+
SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi';
155+
SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi";
156+
SET DEBUG_SYNC = 'RESET';
157+
SET GLOBAL DEBUG_DBUG = "";
158+
SET GLOBAL wsrep_slave_threads=DEFAULT;
159+
connection node_1;
160+
SET DEBUG_SYNC = 'RESET';
161+
SET GLOBAL DEBUG_DBUG = "";
162+
SET GLOBAL wsrep_slave_threads=DEFAULT;
163+
connection node_1;
164+
include/assert_grep.inc [Foreign key referenced table found: 3 tables]
165+
include/assert_grep.inc [Foreign key referenced table found: test.t2]
166+
include/assert_grep.inc [Foreign key referenced table found: test.t3]
167+
include/assert_grep.inc [Foreign key referenced table found: test.t4]
168+
connection node_2;
169+
select * from t1;
170+
id f2
171+
1 0
172+
2 0
173+
3 0
174+
select * from t2;
175+
id t1_id f2
176+
1 1 1234
177+
2 2 1234
178+
select * from t3;
179+
id t2_id f2
180+
1 1 1234
181+
2 2 1234
182+
connection node_1;
183+
select * from t1;
184+
id f2
185+
1 0
186+
2 0
187+
3 0
188+
select * from t2;
189+
id t1_id f2
190+
1 1 1234
191+
2 2 1234
192+
select * from t3;
193+
id t2_id f2
194+
1 1 1234
195+
2 2 1234
196+
DROP TABLE t4, t3, t2, t1;
197+
#
198+
# 3. BF-BF conflict on MDL locks between:
199+
# CREATE TABLE t3 (whose parent table are t4 -> t3 -> t2 -> t1), and
200+
# INSERT on t1.
201+
#
202+
connection node_2;
203+
SET GLOBAL wsrep_slave_threads=2;
204+
CREATE TABLE t1 (
205+
id INTEGER PRIMARY KEY,
206+
f2 INTEGER
207+
);
208+
CREATE TABLE t2 (
209+
id INT PRIMARY KEY,
210+
t1_id INT NOT NULL,
211+
f2 INTEGER NOT NULL,
212+
KEY key_t1_id(t1_id),
213+
CONSTRAINT key_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id) ON UPDATE CASCADE ON DELETE CASCADE
214+
);
215+
CREATE TABLE t3 (
216+
id INT PRIMARY KEY,
217+
t2_id INT NOT NULL,
218+
f2 INTEGER NOT NULL,
219+
KEY key_t2_id(t2_id),
220+
CONSTRAINT key_t2_id FOREIGN KEY (t2_id) REFERENCES t2 (id) ON UPDATE CASCADE ON DELETE CASCADE
221+
);
222+
INSERT INTO t1 VALUES (1,0);
223+
INSERT INTO t1 VALUES (2,0);
224+
INSERT INTO t2 VALUES (1,1,1234);
225+
INSERT INTO t2 VALUES (2,2,1234);
226+
INSERT INTO t3 VALUES (1,1,1234);
227+
INSERT INTO t3 VALUES (2,2,1234);
228+
connection node_2;
229+
SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi';
230+
connection node_1;
231+
CREATE TABLE t4 (id INT PRIMARY KEY, t3_id INT NOT NULL, f2 INTEGER NOT NULL, KEY key_t3_id(t3_id), CONSTRAINT key_t3_id FOREIGN KEY (t3_id) REFERENCES t3 (id) ON UPDATE CASCADE ON DELETE CASCADE);
232+
connection node_2;
233+
SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached";
234+
SET SESSION wsrep_sync_wait = 0;
235+
connection node_1;
236+
SET GLOBAL DEBUG_DBUG = '+d,wsrep_print_foreign_keys_table';
237+
START TRANSACTION;
238+
INSERT INTO t1 VALUES (3,0);
239+
COMMIT;
240+
connection node_2;
241+
SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi';
242+
SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi";
243+
SET DEBUG_SYNC = 'RESET';
244+
SET GLOBAL DEBUG_DBUG = "";
245+
SET GLOBAL wsrep_slave_threads=DEFAULT;
246+
connection node_1;
247+
SET DEBUG_SYNC = 'RESET';
248+
SET GLOBAL DEBUG_DBUG = "";
249+
SET GLOBAL wsrep_slave_threads=DEFAULT;
250+
connection node_1;
251+
include/assert_grep.inc [Foreign key referenced table found: 2 tables]
252+
include/assert_grep.inc [Foreign key referenced table found: test.t2]
253+
include/assert_grep.inc [Foreign key referenced table found: test.t3]
254+
include/assert_grep.inc [Foreign key referenced table found: test.t4]
255+
connection node_2;
256+
select * from t1;
257+
id f2
258+
1 0
259+
2 0
260+
3 0
261+
select * from t2;
262+
id t1_id f2
263+
1 1 1234
264+
2 2 1234
265+
select * from t3;
266+
id t2_id f2
267+
1 1 1234
268+
2 2 1234
269+
connection node_1;
270+
select * from t1;
271+
id f2
272+
1 0
273+
2 0
274+
3 0
275+
select * from t2;
276+
id t1_id f2
277+
1 1 1234
278+
2 2 1234
279+
select * from t3;
280+
id t2_id f2
281+
1 1 1234
282+
2 2 1234
283+
DROP TABLE t4, t3, t2, t1;
284+
#
285+
# 4. BF-BF conflict on MDL locks between:
286+
# OPTIMIZE TABLE t2 (whose parent table are t2 -> t1), and
287+
# INSERT t1.
288+
#
289+
connection node_2;
290+
SET GLOBAL wsrep_slave_threads=2;
291+
CREATE TABLE t1 (
292+
id INTEGER PRIMARY KEY,
293+
f2 INTEGER
294+
);
295+
CREATE TABLE t2 (
296+
id INT PRIMARY KEY,
297+
t1_id INT NOT NULL,
298+
f2 INTEGER NOT NULL,
299+
KEY key_t1_id(t1_id),
300+
CONSTRAINT key_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id) ON UPDATE CASCADE ON DELETE CASCADE
301+
);
302+
CREATE TABLE t3 (
303+
id INT PRIMARY KEY,
304+
t2_id INT NOT NULL,
305+
f2 INTEGER NOT NULL,
306+
KEY key_t2_id(t2_id),
307+
CONSTRAINT key_t2_id FOREIGN KEY (t2_id) REFERENCES t2 (id) ON UPDATE CASCADE ON DELETE CASCADE
308+
);
309+
INSERT INTO t1 VALUES (1,0);
310+
INSERT INTO t1 VALUES (2,0);
311+
INSERT INTO t2 VALUES (1,1,1234);
312+
INSERT INTO t2 VALUES (2,2,1234);
313+
INSERT INTO t3 VALUES (1,1,1234);
314+
INSERT INTO t3 VALUES (2,2,1234);
315+
connection node_2;
316+
SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi';
317+
connection node_1;
318+
OPTIMIZE TABLE t3;
319+
Table Op Msg_type Msg_text
320+
test.t3 optimize note Table does not support optimize, doing recreate + analyze instead
321+
test.t3 optimize status OK
322+
connection node_2;
323+
SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached";
324+
SET SESSION wsrep_sync_wait = 0;
325+
connection node_1;
326+
SET GLOBAL DEBUG_DBUG = '+d,wsrep_print_foreign_keys_table';
327+
START TRANSACTION;
328+
INSERT INTO t1 VALUES (3,0);
329+
COMMIT;
330+
connection node_2;
331+
SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi';
332+
SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi";
333+
SET DEBUG_SYNC = 'RESET';
334+
SET GLOBAL DEBUG_DBUG = "";
335+
SET GLOBAL wsrep_slave_threads=DEFAULT;
336+
connection node_1;
337+
SET DEBUG_SYNC = 'RESET';
338+
SET GLOBAL DEBUG_DBUG = "";
339+
SET GLOBAL wsrep_slave_threads=DEFAULT;
340+
connection node_1;
341+
include/assert_grep.inc [Foreign key referenced table found: 1 tables]
342+
include/assert_grep.inc [Foreign key referenced table found: test.t2]
343+
include/assert_grep.inc [Foreign key referenced table found: test.t3]
344+
connection node_2;
345+
select * from t1;
346+
id f2
347+
1 0
348+
2 0
349+
3 0
350+
select * from t2;
351+
id t1_id f2
352+
1 1 1234
353+
2 2 1234
354+
connection node_1;
355+
select * from t1;
356+
id f2
357+
1 0
358+
2 0
359+
3 0
360+
select * from t2;
361+
id t1_id f2
362+
1 1 1234
363+
2 2 1234
364+
DROP TABLE t3, t2, t1;

0 commit comments

Comments
 (0)