Уведомления Postgres не приходят автоматически

Я открываю psql (PostgreSQL 9.5.4 на Arch Linux) в одном терминале A и выдаю LISTEN "notif";

Затем в другом терминале B я запускаю следующий скрипт (psql -f myscript.sql).

Это создает таблицу с двумя триггерами. Один триггер срабатывает, если добавляется строка, где send имеет значение ИСТИНА, а другой срабатывает, если строка обновляется таким образом, что send переходит из состояния ЛОЖЬ в значение ИСТИНА. Оба триггера отправляют уведомление.

DROP TRIGGER IF EXISTS do_notif ON notif;
DROP TRIGGER IF EXISTS do_notif2 ON notif;
DROP TABLE IF EXISTS notif;

CREATE TABLE notif (id INT PRIMARY KEY, send BOOLEAN, msg TEXT);

CREATE OR REPLACE FUNCTION post() RETURNS TRIGGER AS $$
BEGIN
    PERFORM pg_notify('notif', '+' || NEW.id::TEXT || ',' || coalesce(NEW.msg, '(null)'));
    RETURN NEW;
END $$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION post2() RETURNS TRIGGER AS $$
BEGIN
    PERFORM pg_notify('notif', '~' || NEW.id::TEXT || ',' || coalesce(NEW.msg, '(null)'));
    RETURN NEW;
END $$ LANGUAGE plpgsql;

CREATE TRIGGER do_notif AFTER INSERT ON notif
    FOR EACH ROW WHEN (NEW.send)
    EXECUTE PROCEDURE post();
CREATE TRIGGER do_notif2 AFTER UPDATE OF send ON notif
    FOR EACH ROW WHEN (NEW.send AND NOT OLD.send)
    EXECUTE PROCEDURE post2();

-- LISTEN "notif";

INSERT INTO notif VALUES (1, FALSE, 'update');
INSERT INTO notif VALUES (2, TRUE, 'insert');
UPDATE notif SET send = TRUE;
UPDATE notif SET send = FALSE;
UPDATE notif SET send = TRUE;

START TRANSACTION;
INSERT INTO notif VALUES (10, FALSE, 'a'), (11, TRUE, 'b'), (12, TRUE, 'c');
UPDATE notif SET send = TRUE WHERE id = 10;
COMMIT

Я ожидаю, что соответствующие запросы INSERT и UPDATE должны привести к вызову триггеров, которые отправляют уведомления, которые будут получены в терминале A.

Этого не происходит. Мне приходится снова вручную запускать LISTEN "notif"; в терминале A, который мгновенно выдает мне недостающие уведомления.

Если я раскомментирую LISTEN "notif"; в этом скрипте, то экземпляр psql, выполняющий скрипт (B), будет писать уведомления на терминал в соответствующих местах (после нетранзакционной вставки/обновления, которая устанавливает send в TRUE, и после транзакции тоже).

Но A по-прежнему не показывает их, пока я снова не запущу LISTEN "notif"; в A (или любой другой запрос, например SELECT TRUE;). Это не какая-то проблема буферизации терминала, так как простое нажатие ENTER в A не приводит к появлению уведомлений.

PostgreSQL, похоже, не сразу доставляет уведомления ни по разным соединениям, ни по разным процессам, но доставляет немедленно, когда слушателем является тот же процесс, который сгенерировал уведомления.


person Mark K Cowan    schedule 07.10.2016    source источник


Ответы (1)


psql проверяет очередь уведомлений после каждой команды (точнее, после получения каких-либо результатов от сервера). Таким образом, вы можете использовать listen channel один раз, и каждая последующая команда будет проверять, есть ли новые уведомления.

Программы на основе стандартной библиотеки libpq.c работают аналогичным образом, проверяя очередь уведомлений с помощью функцию PQnotifies после получения данных с сервера. У них есть и другая возможность. При отсутствии связи с сервером в определенное время они могут обновить очередь уведомлений с помощью функции PQconsumeInput. Таким образом, они получают уведомления асинхронно. Поддержка уведомлений драйверами, конечно, может быть реализована на более высоком уровне.

person klin    schedule 08.10.2016
comment
Я написал этот тест из-за того, что столкнулся с похожей проблемой при использовании https://github.com/brianc/node-postgres в Node.js — странно, что неделю назад все работало. Предполагая, что эта библиотека не предоставляет мне способ вызова PQconsumeInput из Node, будет ли мне практично просто выдавать SELECT TRUE; каждые N миллисекунд для достижения того же результата? - person Mark K Cowan; 08.10.2016
comment
Это было обычным способом до того, как PQconsumeInput был добавлен в библиотеку. Тоже должно работать. - person klin; 08.10.2016
comment
Спасибо, записал на завтра утром (здесь уже поздно) - person Mark K Cowan; 08.10.2016