Postgres Trigger + n8n: как запускать сценарии по изменению данных почти в реальном времени - Блог Папы Карло

Опубликовано: 4 месяца назад
Просмотров: 84

Привет. Я тут у себя в мастерской снова ковыряю автоматизацию, и вот мой честный ответ на запрос сразу с порога: если тебе надо запускать workflow не по расписанию, а почти сразу после изменения записи в базе, связка Postgres Trigger + n8n реально решает. Работает это через события insert/update/delete или через канал LISTEN/NOTIFY, где база сама пинает сценарий после коммита.

Ниже покажу, какой вариант брать в обычной работе, когда хватает встроенного Postgres Trigger node, когда лучше писать свой pg_notify, как не утопить workflow в дублях и почему я почти всегда тяну в сценарий не всю строку целиком, а только id, тип события и пару служебных полей.

Ещё будет живой SQL-пример, сравнение двух подходов и мой короткий чек-лист перед публикацией. Так что если ты искал, как сделать запуск n8n при изменении таблицы Postgres, ты зашёл куда надо.

Содержание:

Как это вообще работает

Смотри. В n8n postgres trigger можно собрать двумя путями. Первый — быстрый: берёшь Postgres Trigger node, выбираешь режим прослушки insert, update или delete, цепляешь дальше IF, Set, Telegram, таблицу, CRM — что тебе нужно. Второй — более взрослый и гибкий: база сама шлёт уведомление в канал через postgres listen notify, а n8n слушает именно этот канал и получает полезную нагрузку с нужными полями.

Мне второй путь нравится чаще. Почему? Потому что бизнес-логика редко живёт в духе «любой update по таблице = запускай всё подряд». Обычно хочется тоньше: например, дёргать workflow только когда статус заказа сменился на ready, когда лид получил score выше порога, когда у счёта появилась просрочка или когда в таблице задач выставили флаг needs_review.

Вот за это я и люблю postgresql trigger для n8n: база не просто хранит данные, а становится нормальным источником событий. И не надо каждые 30 секунд гонять SELECT по updated_at и надеяться, что ты не пропустил апдейт между окнами.

Суть простая: запись меняется, триггер отрабатывает, после коммита прилетает notify, n8n ловит событие и запускает сценарий почти в реальном времени. Не мгновенно в физическом смысле, но по ощущениям очень близко, особенно если сравнивать с cron-опросом раз в минуту.

Ноутбук со схемой workflow и базой данных на рабочем верстаке

Как собрать схему: trigger, канал и workflow

Я обычно собираю это так: таблица orders или leads, в ней есть id, status, updated_at и нужные поля. Дальше в Postgres создаю функцию, которая на insert или update шлёт в канал компактный JSON. А уже в n8n поднимаю Postgres Trigger node в режиме Listen to Channel и разбираю payload как нормальный вход.

Шаг 1. Делаем функцию уведомления в Postgres

Вот рабочий скелет, который можно спокойно адаптировать под свою таблицу:

CREATE OR REPLACE FUNCTION notify_order_event()
RETURNS trigger AS $$
DECLARE
  payload text;
BEGIN
  payload := json_build_object(
    'event', TG_OP,
    'table', TG_TABLE_NAME,
    'id', NEW.id,
    'status', NEW.status,
    'updated_at', NEW.updated_at
  )::text;

  PERFORM pg_notify('orders_events', payload);
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

А теперь вешаем trigger:

DROP TRIGGER IF EXISTS orders_notify_trigger ON orders;

CREATE TRIGGER orders_notify_trigger
AFTER INSERT OR UPDATE ON orders
FOR EACH ROW
EXECUTE FUNCTION notify_order_event();

Если тебе нужен delete, делай отдельную ветку через OLD.id и OLD.status, потому что на удалении NEW уже нет. И да, я бы не тащил в payload весь объект целиком, если строка жирная. Для workflow по изменению строки обычно хватает id, типа события и одного-двух признаков, по которым ты дальше решаешь, что делать.

