8000 Teach test_decoding plugin to work with 2PC · postgrespro/postgres_cluster@ef5bb79 · GitHub
[go: up one dir, main page]

Skip to content

Commit ef5bb79

Browse files
Nikhil Sontakkearssher
Nikhil Sontakke
authored andcommitted
Teach test_decoding plugin to work with 2PC
Implement all callbacks required for decoding 2PC in this test_decoding plugin. Includes relevant test cases as well. Additionally, includes a new option "check-xid". If this option points to a valid xid, then the pg_decode_change() API will wait for it to be aborted externally. This allows us to test concurrent rollback of a prepared transaction while it's being actually decoded simultaneously.
1 parent 7dce5ca commit ef5bb79

File tree

5 files changed

+532
-33
lines changed

5 files changed

+532
-33
lines changed

contrib/test_decoding/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ installcheck:;
2626
# installation, allow to do so, but only if requested explicitly.
2727
installcheck-force: regresscheck-install-force isolationcheck-install-force
2828

29-
check: regresscheck isolationcheck
29+
check: regresscheck isolationcheck 2pc-check
3030

3131
submake-regress:
3232
$(MAKE) -C $(top_builddir)/src/test/regress all
@@ -67,3 +67,6 @@ isolationcheck-install-force: all | submake-isolation submake-test_decoding temp
6767
isolationcheck isolationcheck-install-force
6868

6969
temp-install: EXTRA_INSTALL=contrib/test_decoding
70+
71+
2pc-check: temp-install
72+
$(prove_check)

contrib/test_decoding/expected/prepared.out

Lines changed: 160 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,65 +6,200 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
66
init
77
(1 row)
88

