Получите немедленное уведомление о 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 в другую переменную.

Другие вопросы по тегам