Представь: отправляешь SIGTERM Postgres для плавного завершения работы, а он… игнорирует. Как в страшилке! 😱 В реальном инциденте разработчики столкнулись с мертвым сервером после сигнала — база просто не завершилась. Расследование показало, что проблема в обработке сигналов и состоянии shared memory. 🤯 Похоже, PostgreSQL иногда «застревает» в ожидании фоновых процессов. Решение? Пока хардкор: мониторить виснущие экземпляры и слать SIGKILL как крайнюю меру. 💥 Запускаете базы? Готовьтесь к сюрпризам и держите мануалы под рукой! 🛠️ #Postgres #DevOps #АдминскаяБоль
Введение: Крайний случай непокорного процесса
Наихудшие баги — те, которые решаешь оставить без внимания. Они проявляются в 2% случаев или на специфичном оборудовании, а недостаток информации или времени заставляет отложить расследование. Но когда проблема возвращается месяцами позже, сожалеешь об упущенной возможности. История команды ClickPipes стала таким кейсом: создание логического слота репликации на standby-реплике PostgreSQL превращалось в «неубиваемый» процесс, грозящий переполнением WAL и простоем БД. В этом техническом разборе я детально восстановлю путь диагностики, анализ кода PostgreSQL и патч, принятый сообществом.
Технический бэкграунд: Logical Replication и опасности WAL
Архитектура логической репликации
-
Слоты репликации: Механизм преобразования Write-Ahead Log (WAL) в поток событий через декодирование (используя pgoutput или test_decoding)
-
Процесс создания слота:
-
Критическая фаза: Ожидание завершения старых транзакций для достижения согласованной точки (consistent point). Без этого невозможна гарантия целостности данных.
Риски для standby-реплик:
- Накопление неочищаемого WAL → исчерпание дискового пространства
- Блокировка ресурсов → деградация производительности
- Невозможность аварийного завершения процесса → принудительный перезапуск инстанса
Детализация инцидента: Симптомы и неудачные решения
Наблюдаемое поведение:
-
Запрос
pg_create_logical_replication_slotзависает на часах вместо секунд -
Отсутствие
wait_eventвpg_stat_activity: -
Бессилие сигналов:
SIGINT(pg_cancel_backend) иSIGTERM(pg_terminate_backend) возвращаютtrue, но процесс остаётся активным- Слот блокируется с активным PID, препятствуя удалению
Гипотезы и тупики:
- Первоначальное подозрение на конфликт в managed-сервисе
- Эмпирическое «лечение» через
hot_standby_feedback(оказалось плацебо)
Диагностика: От strace к исходному коду
Ключевое наблюдение:
Анализ системных вызовов через strace выявил цикл 1ms nanosleep:
Воспроизводимый сценарий:
- На primary запустить долгую транзакцию с DML-операцией
- На standby (v16+) выполнить создание слота → зависание
- Завершение транзакции на primary → слот успешно создаётся
Анализ функций PostgreSQL:
- XactLockTableWait(): Основная функция ожидания завершения транзакций
- Код цикла ожидания (упрощённо):
Корень проблемы: Архитектурная асимметрия primary/standby
Ключевые различия в обработке:
| Аспект | Primary | Hot Standby |
|---|---|---|
| Источник транзакций | Локальные процессы | WAL от primary |
| LockAcquire() | Блокирует до завершения | Возвращается мгновенно |
| Проверка статуса | TransactionIdIsInProgress | KnownAssignedXids (через WAL) |
Критический дефект:
- На standby
LockAcquire()не блокируется → цикл переходит вpg_usleep(1000L) - Отсутствие обработки прерываний в цикле сна → сигналы SIGTERM/SIGINT игнорируются
wait_eventне регистрируется → администратор лишён наблюдаемости
Разработка и внедрение патча
Суть исправления:
- Механика: Вставка точки проверки прерываний перед каждым nanosleep
- Эффект: Немедленная реакция на pg_cancel_backend/pg_terminate_backend
Процесс интеграции:
- Патч отправлен в pgsql-hackers
- Принят Fujii Masao с backport до v14
- Побочные улучшения:
- Новая WAIT_EVENT (в разработке для v19)
- Оптимизация цикла ожидания
Практические рекомендации и выводы
Стратегии предотвращения:
- Обновление PostgreSQL: Патч доступен с версий 14.12, 15.11, 16.1+
- Мониторинг:
- Алёрты на длительное создание слотов
- Анализ
pg_stat_activityна отсутствиеwait_eventпри DDL
- Проектирование:
- Избегать создания слотов на нагруженных репликах
- Использовать отдельные реплики для CDC
Инженерные уроки:
- Важность обработки прерываний в циклах ожидания
- Различия поведения primary/standby требуют явного тестирования
- Открытый код как спасение: возможность анализа и исправления
Заключение: Значение для экосистемы
Обнаруженная проблема подчёркивает хрупкость взаимодействия подсистем в распределённых СУБД. Принятие патча сообществом PostgreSQL демонстрирует эффективность open-source модели для критически важной инфраструктуры. Для инженеров это напоминание: даже «незначительные» баги в базовых механизмах могут парализовать систему, а их решение требует глубокого погружения в архитектуру.