92 lines
3.0 KiB
Perl
92 lines
3.0 KiB
Perl
|
|
# Copyright (c) 2021-2023, PostgreSQL Global Development Group
|
|
|
|
# Test for point-in-time recovery (PITR) with prepared transactions
|
|
use strict;
|
|
use warnings;
|
|
use PostgreSQL::Test::Cluster;
|
|
use PostgreSQL::Test::Utils;
|
|
use Test::More;
|
|
use File::Compare;
|
|
|
|
# Initialize and start primary node with WAL archiving
|
|
my $node_primary = PostgreSQL::Test::Cluster->new('primary');
|
|
$node_primary->init(has_archiving => 1, allows_streaming => 1);
|
|
$node_primary->append_conf(
|
|
'postgresql.conf', qq{
|
|
max_prepared_transactions = 10});
|
|
$node_primary->start;
|
|
|
|
# Take backup
|
|
my $backup_name = 'my_backup';
|
|
$node_primary->backup($backup_name);
|
|
|
|
# Initialize node for PITR targeting a very specific restore point, just
|
|
# after a PREPARE TRANSACTION is issued so as we finish with a promoted
|
|
# node where this 2PC transaction needs an explicit COMMIT PREPARED.
|
|
my $node_pitr = PostgreSQL::Test::Cluster->new('node_pitr');
|
|
$node_pitr->init_from_backup(
|
|
$node_primary, $backup_name,
|
|
standby => 0,
|
|
has_restoring => 1);
|
|
$node_pitr->append_conf(
|
|
'postgresql.conf', qq{
|
|
recovery_target_name = 'rp'
|
|
recovery_target_action = 'promote'});
|
|
|
|
# Workload with a prepared transaction and the target restore point.
|
|
$node_primary->psql(
|
|
'postgres', qq{
|
|
CREATE TABLE foo(i int);
|
|
BEGIN;
|
|
INSERT INTO foo VALUES(1);
|
|
PREPARE TRANSACTION 'fooinsert';
|
|
SELECT pg_create_restore_point('rp');
|
|
INSERT INTO foo VALUES(2);
|
|
});
|
|
|
|
# Find next WAL segment to be archived
|
|
my $walfile_to_be_archived = $node_primary->safe_psql('postgres',
|
|
"SELECT pg_walfile_name(pg_current_wal_lsn());");
|
|
|
|
# Make WAL segment eligible for archival
|
|
$node_primary->safe_psql('postgres', 'SELECT pg_switch_wal()');
|
|
|
|
# Wait until the WAL segment has been archived.
|
|
my $archive_wait_query =
|
|
"SELECT '$walfile_to_be_archived' <= last_archived_wal FROM pg_stat_archiver;";
|
|
$node_primary->poll_query_until('postgres', $archive_wait_query)
|
|
or die "Timed out while waiting for WAL segment to be archived";
|
|
my $last_archived_wal_file = $walfile_to_be_archived;
|
|
|
|
# Now start the PITR node.
|
|
$node_pitr->start;
|
|
|
|
# Wait until the PITR node exits recovery.
|
|
$node_pitr->poll_query_until('postgres', "SELECT pg_is_in_recovery() = 'f';")
|
|
or die "Timed out while waiting for PITR promotion";
|
|
|
|
# Commit the prepared transaction in the latest timeline and check its
|
|
# result. There should only be one row in the table, coming from the
|
|
# prepared transaction. The row from the INSERT after the restore point
|
|
# should not show up, since our recovery target was older than the second
|
|
# INSERT done.
|
|
$node_pitr->psql('postgres', qq{COMMIT PREPARED 'fooinsert';});
|
|
my $result = $node_pitr->safe_psql('postgres', "SELECT * FROM foo;");
|
|
is($result, qq{1}, "check table contents after COMMIT PREPARED");
|
|
|
|
# Insert more data and do a checkpoint. These should be generated on the
|
|
# timeline chosen after the PITR promotion.
|
|
$node_pitr->psql(
|
|
'postgres', qq{
|
|
INSERT INTO foo VALUES(3);
|
|
CHECKPOINT;
|
|
});
|
|
|
|
# Enforce recovery, the checkpoint record generated previously should
|
|
# still be found.
|
|
$node_pitr->stop('immediate');
|
|
$node_pitr->start;
|
|
|
|
done_testing();
|