Шаг 2. Поднимаем узел в n8n

В n8n добавляешь Postgres Trigger node, подключаешь те же креды к базе и выбираешь режим прослушки канала. Имя канала — то же самое, что в pg_notify, у нас это orders_events. Дальше после триггера ставишь Code, Edit Fields или Set, приводишь вход в аккуратный JSON и поехали по веткам.

Самая удобная схема у меня обычно такая:

  • Postgres Trigger ловит payload.

  • IF проверяет тип события и статус.

  • Postgres дочитывает полную строку по id, если нужны все поля.

  • Дальше уже уходит уведомление, синхронизация, расчёт, запись в другую систему или постановка задачи.

Почему я люблю делать второй запрос по id, а не передавать всё сразу? Потому что payload у notify маленький, а логика потом меняется. Сегодня тебе нужен email, завтра добавится сумма, послезавтра менеджер и тег сегмента. Когда workflow сам дочитывает актуальную строку, жить проще.

Шаг 3. Фильтруем события ещё в базе

Вот тут самый сок. Если тебе надо запускать сценарий только на важное изменение, фильтруй это прямо в trigger function. Например, можно уведомлять только когда статус реально поменялся:

IF TG_OP = 'UPDATE' AND NEW.status IS DISTINCT FROM OLD.status THEN
  PERFORM pg_notify(
    'orders_events',
    json_build_object(
      'event', TG_OP,
      'id', NEW.id,
      'old_status', OLD.status,
      'new_status', NEW.status
    )::text
  );
END IF;

Так ты не будишь n8n на каждое шевеление updated_at или техническое обновление. Для автоматизации по изменению данных это прям золото: меньше лишних запусков, меньше шума в логах, меньше возни с дедупликацией.

Когда хватает встроенного узла, а когда нужен pg_notify

Тут я бы выбирал не по красоте, а по реальной задаче.

Подход Когда брать Что нравится Где ограничение
Listen and Create Trigger Rule Нужно быстро ловить insert/update/delete Меньше SQL, старт за пару минут Меньше контроля над тем, что именно прилетает в workflow
Listen to Channel + pg_notify Нужна точная фильтрация и свой payload Можно слать только важные события и свои поля Надо один раз нормально написать trigger function

Если кейс простой, типа «новая заявка в таблице — отправь менеджеру сигнал», встроенного режима хватает с головой. Но если у тебя история вида «запуск workflow по изменению строки только при переходе из pending в approved, а потом ещё раз при closed», я бы сразу шёл в канал и pg_notify. Так меньше сюрпризов.

Ещё момент: Postgres умеет склеивать одинаковые уведомления внутри одной транзакции. Если в рамках одного коммита на один и тот же канал ушло несколько одинаковых payload, слушатель получит одно событие. Для части задач это даже плюс, но если тебе критично считать каждое движение поштучно, payload должен чем-то отличаться или сама логика должна опираться на дочитку состояния из таблицы.

И не забывай про размер полезной нагрузки. В стандартной конфигурации payload у NOTIFY должен быть короче 8000 байт. Так что мысль «сейчас я засуну сюда весь NEW в формате JSON на двадцать полей с комментариями» обычно заканчивается кривой архитектурой. Лучше отправить компактный маркер и потом дочитать данные отдельным узлом.

Грабли и тонкие места

Тут уже не теория, а то, на чём я сам ловил нервный тик.

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

Это часто ломает ожидание новичка. Ты обновил строку в транзакции, а n8n молчит. Не потому что всё умерло, а потому что notify отправляется после commit. Если транзакция откатилась, события тоже не будет. И это, кстати, хорошо: workflow не стартует на то, чего по факту в базе не осталось.

Слушатель не должен жить в длинной транзакции

Если клиент, который слушает канал, держит долгую транзакцию, доставка уведомлений к нему может откладываться. Плюс в Postgres есть очередь уведомлений, и её состояние можно смотреть через pg_notification_queue_usage(). На обычных сценариях туда редко упираются, но если база шумная, а событий много, я бы это держал в голове.