9-
CREATE TABLE test_prepared1(id int);
10-
CREATE TABLE test_prepared2(id int);
9+
CREATE TABLE test_prepared1(id integer primary key);
10+
CREATE TABLE test_prepared2(id integer primary key);
1111
-- test simple successful use of a prepared xact
1212
BEGIN;
1313
INSERT INTO test_prepared1 VALUES (1);
1414
PREPARE TRANSACTION 'test_prepared#1';
15+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip- ED4F empty-xacts', '1');
16+
data
17+
----------------------------------------------------
18+
BEGIN
19+
table public.test_prepared1: INSERT: id[integer]:1
20+
PREPARE TRANSACTION 'test_prepared#1'
21+
(3 rows)
22+
1523
COMMIT PREPARED 'test_prepared#1';
24+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
25+
data
26+
-----------------------------------
27+
COMMIT PREPARED 'test_prepared#1'
28+
(1 row)
29+
1630
INSERT INTO test_prepared1 VALUES (2);
1731
-- test abort of a prepared xact
1832
BEGIN;
1933
INSERT INTO test_prepared1 VALUES (3);
2034
PREPARE TRANSACTION 'test_prepared#2';
35+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
36+
data
37+
----------------------------------------------------
38+
BEGIN
39+
table public.test_prepared1: INSERT: id[integer]:2
40+
COMMIT
41+
BEGIN
42+
table public.test_prepared1: INSERT: id[integer]:3
43+
PREPARE TRANSACTION 'test_prepared#2'
44+
(6 rows)
45+
2146
ROLLBACK PREPARED 'test_prepared#2';
47+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
48+
data
49+
-------------------------------------
50+
ROLLBACK PREPARED 'test_prepared#2'
51+
(1 row)
52+
2253
INSERT INTO test_prepared1 VALUES (4);
2354
-- test prepared xact containing ddl
2455
BEGIN;
2556
INSERT INTO test_prepared1 VALUES (5);
2657
ALTER TABLE test_prepared1 ADD COLUMN data text;
2758
INSERT INTO test_prepared1 VALUES (6, 'frakbar');
2859
PREPARE TRANSACTION 'test_prepared#3';
29-
-- test that we decode correctly while an uncommitted prepared xact
30-
-- with ddl exists.
31-
-- separate table because of the lock from the ALTER
32-
-- this will come before the '5' row above, as this commits before it.
33-
INSERT INTO test_prepared2 VALUES (7);
34-
COMMIT PREPARED 'test_prepared#3';
35-
-- make sure stuff still works
36-
INSERT INTO test_prepared1 VALUES (8);
37-
INSERT INTO test_prepared2 VALUES (9);
38-
-- cleanup
39-
DROP TABLE test_prepared1;
40-
DROP TABLE test_prepared2;
41-
-- show results
60+
SELECT 'test_prepared_1' AS relation, locktype, mode
61+
FROM pg_locks
62+
WHERE locktype = 'relation'
63+
AND relation = 'test_prepared1'::regclass;
64+
relation | locktype | mode
65+
-----------------+----------+---------------------
66+
test_prepared_1 | relation | RowExclusiveLock
67+
test_prepared_1 | relation | AccessExclusiveLock
68+
(2 rows)
69+
4270
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
4371
data
4472
-------------------------------------------------------------------------
45-
BEGIN
46-
table public.test_prepared1: INSERT: id[integer]:1
47-
COMMIT
48-
BEGIN
49-
table public.test_prepared1: INSERT: id[integer]:2
50-
COMMIT
5173
BEGIN
5274
table public.test_prepared1: INSERT: id[integer]:4
5375
COMMIT
5476
BEGIN
55-
table public.test_prepared2: INSERT: id[integer]:7
56-
COMMIT
57-
BEGIN
5877
table public.test_prepared1: INSERT: id[integer]:5
5978
table public.test_prepared1: INSERT: id[integer]:6 data[text]:'frakbar'
79+
PREPARE TRANSACTION 'test_prepared#3'
80+
(7 rows)
81+
82+
-- Test that we decode correctly while an uncommitted prepared xact
83+
-- with ddl exists.
84+
--
85+
-- Use a separate table for the concurrent transaction because the lock from
86+
-- the ALTER will stop us inserting into the other one.
87+
--
88+
-- We should see '7' before '5' in our results since it commits first.
89+
--
90+
INSERT INTO test_prepared2 VALUES (7);
91+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
92+
data
93+
----------------------------------------------------
94+
BEGIN
95+
table public.test_prepared2: INSERT: id[integer]:7
6096
COMMIT
97+
(3 rows)
98+
99+
COMMIT PREPARED 'test_prepared#3';
100+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
101+
data
102+
-----------------------------------
103+
COMMIT PREPARED 'test_prepared#3'
104+
(1 row)
105+
106+
-- make sure stuff still works
107+
INSERT INTO test_prepared1 VALUES (8);
108+
INSERT INTO test_prepared2 VALUES (9);
109+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
110+
data
111+
--------------------------------------------------------------------
61112
BEGIN
62113
table public.test_prepared1: INSERT: id[integer]:8 data[text]:null
63114
COMMIT
64115
BEGIN
65116
table public.test_prepared2: INSERT: id[integer]:9
66117
COMMIT
67-
(22 rows)
118+
(6 rows)
119+
120+
-- Check `CLUSTER` (as operation that hold exclusive lock) doesn't block
121+
-- logical decoding.
122+
BEGIN;
123+
INSERT INTO test_prepared1 VALUES (10, 'othercol');
124+
CLUSTER test_prepared1 USING test_prepared1_pkey;
125+
INSERT INTO test_prepared1 VALUES (11, 'othercol2');
126+
PREPARE TRANSACTION 'test_prepared_lock';
127+
BEGIN;
128+
insert into test_prepared2 values (12);
129+
PREPARE TRANSACTION 'test_prepared_lock2';
130+
COMMIT PREPARED 'test_prepared_lock2';
131+
SELECT 'pg_class' AS relation, locktype, mode
132+
FROM pg_locks
133+
WHERE locktype = 'relation'
134+
AND relation = 'pg_class'::regclass;
135+
relation | locktype | mode
136+
----------+----------+------
137+
(0 rows)
138+
139+
-- Shouldn't timeout on 2pc decoding.
140+
SET statement_timeout = '1s';
141+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
142+
data
143+
----------------------------------------------------------------------------
144+
BEGIN
145+
table public.test_prepared1: INSERT: id[integer]:10 data[text]:'othercol'
146+
table public.test_prepared1: INSERT: id[integer]:11 data[text]:'othercol2'
147+
PREPARE TRANSACTION 'test_prepared_lock'
148+
BEGIN
149+
table public.test_prepared2: INSERT: id[integer]:12
150+
PREPARE TRANSACTION 'test_prepared_lock2'
151+
COMMIT PREPARED 'test_prepared_lock2'
152+
(8 rows)
153+
154+
RESET statement_timeout;
155+
COMMIT PREPARED 'test_prepared_lock';
156+
-- will work normally after we commit
157+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
158+
data
159+
--------------------------------------
160+
COMMIT PREPARED 'test_prepared_lock'
161+
(1 row)
162+
163+
-- test savepoints
164+
BEGIN;
165+
SAVEPOINT test_savepoint;
166+
CREATE TABLE test_prepared_savepoint (a int);
167+
PREPARE TRANSACTION 'test_prepared_savepoint';
168+
COMMIT PREPARED 'test_prepared_savepoint';
169+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
170+
data
171+
-------------------------------------------
172+
COMMIT PREPARED 'test_prepared_savepoint'
173+
(1 row)
174+
175+
-- test that a GID containing "nodecode" gets decoded at commit prepared time
176+
BEGIN;
177+
INSERT INTO test_prepared1 VALUES (20);
178+
PREPARE TRANSACTION 'test_prepared_nodecode';
179+
-- should show nothing
180+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
181+
data
182+
------
183+
(0 rows)
184+
185+
COMMIT PREPARED 'test_prepared_nodecode';
186+
-- should be decoded now
187+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
188+
data
189+
---------------------------------------------------------------------
190+
BEGIN
191+
table public.test_prepared1: INSERT: id[integer]:20 data[text]:null
192+
COMMIT
193+
(3 rows)
194+
195+
-- cleanup
196+
DROP TABLE test_prepared1;
197+
DROP TABLE test_prepared2;
198+
-- show results. There should be nothing to show
199+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
200+
data
201+
------
202+
(0 rows)
68203

