Skip to content

Commit 9713fbb

Browse files
committed
port old test
1 parent 947c3fd commit 9713fbb

File tree

1 file changed

+314
-0
lines changed

1 file changed

+314
-0
lines changed

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

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

0 commit comments

Comments
 (0)