@@ -6,69 +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_get_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
154
+ (4 rows)
155
+
156
+ :get_with2pc
157
+ data
158
+ -------------------------------------------------------------------------
159
+ BEGIN
160
+ table public.test_prepared1: INSERT: id[integer]:5
161
+ table public.test_prepared1: INSERT: id[integer]:6 data[text]:'frakbar'
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
+ --------------------------------------------------------------------
172
+ BEGIN
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
177
+ COMMIT
178
+ (6 rows)
179
+
180
+ :get_no2pc
181
+ data
182
+ --------------------------------------------------------------------
61
183
BEGIN
62
184
table public.test_prepared1: INSERT: id[integer]:8 data[text]:null
63
185
COMMIT
64
186
BEGIN
65
187
table public.test_prepared2: INSERT: id[integer]:9
66
188
COMMIT
67
- (22 rows)
189
+ (6 rows)
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)
68
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;
69
248
SELECT pg_drop_replication_slot('regression_slot');
70
249
pg_drop_replication_slot
71
250
--------------------------
72
251
73
252
(1 row)
74
253
254
+ SELECT pg_drop_replication_slot('regression_slot_2pc');
255
+ pg_drop_replication_slot
256
+ --------------------------
257
+
258
+ (1 row)
259
+
0 commit comments