1
2# Copyright (c) 2021, PostgreSQL Global Development Group
3
4# Testing streaming replication where standby is promoted and a new cascading
5# standby (without WAL) is connected to the promoted standby.  Both archiving
6# and streaming are enabled, but only the history file is available from the
7# archive, so the WAL files all have to be streamed.  Test that the cascading
8# standby can follow the new primary (promoted standby).
9use strict;
10use warnings;
11use PostgresNode;
12use TestLib;
13
14use File::Basename;
15use FindBin;
16use Test::More tests => 1;
17
18# Initialize primary node
19my $node_primary = get_new_node('primary');
20
21# Set up an archive command that will copy the history file but not the WAL
22# files. No real archive command should behave this way; the point is to
23# simulate a race condition where the new cascading standby starts up after
24# the timeline history file reaches the archive but before any of the WAL files
25# get there.
26$node_primary->init(allows_streaming => 1, has_archiving => 1);
27
28# Note: consistent use of forward slashes here avoids any escaping problems
29# that arise from use of backslashes. That means we need to double-quote all
30# the paths in the archive_command
31my $perlbin = TestLib::perl2host($^X);
32$perlbin =~ s!\\!/!g if $TestLib::windows_os;
33my $archivedir_primary = $node_primary->archive_dir;
34$archivedir_primary =~ s!\\!/!g if $TestLib::windows_os;
35$node_primary->append_conf('postgresql.conf', qq(
36archive_command = '"$perlbin" "$FindBin::RealBin/cp_history_files" "%p" "$archivedir_primary/%f"'
37wal_keep_segments=8
38));
39# Make sure that Msys perl doesn't complain about difficulty in setting locale
40# when called from the archive_command.
41local $ENV{PERL_BADLANG}=0;
42$node_primary->start;
43
44# Take backup from primary
45my $backup_name = 'my_backup';
46$node_primary->backup($backup_name);
47
48# Create streaming standby linking to primary
49my $node_standby = get_new_node('standby');
50$node_standby->init_from_backup($node_primary, $backup_name,
51	allows_streaming => 1, has_streaming => 1, has_archiving => 1);
52$node_standby->start;
53
54# Take backup of standby, use -Xnone so that pg_wal is empty.
55$node_standby->backup($backup_name, backup_options => ['-Xnone']);
56
57# Create cascading standby but don't start it yet.
58# Must set up both streaming and archiving.
59my $node_cascade = get_new_node('cascade');
60$node_cascade->init_from_backup($node_standby, $backup_name,
61	has_streaming => 1);
62$node_cascade->enable_restoring($node_primary);
63$node_cascade->append_conf('recovery.conf', qq(
64recovery_target_timeline='latest'
65));
66
67# Promote the standby.
68$node_standby->promote;
69
70# Wait for promotion to complete
71$node_standby->poll_query_until('postgres',
72								"SELECT NOT pg_is_in_recovery();")
73	or die "Timed out while waiting for promotion";
74
75# Find next WAL segment to be archived
76my $walfile_to_be_archived = $node_standby->safe_psql('postgres',
77	"SELECT pg_walfile_name(pg_current_wal_lsn());");
78
79# Make WAL segment eligible for archival
80$node_standby->safe_psql('postgres', 'SELECT pg_switch_wal()');
81
82# Wait until the WAL segment has been archived.
83# Since the history file gets created on promotion and is archived before any
84# WAL segment, this is enough to guarantee that the history file was
85# archived.
86my $archive_wait_query =
87  "SELECT '$walfile_to_be_archived' <= last_archived_wal FROM pg_stat_archiver";
88$node_standby->poll_query_until('postgres', $archive_wait_query)
89  or die "Timed out while waiting for WAL segment to be archived";
90my $last_archived_wal_file = $walfile_to_be_archived;
91
92# Start cascade node
93$node_cascade->start;
94
95# Create some content on promoted standby and check its presence on the
96# cascading standby.
97$node_standby->safe_psql('postgres', "CREATE TABLE tab_int AS SELECT 1 AS a");
98
99# Wait for the replication to catch up
100$node_standby->wait_for_catchup($node_cascade, 'replay',
101	$node_standby->lsn('insert'));
102
103# Check that cascading standby has the new content
104my $result =
105  $node_cascade->safe_psql('postgres', "SELECT count(*) FROM tab_int");
106print "cascade: $result\n";
107is($result, 1, 'check streamed content on cascade standby');
108