Skip to content

Commit eb2a613

Browse files
Add a pg_recvlogical wrapper to PostgresNode
Allows testing of logical decoding using SQL interface and/or pg_recvlogical Most logical decoding tests are in contrib/test_decoding. This module is for work that doesn't fit well there, like where server restarts are required. Craig Ringer
1 parent d3cc37f commit eb2a613

File tree

2 files changed

+108
-1
lines changed

2 files changed

+108
-1
lines changed

src/test/perl/PostgresNode.pm

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,6 +1505,84 @@ sub slot
15051505

15061506
=pod
15071507
1508+
=item $node->pg_recvlogical_upto(self, dbname, slot_name, endpos, timeout_secs, ...)
1509+
1510+
Invoke pg_recvlogical to read from slot_name on dbname until LSN endpos, which
1511+
corresponds to pg_recvlogical --endpos. Gives up after timeout (if nonzero).
1512+
1513+
Disallows pg_recvlogical from internally retrying on error by passing --no-loop.
1514+
1515+
Plugin options are passed as additional keyword arguments.
1516+
1517+
If called in scalar context, returns stdout, and die()s on timeout or nonzero return.
1518+
1519+
If called in array context, returns a tuple of (retval, stdout, stderr, timeout).
1520+
timeout is the IPC::Run::Timeout object whose is_expired method can be tested
1521+
to check for timeout. retval is undef on timeout.
1522+
1523+
=cut
1524+
1525+
sub pg_recvlogical_upto
1526+
{
1527+
my ($self, $dbname, $slot_name, $endpos, $timeout_secs, %plugin_options) = @_;
1528+
my ($stdout, $stderr);
1529+
1530+
my $timeout_exception = 'pg_recvlogical timed out';
1531+
1532+
die 'slot name must be specified' unless defined($slot_name);
1533+
die 'endpos must be specified' unless defined($endpos);
1534+
1535+
my @cmd = ('pg_recvlogical', '-S', $slot_name, '--dbname', $self->connstr($dbname));
1536+
push @cmd, '--endpos', $endpos;
1537+
push @cmd, '-f', '-', '--no-loop', '--start';
1538+
1539+
while (my ($k, $v) = each %plugin_options)
1540+
{
1541+
die "= is not permitted to appear in replication option name" if ($k =~ qr/=/);
1542+
push @cmd, "-o", "$k=$v";
1543+
}
1544+
1545+
my $timeout;
1546+
$timeout = IPC::Run::timeout($timeout_secs, exception => $timeout_exception ) if $timeout_secs;
1547+
my $ret = 0;
1548+
1549+
do {
1550+
local $@;
1551+
eval {
1552+
IPC::Run::run(\@cmd, ">", \$stdout, "2>", \$stderr, $timeout);
1553+
$ret = $?;
1554+
};
1555+
my $exc_save = $@;
1556+
if ($exc_save)
1557+
{
1558+
# IPC::Run::run threw an exception. re-throw unless it's a
1559+
# timeout, which we'll handle by testing is_expired
1560+
die $exc_save
1561+
if (blessed($exc_save) || $exc_save !~ qr/$timeout_exception/);
1562+
1563+
$ret = undef;
1564+
1565+
die "Got timeout exception '$exc_save' but timer not expired?!"
1566+
unless $timeout->is_expired;
1567+
1568+
die "$exc_save waiting for endpos $endpos with stdout '$stdout', stderr '$stderr'"
1569+
unless wantarray;
1570+
}
1571+
};
1572+
1573+
if (wantarray)
1574+
{
1575+
return ($ret, $stdout, $stderr, $timeout);
1576+
}
1577+
else
1578+
{
1579+
die "pg_recvlogical exited with code '$ret', stdout '$stdout' and stderr '$stderr'" if $ret;
1580+
return $stdout;
1581+
}
1582+
}
1583+
1584+
=pod
1585+
15081586
=back
15091587
15101588
=cut

src/test/recovery/t/006_logical_decoding.pl

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
# Testing of logical decoding using SQL interface and/or pg_recvlogical
2+
#
3+
# Most logical decoding tests are in contrib/test_decoding. This module
4+
# is for work that doesn't fit well there, like where server restarts
5+
# are required.
26
use strict;
37
use warnings;
48
use PostgresNode;
59
use TestLib;
6-
use Test::More tests => 2;
10+
use Test::More tests => 5;
711

812
# Initialize master node
913
my $node_master = get_new_node('master');
@@ -35,5 +39,30 @@
3539
chomp($result);
3640
is($result, '', 'Decoding after fast restart repeats no rows');
3741

42+
# Insert some rows and verify that we get the same results from pg_recvlogical
43+
# and the SQL interface.
44+
$node_master->safe_psql('postgres', qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(1,4) s;]);
45+
46+
my $expected = q{BEGIN
47+
table public.decoding_test: INSERT: x[integer]:1 y[text]:'1'
48+
table public.decoding_test: INSERT: x[integer]:2 y[text]:'2'
49+
table public.decoding_test: INSERT: x[integer]:3 y[text]:'3'
50+
table public.decoding_test: INSERT: x[integer]:4 y[text]:'4'
51+
COMMIT};
52+
53+
my $stdout_sql = $node_master->safe_psql('postgres', qq[SELECT data FROM pg_logical_slot_peek_changes('test_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');]);
54+
is($stdout_sql, $expected, 'got expected output from SQL decoding session');
55+
56+
my $endpos = $node_master->safe_psql('postgres', "SELECT location FROM pg_logical_slot_peek_changes('test_slot', NULL, NULL) ORDER BY location DESC LIMIT 1;");
57+
diag "waiting to replay $endpos";
58+
59+
my $stdout_recv = $node_master->pg_recvlogical_upto('postgres', 'test_slot', $endpos, 10, 'include-xids' => '0', 'skip-empty-xacts' => '1');
60+
chomp($stdout_recv);
61+
is($stdout_recv, $expected, 'got same expected output from pg_recvlogical decoding session');
62+
63+
$stdout_recv = $node_master->pg_recvlogical_upto('postgres', 'test_slot', $endpos, 10, 'include-xids' => '0', 'skip-empty-xacts' => '1');
64+
chomp($stdout_recv);
65+
is($stdout_recv, '', 'pg_recvlogical acknowledged changes, nothing pending on slot');
66+
3867
# done with the node
3968
$node_master->stop;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy