2 # Copyright (c) 2021-2022, PostgreSQL Global Development Group
4 # Minimal test testing synchronous replication sync_state transition
7 use PostgreSQL
::Test
::Cluster
;
8 use PostgreSQL
::Test
::Utils
;
9 use Test
::More tests
=> 11;
11 # Query checking sync_priority and sync_state of each standby
13 "SELECT application_name, sync_priority, sync_state FROM pg_stat_replication ORDER BY application_name;";
15 # Check that sync_state of each standby is expected (waiting till it is).
16 # If $setting is given, synchronous_standby_names is set to it and
17 # the configuration file is reloaded before the test.
20 local $Test::Builder
::Level
= $Test::Builder
::Level
+ 1;
22 my ($self, $expected, $msg, $setting) = @_;
24 if (defined($setting))
26 $self->safe_psql('postgres',
27 "ALTER SYSTEM SET synchronous_standby_names = '$setting';");
31 ok
($self->poll_query_until('postgres', $check_sql, $expected), $msg);
35 # Start a standby and check that it is registered within the WAL sender
36 # array of the given primary. This polls the primary's pg_stat_replication
37 # until the standby is confirmed as registered.
38 sub start_standby_and_wait
40 my ($primary, $standby) = @_;
41 my $primary_name = $primary->name;
42 my $standby_name = $standby->name;
44 "SELECT count(1) = 1 FROM pg_stat_replication WHERE application_name = '$standby_name'";
48 print("### Waiting for standby \"$standby_name\" on \"$primary_name\"\n");
49 $primary->poll_query_until('postgres', $query);
53 # Initialize primary node
54 my $node_primary = PostgreSQL
::Test
::Cluster
->new('primary');
55 $node_primary->init(allows_streaming
=> 1);
57 my $backup_name = 'primary_backup';
60 $node_primary->backup($backup_name);
62 # Create all the standbys. Their status on the primary is checked to ensure
63 # the ordering of each one of them in the WAL sender array of the primary.
65 # Create standby1 linking to primary
66 my $node_standby_1 = PostgreSQL
::Test
::Cluster
->new('standby1');
67 $node_standby_1->init_from_backup($node_primary, $backup_name,
69 start_standby_and_wait
($node_primary, $node_standby_1);
71 # Create standby2 linking to primary
72 my $node_standby_2 = PostgreSQL
::Test
::Cluster
->new('standby2');
73 $node_standby_2->init_from_backup($node_primary, $backup_name,
75 start_standby_and_wait
($node_primary, $node_standby_2);
77 # Create standby3 linking to primary
78 my $node_standby_3 = PostgreSQL
::Test
::Cluster
->new('standby3');
79 $node_standby_3->init_from_backup($node_primary, $backup_name,
81 start_standby_and_wait
($node_primary, $node_standby_3);
83 # Check that sync_state is determined correctly when
84 # synchronous_standby_names is specified in old syntax.
86 $node_primary, qq(standby1
|1|sync
89 'old syntax of synchronous_standby_names',
92 # Check that all the standbys are considered as either sync or
93 # potential when * is specified in synchronous_standby_names.
94 # Note that standby1 is chosen as sync standby because
95 # it's stored in the head of WalSnd array which manages
96 # all the standbys though they have the same priority.
98 $node_primary, qq(standby1
|1|sync
100 standby3
|1|potential
),
101 'asterisk in synchronous_standby_names',
104 # Stop and start standbys to rearrange the order of standbys
105 # in WalSnd array. Now, if standbys have the same priority,
106 # standby2 is selected preferentially and standby3 is next.
107 $node_standby_1->stop;
108 $node_standby_2->stop;
109 $node_standby_3->stop;
111 # Make sure that each standby reports back to the primary in the wanted
113 start_standby_and_wait
($node_primary, $node_standby_2);
114 start_standby_and_wait
($node_primary, $node_standby_3);
116 # Specify 2 as the number of sync standbys.
117 # Check that two standbys are in 'sync' state.
119 $node_primary, qq(standby2
|2|sync
121 '2 synchronous standbys',
122 '2(standby1,standby2,standby3)');
125 start_standby_and_wait
($node_primary, $node_standby_1);
127 # Create standby4 linking to primary
128 my $node_standby_4 = PostgreSQL
::Test
::Cluster
->new('standby4');
129 $node_standby_4->init_from_backup($node_primary, $backup_name,
131 $node_standby_4->start;
133 # Check that standby1 and standby2 whose names appear earlier in
134 # synchronous_standby_names are considered as sync. Also check that
135 # standby3 appearing later represents potential, and standby4 is
136 # in 'async' state because it's not in the list.
138 $node_primary, qq(standby1
|1|sync
142 '2 sync, 1 potential, and 1 async');
144 # Check that sync_state of each standby is determined correctly
145 # when num_sync exceeds the number of names of potential sync standbys
146 # specified in synchronous_standby_names.
148 $node_primary, qq(standby1
|0|async
152 'num_sync exceeds the num of potential sync standbys',
153 '6(standby4,standby0,standby3,standby2)');
155 # The setting that * comes before another standby name is acceptable
156 # but does not make sense in most cases. Check that sync_state is
157 # chosen properly even in case of that setting. standby1 is selected
158 # as synchronous as it has the highest priority, and is followed by a
159 # second standby listed first in the WAL sender array, which is
160 # standby2 in this case.
162 $node_primary, qq(standby1
|1|sync
165 standby4
|2|potential
),
166 'asterisk before another standby name',
167 '2(standby1,*,standby2)');
169 # Check that the setting of '2(*)' chooses standby2 and standby3 that are stored
170 # earlier in WalSnd array as sync standbys.
172 $node_primary, qq(standby1
|1|potential
175 standby4
|1|potential
),
176 'multiple standbys having the same priority are chosen as sync',
179 # Stop Standby3 which is considered in 'sync' state.
180 $node_standby_3->stop;
182 # Check that the state of standby1 stored earlier in WalSnd array than
183 # standby4 is transited from potential to sync.
185 $node_primary, qq(standby1
|1|sync
187 standby4
|1|potential
),
188 'potential standby found earlier in array is promoted to sync');
190 # Check that standby1 and standby2 are chosen as sync standbys
191 # based on their priorities.
193 $node_primary, qq(standby1
|1|sync
196 'priority-based sync replication specified by FIRST keyword',
197 'FIRST 2(standby1, standby2)');
199 # Check that all the listed standbys are considered as candidates
200 # for sync standbys in a quorum-based sync replication.
202 $node_primary, qq(standby1
|1|quorum
205 '2 quorum and 1 async',
206 'ANY 2(standby1, standby2)');
208 # Start Standby3 which will be considered in 'quorum' state.
209 $node_standby_3->start;
211 # Check that the setting of 'ANY 2(*)' chooses all standbys as
212 # candidates for quorum sync standbys.
214 $node_primary, qq(standby1
|1|quorum
218 'all standbys are considered as candidates for quorum sync standbys',