PostgreSQL
LISTEN/NOTIFY и logical decoding
2018 год. LinkedIn синхронизирует PostgreSQL с Elasticsearch для поиска по профилям. Polling каждые 10 секунд: 50ms latency, 30% load на СУБД, DELETE не виден. После CDC через Debezium + Kafka: 50ms → 500ms latency, нагрузка на БД ~0, полный захват DELETE. Те же 500K событий/сек - но БД не знает об этом. LISTEN/NOTIFY и logical decoding превращают PostgreSQL в источник событий для всей системы.
- **LinkedIn** - Debezium + logical replication slots для синхронизации PostgreSQL -> Kafka -> Elasticsearch: поиск по 900M профилям с latency под 500ms
- **Notion** - LISTEN/NOTIFY для real-time коллаборации: изменение одного пользователя через NOTIFY немедленно доставляется другим подключённым клиентам без polling
- **Zalando** - WAL-G + logical decoding для zero-downtime миграций между major версиями PostgreSQL: новый сервер реплицируется логически, трафик переключается без downtime
LISTEN/NOTIFY: встроенный Pub/Sub
**LISTEN/NOTIFY - встроенный механизм pub/sub в PostgreSQL.** Без Redis, без Kafka, без RabbitMQ. Одно соединение подписывается на канал (`LISTEN events`), другое публикует (`NOTIFY events, 'payload'`). PostgreSQL доставляет сообщение асинхронно всем подписчикам канала. Payload - строка до 8000 байт, обычно JSON.
**NOTIFY в транзакции доставляется только при COMMIT.** Это ключевое преимущество перед внешними message queues: нет проблемы гонки, когда INSERT зафиксирован, а сообщение в очередь ещё не отправлено. Атомарность гарантирована самой транзакционной моделью PostgreSQL.
**NOTIFY не персистентный.** Если подписчик не подключён - сообщение потеряно. Если подключён, но медленно обрабатывает - буфер сообщений ограничен. Для надёжной доставки нужна outbox pattern поверх NOTIFY или logical decoding.
Транзакция делает INSERT и NOTIFY, затем ROLLBACK. Что получат подписчики канала?
Logical Decoding: WAL как поток изменений
**Logical decoding - декодирование WAL в поток логических изменений.** WAL содержит физические изменения страниц. Logical decoding переводит их в понятный формат: INSERT/UPDATE/DELETE с реальными значениями строк. Это основа для Change Data Capture (CDC), репликации в разнородные системы, аудита на уровне строк.
**Replication slot удерживает WAL** пока потребитель не подтвердил получение. Если потребитель упал и слот не удалён - WAL накапливается бесконечно, диск заполнится. В production обязателен мониторинг `pg_replication_slots` и алерт на `pg_wal_lsn_diff`.
Приложение создало logical replication slot и начало читать изменения. Затем упало на 3 дня. Что происходит с WAL на сервере?
Replication Slots: гарантированная доставка
**Replication slot - механизм гарантированной доставки WAL потребителю.** Слот хранит позицию (LSN), до которой потребитель подтвердил получение. PostgreSQL не удаляет WAL-сегменты старше этой позиции. Гарантия: потребитель может отстать на любое время и не пропустит изменения - ценой накопления WAL на диске.
| Тип слота | Использование | Формат данных |
|---|---|---|
| Physical | Streaming replication (standby) | Бинарные WAL-записи |
| Logical | CDC, logical replication, Debezium | Декодированные INSERT/UPDATE/DELETE |
**Debezium** (Red Hat) - популярный CDC-коннектор для Kafka, использует logical replication slots. LinkedIn использует Debezium + Kafka для синхронизации PostgreSQL с Elasticsearch - 500K событий/сек без нагрузки на OLTP запросы.
Команда `SELECT pg_drop_replication_slot('my_slot')` выполнена пока слот активен. Что произойдёт?
pg_recvlogical: CLI для logical decoding
**pg_recvlogical** - утилита командной строки для получения logical replication stream. Создаёт слот, читает изменения, выводит в stdout или файл. Полезна для отладки, скриптов миграции, прототипирования CDC без Debezium.
**Output plugins** определяют формат вывода: `pgoutput` (встроенный, бинарный, для logical replication), `wal2json` (JSON, удобен для дебага), `decoder_bufs` (Protobuf, для Bottledwater/Kafka). Для продакшн CDC через Kafka обычно используют Debezium, который сам управляет слотами.
Для чего нужен output plugin при использовании logical decoding?
CDC Pattern: Change Data Capture
**Change Data Capture (CDC) - архитектурный паттерн получения потока изменений из БД.** Вместо опроса таблицы (`SELECT WHERE updated_at > ?`) - подписка на WAL. Преимущества: минимальная нагрузка на OLTP, захват DELETE (polling не видит удалённые строки), точный порядок изменений, гарантированная доставка через replication slot.
**REPLICA IDENTITY FULL** записывает все столбцы в before-image при UPDATE/DELETE. Нужен Debezium для отслеживания изменений. Увеличивает размер WAL - используйте только для таблиц без больших text/jsonb колонок.
LISTEN/NOTIFY и CDC через logical decoding - одно и то же, просто разные API
LISTEN/NOTIFY - легковесный pub/sub для уведомлений, без гарантий доставки, максимум 8000 байт. Logical decoding - полный поток изменений WAL с гарантированной доставкой через replication slots
NOTIFY теряет сообщения если подписчик не подключён. Logical slot хранит все изменения пока потребитель не подтвердит. NOTIFY не захватывает удалённые строки с данными, не гарантирует порядок при высокой нагрузке. Для CDC (синхронизация Elasticsearch, инвалидация кеша) - только logical decoding
Команда DELETE в PostgreSQL удалила строку. CDC через logical decoding получит это изменение. При REPLICA IDENTITY DEFAULT (только PK) что будет в before-image?
Итоги
- **LISTEN/NOTIFY** - встроенный pub/sub, NOTIFY в транзакции доставляется только при COMMIT. Не персистентный: потерянные сообщения не восстанавливаются
- **Logical decoding** превращает бинарный WAL в поток INSERT/UPDATE/DELETE. Требует `wal_level = logical`
- **Replication slot** гарантирует доставку: PostgreSQL хранит WAL пока потребитель не подтвердит. Неактивный slot = бомба замедленного действия для диска
- **CDC (Debezium + Kafka)** - промышленный паттерн: захват всех изменений с нулевой нагрузкой на OLTP, включая DELETE с before-image через REPLICA IDENTITY FULL
- Мониторинг `pg_replication_slots` обязателен: lag > 1GB сигнализирует о проблеме с потребителем
Связанные темы
LISTEN/NOTIFY и CDC строятся поверх нескольких ключевых механизмов PostgreSQL:
- WAL: Write-Ahead Log — Logical decoding читает и декодирует WAL - основа работы replication slots и CDC
- Logical Replication — PUBLICATION/SUBSCRIPTION использует те же logical replication slots, но для репликации между PostgreSQL серверами
- Streaming Replication — Physical replication slots работают аналогично logical - гарантируют хранение WAL до подтверждения standby
Вопросы для размышления
- Система использует LISTEN/NOTIFY для уведомления кеша об изменениях. При высокой нагрузке кеш-сервер падает на 10 минут. Что происходит с уведомлениями? Как CDC через logical decoding решает эту проблему?
- DELETE в таблице `payments` с REPLICA IDENTITY DEFAULT. CDC-потребитель получил событие. Какой информации достаточно для инвалидации кеша? Когда нужен REPLICA IDENTITY FULL?
- Команда решила использовать logical replication slot для репликации в analytics DB. Через месяц analytics-сервис упал на неделю. Какую катастрофу это создало для production PostgreSQL и как её предотвратить?