1# Minimal test testing synchronous replication sync_state transition 2use strict; 3use warnings; 4use PostgresNode; 5use TestLib; 6use Test::More tests => 11; 7 8# Query checking sync_priority and sync_state of each standby 9my $check_sql = 10"SELECT application_name, sync_priority, sync_state FROM pg_stat_replication ORDER BY application_name;"; 11 12# Check that sync_state of each standby is expected (waiting till it is). 13# If $setting is given, synchronous_standby_names is set to it and 14# the configuration file is reloaded before the test. 15sub test_sync_state 16{ 17 my ($self, $expected, $msg, $setting) = @_; 18 19 if (defined($setting)) 20 { 21 $self->psql('postgres', 22 "ALTER SYSTEM SET synchronous_standby_names = '$setting';"); 23 $self->reload; 24 } 25 26 ok($self->poll_query_until('postgres', $check_sql, $expected), $msg); 27} 28 29# Start a standby and check that it is registered within the WAL sender 30# array of the given primary. This polls the primary's pg_stat_replication 31# until the standby is confirmed as registered. 32sub start_standby_and_wait 33{ 34 my ($master, $standby) = @_; 35 my $master_name = $master->name; 36 my $standby_name = $standby->name; 37 my $query = 38 "SELECT count(1) = 1 FROM pg_stat_replication WHERE application_name = '$standby_name'"; 39 40 $standby->start; 41 42 print("### Waiting for standby \"$standby_name\" on \"$master_name\"\n"); 43 $master->poll_query_until('postgres', $query); 44 return; 45} 46 47# Initialize master node 48my $node_master = get_new_node('master'); 49$node_master->init(allows_streaming => 1); 50$node_master->start; 51my $backup_name = 'master_backup'; 52 53# Take backup 54$node_master->backup($backup_name); 55 56# Create all the standbys. Their status on the primary is checked to ensure 57# the ordering of each one of them in the WAL sender array of the primary. 58 59# Create standby1 linking to master 60my $node_standby_1 = get_new_node('standby1'); 61$node_standby_1->init_from_backup($node_master, $backup_name, 62 has_streaming => 1); 63start_standby_and_wait($node_master, $node_standby_1); 64 65# Create standby2 linking to master 66my $node_standby_2 = get_new_node('standby2'); 67$node_standby_2->init_from_backup($node_master, $backup_name, 68 has_streaming => 1); 69start_standby_and_wait($node_master, $node_standby_2); 70 71# Create standby3 linking to master 72my $node_standby_3 = get_new_node('standby3'); 73$node_standby_3->init_from_backup($node_master, $backup_name, 74 has_streaming => 1); 75start_standby_and_wait($node_master, $node_standby_3); 76 77# Check that sync_state is determined correctly when 78# synchronous_standby_names is specified in old syntax. 79test_sync_state( 80 $node_master, qq(standby1|1|sync 81standby2|2|potential 82standby3|0|async), 83 'old syntax of synchronous_standby_names', 84 'standby1,standby2'); 85 86# Check that all the standbys are considered as either sync or 87# potential when * is specified in synchronous_standby_names. 88# Note that standby1 is chosen as sync standby because 89# it's stored in the head of WalSnd array which manages 90# all the standbys though they have the same priority. 91test_sync_state( 92 $node_master, qq(standby1|1|sync 93standby2|1|potential 94standby3|1|potential), 95 'asterisk in synchronous_standby_names', 96 '*'); 97 98# Stop and start standbys to rearrange the order of standbys 99# in WalSnd array. Now, if standbys have the same priority, 100# standby2 is selected preferentially and standby3 is next. 101$node_standby_1->stop; 102$node_standby_2->stop; 103$node_standby_3->stop; 104 105# Make sure that each standby reports back to the primary in the wanted 106# order. 107start_standby_and_wait($node_master, $node_standby_2); 108start_standby_and_wait($node_master, $node_standby_3); 109 110# Specify 2 as the number of sync standbys. 111# Check that two standbys are in 'sync' state. 112test_sync_state( 113 $node_master, qq(standby2|2|sync 114standby3|3|sync), 115 '2 synchronous standbys', 116 '2(standby1,standby2,standby3)'); 117 118# Start standby1 119start_standby_and_wait($node_master, $node_standby_1); 120 121# Create standby4 linking to master 122my $node_standby_4 = get_new_node('standby4'); 123$node_standby_4->init_from_backup($node_master, $backup_name, 124 has_streaming => 1); 125$node_standby_4->start; 126 127# Check that standby1 and standby2 whose names appear earlier in 128# synchronous_standby_names are considered as sync. Also check that 129# standby3 appearing later represents potential, and standby4 is 130# in 'async' state because it's not in the list. 131test_sync_state( 132 $node_master, qq(standby1|1|sync 133standby2|2|sync 134standby3|3|potential 135standby4|0|async), 136 '2 sync, 1 potential, and 1 async'); 137 138# Check that sync_state of each standby is determined correctly 139# when num_sync exceeds the number of names of potential sync standbys 140# specified in synchronous_standby_names. 141test_sync_state( 142 $node_master, qq(standby1|0|async 143standby2|4|sync 144standby3|3|sync 145standby4|1|sync), 146 'num_sync exceeds the num of potential sync standbys', 147 '6(standby4,standby0,standby3,standby2)'); 148 149# The setting that * comes before another standby name is acceptable 150# but does not make sense in most cases. Check that sync_state is 151# chosen properly even in case of that setting. standby1 is selected 152# as synchronous as it has the highest priority, and is followed by a 153# second standby listed first in the WAL sender array, which is 154# standby2 in this case. 155test_sync_state( 156 $node_master, qq(standby1|1|sync 157standby2|2|sync 158standby3|2|potential 159standby4|2|potential), 160 'asterisk before another standby name', 161 '2(standby1,*,standby2)'); 162 163# Check that the setting of '2(*)' chooses standby2 and standby3 that are stored 164# earlier in WalSnd array as sync standbys. 165test_sync_state( 166 $node_master, qq(standby1|1|potential 167standby2|1|sync 168standby3|1|sync 169standby4|1|potential), 170 'multiple standbys having the same priority are chosen as sync', 171 '2(*)'); 172 173# Stop Standby3 which is considered in 'sync' state. 174$node_standby_3->stop; 175 176# Check that the state of standby1 stored earlier in WalSnd array than 177# standby4 is transited from potential to sync. 178test_sync_state( 179 $node_master, qq(standby1|1|sync 180standby2|1|sync 181standby4|1|potential), 182 'potential standby found earlier in array is promoted to sync'); 183 184# Check that standby1 and standby2 are chosen as sync standbys 185# based on their priorities. 186test_sync_state( 187 $node_master, qq(standby1|1|sync 188standby2|2|sync 189standby4|0|async), 190 'priority-based sync replication specified by FIRST keyword', 191 'FIRST 2(standby1, standby2)'); 192 193# Check that all the listed standbys are considered as candidates 194# for sync standbys in a quorum-based sync replication. 195test_sync_state( 196 $node_master, qq(standby1|1|quorum 197standby2|1|quorum 198standby4|0|async), 199 '2 quorum and 1 async', 200 'ANY 2(standby1, standby2)'); 201 202# Start Standby3 which will be considered in 'quorum' state. 203$node_standby_3->start; 204 205# Check that the setting of 'ANY 2(*)' chooses all standbys as 206# candidates for quorum sync standbys. 207test_sync_state( 208 $node_master, qq(standby1|1|quorum 209standby2|1|quorum 210standby3|1|quorum 211standby4|1|quorum), 212 'all standbys are considered as candidates for quorum sync standbys', 213 'ANY 2(*)'); 214