Is there any way with DBD::Pg to do a blocking wait for a NOTIFY that will immediately return when a message is ready?
I have a simple test script that can send messages using the Postgres 'NOTIFY' mechanism:
#!/usr/bin/perl
use 5.018;
use strict;
use warnings;
use autodie;
use DBI qw();
$| = 1; # Flush buffer on print
my $dsn = 'dbi:Pg:dbname=test';
my $attr = {
AutoCommit => 0,
RaiseError => 1,
PrintError => 0,
};
my $topic = 'test_topic';
my $dbh = DBI->connect($dsn, '', '', $attr);
while (1) {
print "payload: ";
chomp(my $payload = <>);
$dbh->do("NOTIFY $topic, '$payload'");
$dbh->commit;
}
I also have a simple receiver script that uses LISTEN to subscribe to the messages:
#!/usr/bin/perl
use 5.018;
use strict;
use warnings;
use autodie;
use DBI qw();
$| = 1; # Flush buffer on print
my $dsn = 'dbi:Pg:dbname=test';
my $attr = {
AutoCommit => 0,
RaiseError => 1,
PrintError => 0,
};
my $topic = 'test_topic';
my $dbh = DBI->connect($dsn, '', '', $attr);
$dbh->do("LISTEN $topic");
while (1) {
$dbh->commit();
while(my $notify = $dbh->pg_notifies) {
my($topic, $pid, $payload) = @$notify;
say "Got message: $topic => $payload";
}
sleep(10);
}
The problem is that $dbh->pg_notifies
does not block, so if there are no notifications queued, it immediately returns undef
. I've put the sleep(10)
so that it's not a busy loop, but of course this means I get a delay of up to 10 seconds after a NOTIFY message is sent but before my LISTEN receives it.
Some searching suggested that at the libpq
level, you could do a select
on the socket to be notified immediately of an incoming NOTIFY, so I tried this:
my $sock_fd = $dbh->{pg_socket};
my $read_set = '';
vec($read_set, $sock_fd, 1) = 1;
while (1) {
$dbh->commit();
while(my $notify = $dbh->pg_notifies) {
my($topic, $pid, $payload) = @$notify;
say "Got message: $topic => $payload";
}
select($read_set, undef, undef, 10);
}
But it doesn't seem to work and the select
only seems to return when my 10 second timeout expires.
It always seemed to me that NOTIFY/LISTEN provided a way to avoid polling loops, but I don't seem to be able to make it work without a polling loop. Suggestions?