1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
# Copyright (c) 2021-2023, PostgreSQL Global Development Group
#
# Test using a standby server as the source.
#
# This sets up three nodes: A, B and C. First, A is the primary,
# B follows A, and C follows B:
#
# A (primary) <--- B (standby) <--- C (standby)
#
#
# Then we promote C, and insert some divergent rows in A and C:
#
# A (primary) <--- B (standby) C (primary)
#
#
# Finally, we run pg_rewind on C, to re-point it at B again:
#
# A (primary) <--- B (standby) <--- C (standby)
#
#
# The test is similar to the basic tests, but since we're dealing with
# three nodes, not two, we cannot use most of the RewindTest functions
# as is.
use strict;
use warnings;
use PostgreSQL::Test::Utils;
use Test::More;
use FindBin;
use lib $FindBin::RealBin;
use File::Copy;
use PostgreSQL::Test::Cluster;
use RewindTest;
my $tmp_folder = PostgreSQL::Test::Utils::tempdir;
my $node_a;
my $node_b;
my $node_c;
# Set up node A, as primary
#
# A (primary)
setup_cluster('a');
start_primary();
$node_a = $node_primary;
# Create a test table and insert a row in primary.
$node_a->safe_psql('postgres', "CREATE TABLE tbl1 (d text)");
$node_a->safe_psql('postgres', "INSERT INTO tbl1 VALUES ('in A')");
primary_psql("CHECKPOINT");
# Set up node B and C, as cascaded standbys
#
# A (primary) <--- B (standby) <--- C (standby)
$node_a->backup('my_backup');
$node_b = PostgreSQL::Test::Cluster->new('node_b');
$node_b->init_from_backup($node_a, 'my_backup', has_streaming => 1);
$node_b->set_standby_mode();
$node_b->start;
$node_b->backup('my_backup');
$node_c = PostgreSQL::Test::Cluster->new('node_c');
$node_c->init_from_backup($node_b, 'my_backup', has_streaming => 1);
$node_c->set_standby_mode();
$node_c->start;
# Insert additional data on A, and wait for both standbys to catch up.
$node_a->safe_psql('postgres',
"INSERT INTO tbl1 values ('in A, before promotion')");
$node_a->safe_psql('postgres', 'CHECKPOINT');
my $lsn = $node_a->lsn('write');
$node_a->wait_for_catchup('node_b', 'write', $lsn);
$node_b->wait_for_catchup('node_c', 'write', $lsn);
# Promote C
#
# A (primary) <--- B (standby) C (primary)
$node_c->promote;
# Insert a row in A. This causes A/B and C to have "diverged", so that it's
# no longer possible to just apply the standy's logs over primary directory
# - you need to rewind.
$node_a->safe_psql('postgres',
"INSERT INTO tbl1 VALUES ('in A, after C was promoted')");
# make sure it's replicated to B before we continue
$node_a->wait_for_catchup('node_b');
# Also insert a new row in the standby, which won't be present in the
# old primary.
$node_c->safe_psql('postgres',
"INSERT INTO tbl1 VALUES ('in C, after C was promoted')");
#
# All set up. We're ready to run pg_rewind.
#
my $node_c_pgdata = $node_c->data_dir;
# Stop the node and be ready to perform the rewind.
$node_c->stop('fast');
# Keep a temporary postgresql.conf or it would be overwritten during the rewind.
copy(
"$node_c_pgdata/postgresql.conf",
"$tmp_folder/node_c-postgresql.conf.tmp");
{
# Temporarily unset PGAPPNAME so that the server doesn't
# inherit it. Otherwise this could affect libpqwalreceiver
# connections in confusing ways.
local %ENV = %ENV;
delete $ENV{PGAPPNAME};
# Do rewind using a remote connection as source, generating
# recovery configuration automatically.
command_ok(
[
'pg_rewind', "--debug",
"--source-server", $node_b->connstr('postgres'),
"--target-pgdata=$node_c_pgdata", "--no-sync",
"--write-recovery-conf"
],
'pg_rewind remote');
}
# Now move back postgresql.conf with old settings
move(
"$tmp_folder/node_c-postgresql.conf.tmp",
"$node_c_pgdata/postgresql.conf");
# Restart the node.
$node_c->start;
# set RewindTest::node_primary to point to the rewound node, so that we can
# use check_query()
$node_primary = $node_c;
# Run some checks to verify that C has been successfully rewound,
# and connected back to follow B.
check_query(
'SELECT * FROM tbl1',
qq(in A
in A, before promotion
in A, after C was promoted
),
'table content after rewind');
# Insert another row, and observe that it's cascaded from A to B to C.
$node_a->safe_psql('postgres',
"INSERT INTO tbl1 values ('in A, after rewind')");
$node_b->wait_for_replay_catchup('node_c', $node_a);
check_query(
'SELECT * FROM tbl1',
qq(in A
in A, before promotion
in A, after C was promoted
in A, after rewind
),
'table content after rewind and insert');
# clean up
$node_a->teardown_node;
$node_b->teardown_node;
$node_c->teardown_node;
done_testing();
|