2 # Copyright (c) 2021-2022, PostgreSQL Global Development Group
4 # Tests dedicated to two-phase commit in recovery
8 use PostgreSQL
::Test
::Cluster
;
9 use PostgreSQL
::Test
::Utils
;
10 use Test
::More tests
=> 24;
15 sub configure_and_reload
17 local $Test::Builder
::Level
= $Test::Builder
::Level
+ 1;
19 my ($node, $parameter) = @_;
20 my $name = $node->name;
23 'postgresql.conf', qq(
26 $node->psql('postgres', "SELECT pg_reload_conf()", stdout
=> \
$psql_out);
27 is
($psql_out, 't', "reload node $name with $parameter");
31 # Set up two nodes, which will alternately be primary and replication standby.
34 my $node_london = PostgreSQL
::Test
::Cluster
->new("london");
35 $node_london->init(allows_streaming
=> 1);
36 $node_london->append_conf(
37 'postgresql.conf', qq(
38 max_prepared_transactions
= 10
39 log_checkpoints
= true
42 $node_london->backup('london_backup');
45 my $node_paris = PostgreSQL
::Test
::Cluster
->new('paris');
46 $node_paris->init_from_backup($node_london, 'london_backup',
50 # Switch to synchronous replication in both directions
51 configure_and_reload
($node_london, "synchronous_standby_names = 'paris'");
52 configure_and_reload
($node_paris, "synchronous_standby_names = 'london'");
54 # Set up nonce names for current primary and standby nodes
55 note
"Initially, london is primary and paris is standby";
56 my ($cur_primary, $cur_standby) = ($node_london, $node_paris);
57 my $cur_primary_name = $cur_primary->name;
59 # Create table we'll use in the test transactions
60 $cur_primary->psql('postgres', "CREATE TABLE t_009_tbl (id int, msg text)");
62 ###############################################################################
63 # Check that we can commit and abort transaction after soft restart.
64 # Here checkpoint happens before shutdown and no WAL replay will occur at next
65 # startup. In this case postgres re-creates shared-memory state from twophase
67 ###############################################################################
72 INSERT INTO t_009_tbl VALUES (1, 'issued to ${cur_primary_name}');
74 INSERT INTO t_009_tbl VALUES (2, 'issued to ${cur_primary_name}');
75 PREPARE TRANSACTION 'xact_009_1';
77 INSERT INTO t_009_tbl VALUES (3, 'issued to ${cur_primary_name}');
79 INSERT INTO t_009_tbl VALUES (4, 'issued to ${cur_primary_name}');
80 PREPARE TRANSACTION 'xact_009_2';");
84 $psql_rc = $cur_primary->psql('postgres', "COMMIT PREPARED 'xact_009_1'");
85 is
($psql_rc, '0', 'Commit prepared transaction after restart');
87 $psql_rc = $cur_primary->psql('postgres', "ROLLBACK PREPARED 'xact_009_2'");
88 is
($psql_rc, '0', 'Rollback prepared transaction after restart');
90 ###############################################################################
91 # Check that we can commit and abort after a hard restart.
92 # At next startup, WAL replay will re-create shared memory state for prepared
93 # transaction using dedicated WAL records.
94 ###############################################################################
100 INSERT INTO t_009_tbl VALUES (5, 'issued to ${cur_primary_name}');
102 INSERT INTO t_009_tbl VALUES (6, 'issued to ${cur_primary_name}');
103 PREPARE TRANSACTION 'xact_009_3';
105 INSERT INTO t_009_tbl VALUES (7, 'issued to ${cur_primary_name}');
107 INSERT INTO t_009_tbl VALUES (8, 'issued to ${cur_primary_name}');
108 PREPARE TRANSACTION 'xact_009_4';");
109 $cur_primary->teardown_node;
112 $psql_rc = $cur_primary->psql('postgres', "COMMIT PREPARED 'xact_009_3'");
113 is
($psql_rc, '0', 'Commit prepared transaction after teardown');
115 $psql_rc = $cur_primary->psql('postgres', "ROLLBACK PREPARED 'xact_009_4'");
116 is
($psql_rc, '0', 'Rollback prepared transaction after teardown');
118 ###############################################################################
119 # Check that WAL replay can handle several transactions with same GID name.
120 ###############################################################################
126 INSERT INTO t_009_tbl VALUES (9, 'issued to ${cur_primary_name}');
128 INSERT INTO t_009_tbl VALUES (10, 'issued to ${cur_primary_name}');
129 PREPARE TRANSACTION 'xact_009_5';
130 COMMIT PREPARED 'xact_009_5';
132 INSERT INTO t_009_tbl VALUES (11, 'issued to ${cur_primary_name}');
134 INSERT INTO t_009_tbl VALUES (12, 'issued to ${cur_primary_name}');
135 PREPARE TRANSACTION 'xact_009_5';");
136 $cur_primary->teardown_node;
139 $psql_rc = $cur_primary->psql('postgres', "COMMIT PREPARED 'xact_009_5'");
140 is
($psql_rc, '0', 'Replay several transactions with same GID');
142 ###############################################################################
143 # Check that WAL replay cleans up its shared memory state and releases locks
144 # while replaying transaction commits.
145 ###############################################################################
150 INSERT INTO t_009_tbl VALUES (13, 'issued to ${cur_primary_name}');
152 INSERT INTO t_009_tbl VALUES (14, 'issued to ${cur_primary_name}');
153 PREPARE TRANSACTION 'xact_009_6';
154 COMMIT PREPARED 'xact_009_6';");
155 $cur_primary->teardown_node;
157 $psql_rc = $cur_primary->psql(
160 INSERT INTO t_009_tbl VALUES (15, 'issued to ${cur_primary_name}');
162 INSERT INTO t_009_tbl VALUES (16, 'issued to ${cur_primary_name}');
163 -- This prepare can fail due to conflicting GID or locks conflicts if
164 -- replay did not fully cleanup its state on previous commit.
165 PREPARE TRANSACTION 'xact_009_7';");
166 is
($psql_rc, '0', "Cleanup of shared memory state for 2PC commit");
168 $cur_primary->psql('postgres', "COMMIT PREPARED 'xact_009_7'");
170 ###############################################################################
171 # Check that WAL replay will cleanup its shared memory state on running standby.
172 ###############################################################################
177 INSERT INTO t_009_tbl VALUES (17, 'issued to ${cur_primary_name}');
179 INSERT INTO t_009_tbl VALUES (18, 'issued to ${cur_primary_name}');
180 PREPARE TRANSACTION 'xact_009_8';
181 COMMIT PREPARED 'xact_009_8';");
184 "SELECT count(*) FROM pg_prepared_xacts",
185 stdout
=> \
$psql_out);
187 "Cleanup of shared memory state on running standby without checkpoint");
189 ###############################################################################
190 # Same as in previous case, but let's force checkpoint on standby between
191 # prepare and commit to use on-disk twophase files.
192 ###############################################################################
197 INSERT INTO t_009_tbl VALUES (19, 'issued to ${cur_primary_name}');
199 INSERT INTO t_009_tbl VALUES (20, 'issued to ${cur_primary_name}');
200 PREPARE TRANSACTION 'xact_009_9';");
201 $cur_standby->psql('postgres', "CHECKPOINT");
202 $cur_primary->psql('postgres', "COMMIT PREPARED 'xact_009_9'");
205 "SELECT count(*) FROM pg_prepared_xacts",
206 stdout
=> \
$psql_out);
208 "Cleanup of shared memory state on running standby after checkpoint");
210 ###############################################################################
211 # Check that prepared transactions can be committed on promoted standby.
212 ###############################################################################
217 INSERT INTO t_009_tbl VALUES (21, 'issued to ${cur_primary_name}');
219 INSERT INTO t_009_tbl VALUES (22, 'issued to ${cur_primary_name}');
220 PREPARE TRANSACTION 'xact_009_10';");
221 $cur_primary->teardown_node;
222 $cur_standby->promote;
225 note
"Now paris is primary and london is standby";
226 ($cur_primary, $cur_standby) = ($node_paris, $node_london);
227 $cur_primary_name = $cur_primary->name;
229 # because london is not running at this point, we can't use syncrep commit
231 $psql_rc = $cur_primary->psql('postgres',
232 "SET synchronous_commit = off; COMMIT PREPARED 'xact_009_10'");
233 is
($psql_rc, '0', "Restore of prepared transaction on promoted standby");
235 # restart old primary as new standby
236 $cur_standby->enable_streaming($cur_primary);
239 ###############################################################################
240 # Check that prepared transactions are replayed after soft restart of standby
241 # while primary is down. Since standby knows that primary is down it uses a
242 # different code path on startup to ensure that the status of transactions is
244 ###############################################################################
249 INSERT INTO t_009_tbl VALUES (23, 'issued to ${cur_primary_name}');
251 INSERT INTO t_009_tbl VALUES (24, 'issued to ${cur_primary_name}');
252 PREPARE TRANSACTION 'xact_009_11';");
254 $cur_standby->restart;
255 $cur_standby->promote;
258 note
"Now london is primary and paris is standby";
259 ($cur_primary, $cur_standby) = ($node_london, $node_paris);
260 $cur_primary_name = $cur_primary->name;
264 "SELECT count(*) FROM pg_prepared_xacts",
265 stdout
=> \
$psql_out);
267 "Restore prepared transactions from files with primary down");
269 # restart old primary as new standby
270 $cur_standby->enable_streaming($cur_primary);
273 $cur_primary->psql('postgres', "COMMIT PREPARED 'xact_009_11'");
275 ###############################################################################
276 # Check that prepared transactions are correctly replayed after standby hard
277 # restart while primary is down.
278 ###############################################################################
283 INSERT INTO t_009_tbl VALUES (25, 'issued to ${cur_primary_name}');
285 INSERT INTO t_009_tbl VALUES (26, 'issued to ${cur_primary_name}');
286 PREPARE TRANSACTION 'xact_009_12';
289 $cur_standby->teardown_node;
291 $cur_standby->promote;
294 note
"Now paris is primary and london is standby";
295 ($cur_primary, $cur_standby) = ($node_paris, $node_london);
296 $cur_primary_name = $cur_primary->name;
300 "SELECT count(*) FROM pg_prepared_xacts",
301 stdout
=> \
$psql_out);
303 "Restore prepared transactions from records with primary down");
305 # restart old primary as new standby
306 $cur_standby->enable_streaming($cur_primary);
309 $cur_primary->psql('postgres', "COMMIT PREPARED 'xact_009_12'");
311 ###############################################################################
312 # Check for a lock conflict between prepared transaction with DDL inside and
313 # replay of XLOG_STANDBY_LOCK wal record.
314 ###############################################################################
319 CREATE TABLE t_009_tbl2 (id int, msg text);
321 INSERT INTO t_009_tbl2 VALUES (27, 'issued to ${cur_primary_name}');
322 PREPARE TRANSACTION 'xact_009_13';
323 -- checkpoint will issue XLOG_STANDBY_LOCK that can conflict with lock
324 -- held by 'create table' statement
326 COMMIT PREPARED 'xact_009_13';");
328 # Ensure that last transaction is replayed on standby.
329 my $cur_primary_lsn =
330 $cur_primary->safe_psql('postgres', "SELECT pg_current_wal_lsn()");
332 "SELECT '$cur_primary_lsn'::pg_lsn <= pg_last_wal_replay_lsn()";
333 $cur_standby->poll_query_until('postgres', $caughtup_query)
334 or die "Timed out while waiting for standby to catch up";
338 "SELECT count(*) FROM t_009_tbl2",
339 stdout
=> \
$psql_out);
340 is
($psql_out, '1', "Replay prepared transaction with DDL");
342 ###############################################################################
343 # Check recovery of prepared transaction with DDL inside after a hard restart
345 ###############################################################################
350 CREATE TABLE t_009_tbl3 (id int, msg text);
352 INSERT INTO t_009_tbl3 VALUES (28, 'issued to ${cur_primary_name}');
353 PREPARE TRANSACTION 'xact_009_14';
355 CREATE TABLE t_009_tbl4 (id int, msg text);
357 INSERT INTO t_009_tbl4 VALUES (29, 'issued to ${cur_primary_name}');
358 PREPARE TRANSACTION 'xact_009_15';");
360 $cur_primary->teardown_node;
363 $psql_rc = $cur_primary->psql('postgres', "COMMIT PREPARED 'xact_009_14'");
364 is
($psql_rc, '0', 'Commit prepared transaction after teardown');
366 $psql_rc = $cur_primary->psql('postgres', "ROLLBACK PREPARED 'xact_009_15'");
367 is
($psql_rc, '0', 'Rollback prepared transaction after teardown');
369 ###############################################################################
370 # Check recovery of prepared transaction with DDL inside after a soft restart
372 ###############################################################################
377 CREATE TABLE t_009_tbl5 (id int, msg text);
379 INSERT INTO t_009_tbl5 VALUES (30, 'issued to ${cur_primary_name}');
380 PREPARE TRANSACTION 'xact_009_16';
382 CREATE TABLE t_009_tbl6 (id int, msg text);
384 INSERT INTO t_009_tbl6 VALUES (31, 'issued to ${cur_primary_name}');
385 PREPARE TRANSACTION 'xact_009_17';");
390 $psql_rc = $cur_primary->psql('postgres', "COMMIT PREPARED 'xact_009_16'");
391 is
($psql_rc, '0', 'Commit prepared transaction after restart');
393 $psql_rc = $cur_primary->psql('postgres', "ROLLBACK PREPARED 'xact_009_17'");
394 is
($psql_rc, '0', 'Rollback prepared transaction after restart');
396 ###############################################################################
397 # Verify expected data appears on both servers.
398 ###############################################################################
402 "SELECT count(*) FROM pg_prepared_xacts",
403 stdout
=> \
$psql_out);
404 is
($psql_out, '0', "No uncommitted prepared transactions on primary");
408 "SELECT * FROM t_009_tbl ORDER BY id",
409 stdout
=> \
$psql_out);
410 is
( $psql_out, qq{1|issued to london
431 26|issued to london
},
432 "Check expected t_009_tbl data on primary");
436 "SELECT * FROM t_009_tbl2",
437 stdout
=> \
$psql_out);
439 qq{27|issued to paris
},
440 "Check expected t_009_tbl2 data on primary");
444 "SELECT count(*) FROM pg_prepared_xacts",
445 stdout
=> \
$psql_out);
446 is
($psql_out, '0', "No uncommitted prepared transactions on standby");
450 "SELECT * FROM t_009_tbl ORDER BY id",
451 stdout
=> \
$psql_out);
452 is
( $psql_out, qq{1|issued to london
473 26|issued to london
},
474 "Check expected t_009_tbl data on standby");
478 "SELECT * FROM t_009_tbl2",
479 stdout
=> \
$psql_out);
481 qq{27|issued to paris
},
482 "Check expected t_009_tbl2 data on standby");