Improve TAP test function PostgresNode::poll_query_until().

Add an optional "expected" argument to override the default assumption
that we're waiting for the query to return "t".  This allows replacing
a handwritten polling loop in recovery/t/007_sync_rep.pl with use of
poll_query_until(); AFAICS that's the only remaining ad-hoc polling
loop in our TAP tests.

Change poll_query_until() to probe ten times per second not once per
second.  Like some similar changes I've been making recently, the
one-second interval seems to be rooted in ancient traditions rather
than the actual likely wait duration on modern machines.  I'd consider
reducing it further if there were a convenient way to spawn just one
psql for the whole loop rather than one per probe attempt.

Discussion: https://postgr.es/m/12486.1498938782@sss.pgh.pa.us
This commit is contained in:
Tom Lane 2017-07-02 14:03:41 -04:00
parent 2dca03439f
commit de3de0afd7
2 changed files with 20 additions and 30 deletions

View File

@ -1213,36 +1213,43 @@ sub psql
=pod =pod
=item $node->poll_query_until(dbname, query) =item $node->poll_query_until($dbname, $query [, $expected ])
Run a query once a second, until it returns 't' (i.e. SQL boolean true). Run B<$query> repeatedly, until it returns the B<$expected> result
Continues polling if psql returns an error result. Times out after 180 seconds. ('t', or SQL boolean true, by default).
Continues polling if B<psql> returns an error result.
Times out after 180 seconds.
Returns 1 if successful, 0 if timed out.
=cut =cut
sub poll_query_until sub poll_query_until
{ {
my ($self, $dbname, $query) = @_; my ($self, $dbname, $query, $expected) = @_;
my $max_attempts = 180; $expected = 't' unless defined($expected); # default value
my $attempts = 0;
my $cmd =
[ 'psql', '-XAt', '-c', $query, '-d', $self->connstr($dbname) ];
my ($stdout, $stderr); my ($stdout, $stderr);
my $max_attempts = 180 * 10;
my $attempts = 0;
while ($attempts < $max_attempts) while ($attempts < $max_attempts)
{ {
my $cmd =
[ 'psql', '-XAt', '-c', $query, '-d', $self->connstr($dbname) ];
my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr; my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
chomp($stdout); chomp($stdout);
$stdout =~ s/\r//g if $TestLib::windows_os; $stdout =~ s/\r//g if $TestLib::windows_os;
if ($stdout eq "t")
if ($stdout eq $expected)
{ {
return 1; return 1;
} }
# Wait a second before retrying. # Wait 0.1 second before retrying.
sleep 1; select undef, undef, undef, 0.1;
$attempts++; $attempts++;
} }

View File

@ -9,7 +9,7 @@ use Test::More tests => 11;
my $check_sql = my $check_sql =
"SELECT application_name, sync_priority, sync_state FROM pg_stat_replication ORDER BY application_name;"; "SELECT application_name, sync_priority, sync_state FROM pg_stat_replication ORDER BY application_name;";
# Check that sync_state of each standby is expected. # Check that sync_state of each standby is expected (waiting till it is).
# If $setting is given, synchronous_standby_names is set to it and # If $setting is given, synchronous_standby_names is set to it and
# the configuration file is reloaded before the test. # the configuration file is reloaded before the test.
sub test_sync_state sub test_sync_state
@ -23,24 +23,7 @@ sub test_sync_state
$self->reload; $self->reload;
} }
my $timeout_max = 30; ok( $self->poll_query_until('postgres', $check_sql, $expected), $msg);
my $timeout = 0;
my $result;
# A reload may take some time to take effect on busy machines,
# hence use a loop with a timeout to give some room for the test
# to pass.
while ($timeout < $timeout_max)
{
$result = $self->safe_psql('postgres', $check_sql);
last if ($result eq $expected);
$timeout++;
sleep 1;
}
is($result, $expected, $msg);
} }
# Initialize master node # Initialize master node