Получите немедленное уведомление о Postgres УВЕДОМЛЕНИЕ
Есть ли способ с DBD::Pg сделать блокировку ожидания для NOTIFY, которая немедленно вернется, когда сообщение будет готово?
У меня есть простой тестовый скрипт, который может отправлять сообщения, используя механизм Postgres NOTIFY:
#!/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;
}
У меня также есть простой скрипт-получатель, который использует LISTEN для подписки на сообщения:
#!/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);
}
Проблема в том, что $dbh->pg_notifies
не блокируется, поэтому если в очереди нет уведомлений, сразу возвращается undef
, Я положил sleep(10)
так что это не занятой цикл, но, конечно, это означает, что я получаю задержку до 10 секунд после отправки сообщения NOTIFY, но до того, как мой LISTEN его получит.
Некоторые поиски предположили, что на libpq
уровень, вы могли бы сделать select
на сокете, чтобы быть немедленно уведомленным о входящем NOTIFY, поэтому я попробовал это:
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);
}
Но это не похоже на работу и select
кажется, возвращается только когда истекает мой 10-секундный тайм-аут.
Мне всегда казалось, что NOTIFY/LISTEN предоставляет способ избежать циклов опроса, но я не могу заставить его работать без цикла опроса. Предложения?
1 ответ
Проблема в том, что $read_set портится, вероятно, после самого первого вызова select(). Вам следует заменить его на что-то вроде:
select(my $read_out = $read_set, undef, undef, 10);
После первого вызова вы, вероятно, еще не получите уведомление, поэтому вы получите пустой набор fd, а в следующий раз, когда вы вызовете select с пустым набором, поэтому вам нужно скопировать $read_set в другую переменную.