Нужен стартовый снимок, а не только события

У listen/notify есть одна житейская особенность: сначала клиент подписывается, потом уже безопасно полагается на новые события. Поэтому для серьёзных сценариев я люблю схему из двух шагов: сначала workflow или отдельная процедура делает начальную сверку по таблице, потом уже живёт на событиях. Это спасает от редких, но неприятных дыр на старте.

Не тащи бизнес-логику целиком в SQL

Сигнализировать из базы — норм. Пытаться засунуть туда половину оркестрации — уже сомнительно. Триггер должен быть коротким и понятным: проверить условие, собрать payload, вызвать pg_notify. Всё тяжёлое пусть делает n8n, потому что там проще ветвить, логировать, повторять и менять поведение не лезя каждый раз править SQL-лес.

На self-hosted n8n следи за режимом исполнения

Если у тебя инстанс крутится с очередями и workers, помни простую вещь: триггеры принимает основной процесс, а выполнение уходит воркерам. Само по себе это ок, но я бы всё равно проверил стабильность соединения с базой, публикацию workflow и логи после перезапусков. Особенно если сценарий у тебя реально важный и завязан на уведомлениях из Postgres.

Что ещё докрутить в рабочем сценарии

Когда базовая связка завелась, я почти всегда докручиваю ещё три вещи.

  • Защиту от дублей. Даже если архитектура аккуратная, полезно иметь idempotency-ключ или таблицу обработанных событий. По соседней теме у меня уже лежит разбор про защиту вебхуков и борьбу с дублями — логика там очень похожая.

  • Нормальную обработку сбоев. Если база шлёт сигнал, а внешний сервис в этот момент лёг, надо не молча падать, а повторять, логировать и уведомлять. Для этого пригодится мой материал про обработку ошибок, retries и отдельный error flow.

  • Тестовый стенд. Перед публикацией я люблю прогонять payload руками и проверять ветки на mock-данных. Тут очень выручает статья про pinned data и сценарии для теста.

А если ты только входишь в эту тему и хочешь сначала набить руку на понятных сценариях, загляни в мой материал про простые n8n-проекты для старта. Когда руками собираешь пару живых цепочек, событийная схема из Postgres потом ощущается уже не как тёмный лес, а как нормальный следующий шаг.

Из готовых идей дальше хорошо заходят уведомления менеджеру о новых оплатах, синхронизация заказов в таблицы, запуск AI-разбора комментария после смены статуса и разные штуки в духе «новая запись в базе — сразу пуш в мессенджер». Если нужен стартовый набор идей, у меня есть ещё подборка шаблонов n8n.

Чек-лист перед запуском

Вот мой быстрый чек-лист, который я гоняю перед тем, как отправить такую схему в работу:

  • Триггер шлёт только важные события, а не любую мелкую правку.

  • Payload компактный: id, тип события, ключевой статус, метка времени.

  • Workflow умеет дочитывать строку по id, если дальше нужны все поля.

  • Есть защита от повторной обработки, если событие может долететь повторно через соседние ветки логики.

  • Есть отдельная ветка на ошибку, а не молчаливый обрыв цепочки.

  • Проверен сценарий после реального commit, а не только на ручном клике в редакторе.

  • Понятно, что будет при delete, если запись исчезнет до дочитки.

Я бы подвёл так: Postgres Trigger + n8n — это очень бодрая связка, когда нужно реагировать на изменения данных почти сразу, но не хочется городить отдельную очередь ради пары рабочих сценариев. Для простых кейсов бери встроенный режим прослушки событий. Для более точной работы — канал и pg_notify с коротким JSON. А дальше уже строй нормальный pipeline: фильтрация, дочитка строки, ветвление, retry, логирование.

Короче, база у тебя и так уже есть. Так почему бы не заставить её не только хранить строки, но и честно подкидывать события в автоматизацию, когда в таблицах начинается движ?

Метки: , , , , , , , ,