69204
SELECT pg_drop_replication_slot('regression_slot');
70205
pg_drop_replication_slot

contrib/test_decoding/sql/prepared.sql

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,25 @@
22
SET synchronous_commit = on;
33
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
44

5-
CREATE TABLE test_prepared1(id int);
6-
CREATE TABLE test_prepared2(id int);
5+
CREATE TABLE test_prepared1(id integer primary key);
6+
CREATE TABLE test_prepared2(id integer primary key);
77

88
-- test simple successful use of a prepared xact
99
BEGIN;
1010
INSERT INTO test_prepared1 VALUES (1);
1111
PREPARE TRANSACTION 'test_prepared#1';
12+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
1213
COMMIT PREPARED 'test_prepared#1';
14+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
1315
INSERT INTO test_prepared1 VALUES (2);
1416

1517
-- test abort of a prepared xact
1618
BEGIN;
1719
INSERT INTO test_prepared1 VALUES (3);
1820
PREPARE TRANSACTION 'test_prepared#2';
21+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
1922
ROLLBACK PREPARED 'test_prepared#2';
23+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
2024

2125
INSERT INTO test_prepared1 VALUES (4);
2226

@@ -27,24 +31,83 @@ ALTER TABLE test_prepared1 ADD COLUMN data text;
2731
INSERT INTO test_prepared1 VALUES (6, 'frakbar');
2832
PREPARE TRANSACTION 'test_prepared#3';
2933

30-
-- test that we decode correctly while an uncommitted prepared xact
31-
-- with ddl exists.
34+
SELECT 'test_prepared_1' AS relation, locktype, mode
35+
FROM pg_locks
36+
WHERE locktype = 'relation'
37+
AND relation = 'test_prepared1'::regclass;
38+
39+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
3240

33-
-- separate table because of the lock from the ALTER
34-
-- this will come before the '5' row above, as this commits before it.
41+
-- Test that we decode correctly while an uncommitted prepared xact
42+
-- with ddl exists.
43+
--
44+
-- Use a separate table for the concurrent transaction because the lock from
45+
-- the ALTER will stop us inserting into the other one.
46+
--
47+
-- We should see '7' before '5' in our results since it commits first.
48+
--
3549
INSERT INTO test_prepared2 VALUES (7);
50+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
3651

3752
COMMIT PREPARED 'test_prepared#3';
53+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
3854

3955
-- make sure stuff still works
4056
INSERT INTO test_prepared1 VALUES (8);
4157
INSERT INTO test_prepared2 VALUES (9);
58+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
59+
60+
-- Check `CLUSTER` (as operation that hold exclusive lock) doesn't block
61+
-- logical decoding.
62+
BEGIN;
63+
INSERT INTO test_prepared1 VALUES (10, 'othercol');
64+
CLUSTER test_prepared1 USING test_prepared1_pkey;
65+
INSERT INTO test_prepared1 VALUES (11, 'othercol2');
66+
PREPARE TRANSACTION 'test_prepared_lock';
67+
68+
BEGIN;
69+
insert into test_prepared2 values (12);
70+
PREPARE TRANSACTION 'test_prepared_lock2';
71+
COMMIT PREPARED 'test_prepared_lock2';
72+
73+
SELECT 'pg_class' AS relation, locktype, mode
74+
FROM pg_locks
75+
WHERE locktype = 'relation'
76+
AND relation = 'pg_class'::regclass;
77+
78+
-- Shouldn't timeout on 2pc decoding.
79+
SET statement_timeout = '1s';
80+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
81+
RESET statement_timeout;
82+
83+
COMMIT PREPARED 'test_prepared_lock';
84+
85+
-- will work normally after we commit
86+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
87+
88+
-- test savepoints
89+
BEGIN;
90+
SAVEPOINT test_savepoint;
91+
CREATE TABLE test_prepared_savepoint (a int);
92+
PREPARE TRANSACTION 'test_prepared_savepoint';
93+
COMMIT PREPARED 'test_prepared_savepoint';
94+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
95+
96+
-- test that a GID containing "nodecode" gets decoded at commit prepared time
97+
BEGIN;
98+
INSERT INTO test_prepared1 VALUES (20);
99+
PREPARE TRANSACTION 'test_prepared_nodecode';
100+
-- should show nothing
101+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
102+
COMMIT PREPARED 'test_prepared_nodecode';
103+
-- should be decoded now
104+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
42105

43106
-- cleanup
44107
DROP TABLE test_prepared1;
45108
DROP TABLE test_prepared2;
46109

47-
-- show results
110+
-- show results. There should be nothing to show
48111
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
49112

50113
SELECT pg_drop_replication_slot('regression_slot');

0 commit comments

Comments
 (0)
0