@@ -6,103 +6,254 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
6
6
init
7
7
(1 row)
8
8
9
- CREATE TABLE test_prepared1(id int);
10
- CREATE TABLE test_prepared2(id int);
9
+ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_2pc', 'test_decoding');
10
+ ?column?
11
+ ----------
12
+ init
13
+ (1 row)
14
+
15
+ CREATE TABLE test_prepared1(id integer primary key);
16
+ CREATE TABLE test_prepared2(id integer primary key);
17
+ -- Reused queries
18
+ \set get_no2pc 'SELECT data FROM pg_logical_slot_get_changes(''regression_slot_2pc'', NULL, NULL, ''include-xids'', ''0'', ''skip-empty-xacts'', ''1'');'
19
+ \set get_with2pc 'SELECT data FROM pg_logical_slot_get_changes(''regression_slot'', NULL, NULL, ''include-xids'', ''0'', ''skip-empty-xacts'', ''1'', ''twophase-decoding'', ''1'');'
20
+ \set get_with2pc_nofilter 'SELECT data FROM pg_logical_slot_get_changes(''regression_slot'', NULL, NULL, ''include-xids'', ''0'', ''skip-empty-xacts'', ''1'', ''twophase-decoding'', ''1'', ''twophase-decode-with-catalog-changes'', ''1'');'
11
21
-- test simple successful use of a prepared xact
12
22
BEGIN;
13
23
INSERT INTO test_prepared1 VALUES (1);
14
24
PREPARE TRANSACTION 'test_prepared#1';
25
+ :get_with2pc
26
+ data
27
+ ----------------------------------------------------
28
+ BEGIN
29
+ table public.test_prepared1: INSERT: id[integer]:1
30
+ PREPARE TRANSACTION 'test_prepared#1'
31
+ (3 rows)
32
+
33
+ :get_no2pc
34
+ data
35
+ ------
36
+ (0 rows)
37
+
15
38
COMMIT PREPARED 'test_prepared#1';
39
+ :get_with2pc
40
+ data
41
+ ------
42
+ (0 rows)
43
+
44
+ :get_no2pc
45
+ data
46
+ ----------------------------------------------------
47
+ BEGIN
48
+ table public.test_prepared1: INSERT: id[integer]:1
49
+ COMMIT
50
+ (3 rows)
51
+
16
52
INSERT INTO test_prepared1 VALUES (2);
17
53
-- test abort of a prepared xact
18
54
BEGIN;
19
55
INSERT INTO test_prepared1 VALUES (3);
20
56
PREPARE TRANSACTION 'test_prepared#2';
57
+ :get_no2pc
58
+ data
59
+ ----------------------------------------------------
60
+ BEGIN
61
+ table public.test_prepared1: INSERT: id[integer]:2
62
+ COMMIT
63
+ (3 rows)
64
+
65
+ :get_with2pc
66
+ data
67
+ ----------------------------------------------------
68
+ BEGIN
69
+ table public.test_prepared1: INSERT: id[integer]:2
70
+ COMMIT
71
+ BEGIN
72
+ table public.test_prepared1: INSERT: id[integer]:3
73
+ PREPARE TRANSACTION 'test_prepared#2'
74
+ (6 rows)
75
+
21
76
ROLLBACK PREPARED 'test_prepared#2';
77
+ :get_no2pc
78
+ data
79
+ ------
80
+ (0 rows)
81
+
82
+ :get_with2pc
83
+ data
84
+ ------
85
+ (0 rows)
86
+
22
87
INSERT INTO test_prepared1 VALUES (4);
23
88
-- test prepared xact containing ddl
24
89
BEGIN;
25
90
INSERT INTO test_prepared1 VALUES (5);
26
91
ALTER TABLE test_prepared1 ADD COLUMN data text;
27
92
INSERT INTO test_prepared1 VALUES (6, 'frakbar');
28
93
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
42
- SELECT data FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
43
- data
44
- -------------------------------------------------------------------------
94
+ SELECT 'test_prepared_1' AS relation, locktype, mode
95
+ FROM pg_locks
96
+ WHERE locktype = 'relation'
97
+ AND relation = 'test_prepared1'::regclass;
98
+ relation | locktype | mode
99
+ -----------------+----------+---------------------
100
+ test_prepared_1 | relation | RowExclusiveLock
101
+ test_prepared_1 | relation | AccessExclusiveLock
102
+ (2 rows)
103
+
104
+ :get_no2pc
105
+ data
106
+ ----------------------------------------------------
45
107
BEGIN
46
- table public.test_prepared1: INSERT: id[integer]:1
108
+ table public.test_prepared1: INSERT: id[integer]:4
47
109
COMMIT
110
+ (3 rows)
111
+
112
+ :get_with2pc
113
+ data
114
+ ----------------------------------------------------
48
115
BEGIN
49
- table public.test_prepared1: INSERT: id[integer]:2
116
+ table public.test_prepared1: INSERT: id[integer]:4
50
117
COMMIT
118
+ (3 rows)
119
+
120
+ -- Test that we decode correctly while an uncommitted prepared xact
121
+ -- with ddl exists. Our 2pc filter callback will skip decoding of xacts
122
+ -- with catalog changes at PREPARE time, so we don't decode it now.
123
+ --
124
+ -- Use a separate table for the concurrent transaction because the lock from
125
+ -- the ALTER will stop us inserting into the other one.
126
+ --
127
+ -- We should see '7' before '5' in our results since it commits first.
128
+ --
129
+ INSERT INTO test_prepared2 VALUES (7);
130
+ :get_with2pc
131
+ data
132
+ ----------------------------------------------------
51
133
BEGIN
52
- table public.test_prepared1 : INSERT: id[integer]:4
134
+ table public.test_prepared2 : INSERT: id[integer]:7
53
135
COMMIT
136
+ (3 rows)
137
+
138
+ :get_no2pc
139
+ data
140
+ ----------------------------------------------------
54
141
BEGIN
55
142
table public.test_prepared2: INSERT: id[integer]:7
56
143
COMMIT
144
+ (3 rows)
145
+
146
+ COMMIT PREPARED 'test_prepared#3';
147
+ :get_no2pc
148
+ data
149
+ -------------------------------------------------------------------------
57
150
BEGIN
58
151
table public.test_prepared1: INSERT: id[integer]:5
59
152
table public.test_prepared1: INSERT: id[integer]:6 data[text]:'frakbar'
60
153
COMMIT
61
- BEGIN
62
- table public.test_prepared1: INSERT: id[integer]:8 data[text]:null
63
- COMMIT
64
- BEGIN
65
- table public.test_prepared2: INSERT: id[integer]:9
66
- COMMIT
67
- (22 rows)
154
+ (4 rows)
68
155
69
- -- same but with twophase decoding
70
- SELECT data FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'twophase-decoding', '1');
156
+ :get_with2pc
71
157
data
72
158
-------------------------------------------------------------------------
73
- BEGIN
74
- table public.test_prepared1: INSERT: id[integer]:1
75
- PREPARE 'test_prepared#1'
76
- COMMIT PREPARED 'test_prepared#1'
77
- BEGIN
78
- table public.test_prepared1: INSERT: id[integer]:2
79
- COMMIT
80
- BEGIN
81
- table public.test_prepared1: INSERT: id[integer]:3
82
- PREPARE 'test_prepared#2'
83
- ABORT PREPARED 'test_prepared#2'
84
- BEGIN
85
- table public.test_prepared1: INSERT: id[integer]:4
86
- COMMIT
87
159
BEGIN
88
160
table public.test_prepared1: INSERT: id[integer]:5
89
161
table public.test_prepared1: INSERT: id[integer]:6 data[text]:'frakbar'
90
- PREPARE 'test_prepared#3'
162
+ PREPARE TRANSACTION 'test_prepared#3';
163
+ COMMIT PREPARED 'test_prepared#3';
164
+ (5 rows)
165
+
166
+ -- make sure stuff still works
167
+ INSERT INTO test_prepared1 VALUES (8);
168
+ INSERT INTO test_prepared2 VALUES (9);
169
+ :get_with2pc
170
+ data
171
+ --------------------------------------------------------------------
91
172
BEGIN
92
- table public.test_prepared2: INSERT: id[integer]:7
173
+ table public.test_prepared1: INSERT: id[integer]:8 data[text]:null
174
+ COMMIT
175
+ BEGIN
176
+ table public.test_prepared2: INSERT: id[integer]:9
93
177
COMMIT
94
- COMMIT PREPARED 'test_prepared#3'
178
+ (6 rows)
179
+
180
+ :get_no2pc
181
+ data
182
+ --------------------------------------------------------------------
95
183
BEGIN
96
184
table public.test_prepared1: INSERT: id[integer]:8 data[text]:null
97
185
COMMIT
98
186
BEGIN
99
187
table public.test_prepared2: INSERT: id[integer]:9
100
188
COMMIT
101
- (28 rows)
189
+ (6 rows)
102
190
191
+ -- If we do something that takes a strong lock on a catalog relation we need to
192
+ -- read in order to decode a transaction we deadlock; we can't finish decoding
193
+ -- until the lock is released, but we're waiting for decoding to finish so we
194
+ -- can make a commit/abort decision.
195
+ ---
196
+ BEGIN;
197
+ INSERT INTO test_prepared1 VALUES (10, 'othercol');
198
+ CLUSTER test_prepared1 USING test_prepared1_pkey;
199
+ INSERT INTO test_prepared1 VALUES (11, 'othercol2');
200
+ PREPARE TRANSACTION 'test_prepared_lock';
201
+ SELECT 'pg_class' AS relation, locktype, mode
202
+ FROM pg_locks
203
+ WHERE locktype = 'relation'
204
+ AND relation = 'pg_class'::regclass;
205
+ relation | locktype | mode
206
+ ----------+----------+------
207
+ (0 rows)
208
+
209
+ -- Shouldn't see anything with 2pc decoding off
210
+ :get_no2pc
211
+ data
212
+ ------
213
+ (0 rows)
214
+
215
+ -- If we try to decode it now we'll deadlock
216
+ SET statement_timeout = '10s';
217
+ :get_with2pc_nofilter
218
+ -- FIXME we expect a timeout here, but it actually works...
219
+ ERROR: statement timed out
220
+
221
+ RESET statement_timeout;
222
+ -- we can decode past it by skipping xacts with catalog changes
223
+ -- and let it be decoded after COMMIT PREPARED, though.
224
+ :get_with2pc
225
+ data
226
+ ------
227
+ (0 rows)
228
+
229
+ COMMIT PREPARED 'test_prepared_lock';
230
+ -- Both will work normally after we commit
231
+ :get_no2pc
232
+ data
233
+ ----------------------------------------------------------------------------
234
+ BEGIN
235
+ table public.test_prepared1: INSERT: id[integer]:10 data[text]:'othercol'
236
+ table public.test_prepared1: INSERT: id[integer]:11 data[text]:'othercol2'
237
+ COMMIT
238
+ (4 rows)
239
+
240
+ :get_with2pc
241
+ data
242
+ ------
243
+ (0 rows)
244
+
245
+ -- cleanup
246
+ DROP TABLE test_prepared1;
247
+ DROP TABLE test_prepared2;
103
248
SELECT pg_drop_replication_slot('regression_slot');
104
249
pg_drop_replication_slot
105
250
--------------------------
106
251
107
252
(1 row)
108
253
254
+ SELECT pg_drop_replication_slot('regression_slot_2pc');
255
+ pg_drop_replication_slot
256
+ --------------------------
257
+
258
+ (1 row)
259
+
0 commit comments