Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3082098

Browse files
committed
Add tests for two-phase commit
There's some ongoing performance work on this area, so let's make sure we don't break things. Extracted from a larger patch originally by Stas Kelvich. Authors: Stas Kelvich, Nikhil Sontakke, Michael Paquier Discussion: https://postgr.es/m/CAMGcDxfsuLLOg=h5cTg3g77Jjk-UGnt=RW7zK57zBSoFsapiWA@mail.gmail.com
1 parent 74321d8 commit 3082098

File tree

1 file changed

+322
-0
lines changed

1 file changed

+322
-0
lines changed

src/test/recovery/t/009_twophase.pl

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
# Tests dedicated to two-phase commit in recovery
2+
use strict;
3+
use warnings;
4+
5+
use PostgresNode;
6+
use TestLib;
7+
use Test::More tests => 13;
8+
9+
# Setup master node
10+
my $node_master = get_new_node("master");
11+
$node_master->init(allows_streaming => 1);
12+
$node_master->append_conf('postgresql.conf', qq(
13+
max_prepared_transactions = 10
14+
log_checkpoints = true
15+
));
16+
$node_master->start;
17+
$node_master->backup('master_backup');
18+
$node_master->psql('postgres', "CREATE TABLE t_009_tbl (id int)");
19+
20+
# Setup slave node
21+
my $node_slave = get_new_node('slave');
22+
$node_slave->init_from_backup($node_master, 'master_backup', has_streaming => 1);
23+
$node_slave->start;
24+
25+
# Switch to synchronous replication
26+
$node_master->append_conf('postgresql.conf', qq(
27+
synchronous_standby_names = '*'
28+
));
29+
$node_master->psql('postgres', "SELECT pg_reload_conf()");
30+
31+
my $psql_out = '';
32+
my $psql_rc = '';
33+
34+
###############################################################################
35+
# Check that we can commit and abort transaction after soft restart.
36+
# Here checkpoint happens before shutdown and no WAL replay will occur at next
37+
# startup. In this case postgres re-creates shared-memory state from twophase
38+
# files.
39+
###############################################################################
40+
41+
$node_master->psql('postgres', "
42+
BEGIN;
43+
INSERT INTO t_009_tbl VALUES (42);
44+
SAVEPOINT s1;
45+
INSERT INTO t_009_tbl VALUES (43);
46+
PREPARE TRANSACTION 'xact_009_1';
47+
BEGIN;
48+
INSERT INTO t_009_tbl VALUES (142);
49+
SAVEPOINT s1;
50+
INSERT INTO t_009_tbl VALUES (143);
51+
PREPARE TRANSACTION 'xact_009_2';");
52+
$node_master->stop;
53+
$node_master->start;
54+
55+
$psql_rc = $node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'");
56+
is($psql_rc, '0', 'Commit prepared transaction after restart');
57+
58+
$psql_rc = $node_master->psql('postgres', "ROLLBACK PREPARED 'xact_009_2'");
59+
is($psql_rc, '0', 'Rollback prepared transaction after restart');
60+
61+
###############################################################################
62+
# Check that we can commit and abort after a hard restart.
63+
# At next startup, WAL replay will re-create shared memory state for prepared
64+
# transaction using dedicated WAL records.
65+
###############################################################################
66+
67+
$node_master->psql('postgres', "
68+
CHECKPOINT;
69+
BEGIN;
70+
INSERT INTO t_009_tbl VALUES (42);
71+
SAVEPOINT s1;
72+
INSERT INTO t_009_tbl VALUES (43);
73+
PREPARE TRANSACTION 'xact_009_1';
74+
BEGIN;
75+
INSERT INTO t_009_tbl VALUES (142);
76+
SAVEPOINT s1;
77+
INSERT INTO t_009_tbl VALUES (143);
78+
PREPARE TRANSACTION 'xact_009_2';");
79+
$node_master->teardown_node;
80+
$node_master->start;
81+
82+
$psql_rc = $node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'");
83+
is($psql_rc, '0', 'Commit prepared transaction after teardown');
84+
85+
$psql_rc = $node_master->psql('postgres', "ROLLBACK PREPARED 'xact_009_2'");
86+
is($psql_rc, '0', 'Rollback prepared transaction after teardown');
87+
88+
###############################################################################
89+
# Check that WAL replay can handle several transactions with same GID name.
90+
###############################################################################
91+
92+
$node_master->psql('postgres', "
93+
CHECKPOINT;
94+
BEGIN;
95+
INSERT INTO t_009_tbl VALUES (42);
96+
SAVEPOINT s1;
97+
INSERT INTO t_009_tbl VALUES (43);
98+
PREPARE TRANSACTION 'xact_009_1';
99+
COMMIT PREPARED 'xact_009_1';
100+
BEGIN;
101+
INSERT INTO t_009_tbl VALUES (42);
102+
SAVEPOINT s1;
103+
INSERT INTO t_009_tbl VALUES (43);
104+
PREPARE TRANSACTION 'xact_009_1';");
105+
$node_master->teardown_node;
106+
$node_master->start;
107+
108+
$psql_rc = $node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'");
109+
is($psql_rc, '0', 'Replay several transactions with same GID');
110+
111+
###############################################################################
112+
# Check that WAL replay cleans up its shared memory state and releases locks
113+
# while replaying transaction commits.
114+
###############################################################################
115+
116+
$node_master->psql('postgres', "
117+
BEGIN;
118+
INSERT INTO t_009_tbl VALUES (42);
119+
SAVEPOINT s1;
120+
INSERT INTO t_009_tbl VALUES (43);
121+
PREPARE TRANSACTION 'xact_009_1';
122+
COMMIT PREPARED 'xact_009_1';");
123+
$node_master->teardown_node;
124+
$node_master->start;
125+
$psql_rc = $node_master->psql('postgres', "
126+
BEGIN;
127+
INSERT INTO t_009_tbl VALUES (42);
128+
SAVEPOINT s1;
129+
INSERT INTO t_009_tbl VALUES (43);
130+
-- This prepare can fail due to conflicting GID or locks conflicts if
131+
-- replay did not fully cleanup its state on previous commit.
132+
PREPARE TRANSACTION 'xact_009_1';");
133+
is($psql_rc, '0', "Cleanup of shared memory state for 2PC commit");
134+
135+
$node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'");
136+
137+
###############################################################################
138+
# Check that WAL replay will cleanup its shared memory state on running slave.
139+
###############################################################################
140+
141+
$node_master->psql('postgres', "
142+
BEGIN;
143+
INSERT INTO t_009_tbl VALUES (42);
144+
SAVEPOINT s1;
145+
INSERT INTO t_009_tbl VALUES (43);
146+
PREPARE TRANSACTION 'xact_009_1';
147+
COMMIT PREPARED 'xact_009_1';");
148+
$node_slave->psql('postgres', "SELECT count(*) FROM pg_prepared_xacts",
149+
stdout => \$psql_out);
150+
is($psql_out, '0',
151+
"Cleanup of shared memory state on running standby without checkpoint");
152+
153+
###############################################################################
154+
# Same as in previous case, but let's force checkpoint on slave between
155+
# prepare and commit to use on-disk twophase files.
156+
###############################################################################
157+
158+
$node_master->psql('postgres', "
159+
BEGIN;
160+
INSERT INTO t_009_tbl VALUES (42);
161+
SAVEPOINT s1;
162+
INSERT INTO t_009_tbl VALUES (43);
163+
PREPARE TRANSACTION 'xact_009_1';");
164+
$node_slave->psql('postgres', "CHECKPOINT");
165+
$node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'");
166+
$node_slave->psql('postgres', "SELECT count(*) FROM pg_prepared_xacts",
167+
stdout => \$psql_out);
168+
is($psql_out, '0',
169+
"Cleanup of shared memory state on running standby after checkpoint");
170+
171+
###############################################################################
172+
# Check that prepared transactions can be committed on promoted slave.
173+
###############################################################################
174+
175+
$node_master->psql('postgres', "
176+
BEGIN;
177+
INSERT INTO t_009_tbl VALUES (42);
178+
SAVEPOINT s1;
179+
INSERT INTO t_009_tbl VALUES (43);
180+
PREPARE TRANSACTION 'xact_009_1';");
181+
$node_master->teardown_node;
182+
$node_slave->promote;
183+
$node_slave->poll_query_until('postgres',
184+
"SELECT NOT pg_is_in_recovery()")
185+
or die "Timed out while waiting for promotion of standby";
186+
187+
$psql_rc = $node_slave->psql('postgres', "COMMIT PREPARED 'xact_009_1'");
188+
is($psql_rc, '0', "Restore of prepared transaction on promoted slave");
189+
190+
# change roles
191+
($node_master, $node_slave) = ($node_slave, $node_master);
192+
$node_slave->enable_streaming($node_master);
193+
$node_slave->append_conf('recovery.conf', qq(
194+
recovery_target_timeline='latest'
195+
));
196+
$node_slave->start;
197+
198+
###############################################################################
199+
# Check that prepared transactions are replayed after soft restart of standby
200+
# while master is down. Since standby knows that master is down it uses a
201+
# different code path on startup to ensure that the status of transactions is
202+
# consistent.
203+
###############################################################################
204+
205+
$node_master->psql('postgres', "
206+
BEGIN;
207+
INSERT INTO t_009_tbl VALUES (42);
208+
SAVEPOINT s1;
209+
INSERT INTO t_009_tbl VALUES (43);
210+
PREPARE TRANSACTION 'xact_009_1';");
211+
$node_master->stop;
212+
$node_slave->restart;
213+
$node_slave->promote;
214+
$node_slave->poll_query_until('postgres',
215+
"SELECT NOT pg_is_in_recovery()")
216+
or die "Timed out while waiting for promotion of standby";
217+
218+
$node_slave->psql('postgres', "SELECT count(*) FROM pg_prepared_xacts",
219+
stdout => \$psql_out);
220+
is($psql_out, '1',
221+
"Restore prepared transactions from files with master down");
222+
223+
# restore state
224+
($node_master, $node_slave) = ($node_slave, $node_master);
225+
$node_slave->enable_streaming($node_master);
226+
$node_slave->append_conf('recovery.conf', qq(
227+
recovery_target_timeline='latest'
228+
));
229+
$node_slave->start;
230+
$node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'");
231+
232+
###############################################################################
233+
# Check that prepared transactions are correctly replayed after slave hard
234+
# restart while master is down.
235+
###############################################################################
236+
237+
$node_master->psql('postgres', "
238+
BEGIN;
239+
INSERT INTO t_009_tbl VALUES (242);
240+
SAVEPOINT s1;
241+
INSERT INTO t_009_tbl VALUES (243);
242+
PREPARE TRANSACTION 'xact_009_1';
243+
");
244+
$node_master->stop;
245+
$node_slave->teardown_node;
246+
$node_slave->start;
247+
$node_slave->promote;
248+
$node_slave->poll_query_until('postgres',
249+
"SELECT NOT pg_is_in_recovery()")
250+
or die "Timed out while waiting for promotion of standby";
251+
252+
$node_slave->psql('postgres', "SELECT count(*) FROM pg_prepared_xacts",
253+
stdout => \$psql_out);
254+
is($psql_out, '1',
255+
"Restore prepared transactions from records with master down");
256+
257+
# restore state
258+
($node_master, $node_slave) = ($node_slave, $node_master);
259+
$node_slave->enable_streaming($node_master);
260+
$node_slave->append_conf('recovery.conf', qq(
261+
recovery_target_timeline='latest'
262+
));
263+
$node_slave->start;
264+
$node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'");
265+
266+
267+
###############################################################################
268+
# Check for a lock conflict between prepared transaction with DDL inside and replay of
269+
# XLOG_STANDBY_LOCK wal record.
270+
###############################################################################
271+
272+
$node_master->psql('postgres', "
273+
BEGIN;
274+
CREATE TABLE t_009_tbl2 (id int);
275+
SAVEPOINT s1;
276+
INSERT INTO t_009_tbl2 VALUES (42);
277+
PREPARE TRANSACTION 'xact_009_1';
278+
-- checkpoint will issue XLOG_STANDBY_LOCK that can conflict with lock
279+
-- held by 'create table' statement
280+
CHECKPOINT;
281+
COMMIT PREPARED 'xact_009_1';");
282+
283+
$node_slave->psql('postgres', "SELECT count(*) FROM pg_prepared_xacts",
284+
stdout => \$psql_out);
285+
is($psql_out, '0', "Replay prepared transaction with DDL");
286+
287+
288+
###############################################################################
289+
# Check that replay will correctly set SUBTRANS and properly advance nextXid
290+
# so that it won't conflict with savepoint xids.
291+
###############################################################################
292+
293+
$node_master->psql('postgres', "
294+
BEGIN;
295+
DELETE FROM t_009_tbl;
296+
INSERT INTO t_009_tbl VALUES (43);
297+
SAVEPOINT s1;
298+
INSERT INTO t_009_tbl VALUES (43);
299+
SAVEPOINT s2;
300+
INSERT INTO t_009_tbl VALUES (43);
301+
SAVEPOINT s3;
302+
INSERT INTO t_009_tbl VALUES (43);
303+
SAVEPOINT s4;
304+
INSERT INTO t_009_tbl VALUES (43);
305+
SAVEPOINT s5;
306+
INSERT INTO t_009_tbl VALUES (43);
307+
PREPARE TRANSACTION 'xact_009_1';
308+
CHECKPOINT;");
309+
310+
$node_master->stop;
311+
$node_master->start;
312+
$node_master->psql('postgres', "
313+
-- here we can get xid of previous savepoint if nextXid
314+
-- wasn't properly advanced
315+
BEGIN;
316+
INSERT INTO t_009_tbl VALUES (142);
317+
ROLLBACK;
318+
COMMIT PREPARED 'xact_009_1';");
319+
320+
$node_master->psql('postgres', "SELECT count(*) FROM t_009_tbl",
321+
stdout => \$psql_out);
322+
is($psql_out, '6', "Check nextXid handling for prepared subtransactions");

0 commit comments

Comments
 (0)