Учебные переключения PostgreSQL при сбоях для небольших команд, которыми руководит основатель
Тренировки failover PostgreSQL помогают небольшим командам, которыми руководит основатель, проверить поднятие резервного узла, смену DNS и переподключение приложения до того, как сбой превратится в ночную суету.

Почему всё превращается в ночную панику
Проблема с базой данных редко надолго остаётся «просто проблемой с базой данных». Если ваш основной PostgreSQL-узел перестаёт принимать записи, регистрации могут зависнуть, повторные попытки списаний — не пройти, а служба поддержки может потерять возможность обновлять аккаунты, начислять кредиты или исправлять простые ошибки клиентов. Один сломанный путь записи может за минуты заморозить половину бизнеса.
Паника обычно начинается ещё до того, как кто-то дотронется до сервера. Небольшие команды часто знают в теории, что резервный узел может взять нагрузку на себя. Но это мало помогает в 1:17 ночи, когда сыпятся алерты и никто не тренировался на реальном переключении. Люди созваниваются и задают базовые вопросы, на которые должны были бы уже знать ответы: кто поднимает резервный узел, какое имя хоста должен использовать сервис, нужно ли перезапускать воркеры и кто сообщает остальной команде, что происходит.
Именно на это уходит время. Само восстановление обычно прямолинейное. Кто-то поднимает резервный узел. Кто-то обновляет DNS или настройку приложения, которая указывает записи на базу данных. Кто-то проверяет, переподключилось ли приложение и возобновились ли фоновые задачи. Всё, что окружает эти шаги, быстро превращается в хаос: поиск учётных данных, копание в старых заметках, сомнения, не держит ли кешированный DNS часть системы на старом мёртвом сервере, и споры о том, действительно ли медленный вход в систему связан с базой данных.
Небольшим командам это даётся ещё тяжелее, потому что те же несколько человек отвечают и за продукт, и за поддержку, и за инфраструктуру, и за общение с клиентами. Основатель может одновременно смотреть на неудачные платежи, отвечать на раздражённые сообщения и принимать технические решения. Такая смесь ведёт к плохим решениям. Люди пропускают проверки, забывают зафиксировать время или меняют DNS до того, как убедятся, что поднятый сервер действительно принимает записи.
Тренировки failover убирают этот хаос до управляемого уровня. Вы не устраиваете театральную постановку про аварийное восстановление. Вы проверяете те части, которые действительно важны под давлением: действия, время и передачу задач между людьми.
Даже если вы уже знаете последовательность и примерное время, следующий сбой всё равно будет неприятным. Просто он перестанет быть слепой паникой.
Как выглядит успешная тренировка
Полезная тренировка заканчивается одним ответом: сможет ли команда восстановить работу приложения достаточно быстро и с достаточной уверенностью, если основной PostgreSQL перестанет работать сегодня ночью? Если вы не можете ответить цифрой, значит, это была лишь репетиция.
Перед стартом поставьте одну цель и не распыляйтесь. Например: поднять резервный узел PostgreSQL, направить на него трафик и вернуть приложение пользователям за 10 минут.
Этот лимит и есть ваша цель восстановления. Выберите число, которое можно измерить по часам, а не расплывчатую формулировку вроде «как можно быстрее». Для небольшой SaaS-команды разумная первая цель — 10–15 минут. Если стек простой, в следующий раз можно ставить планку ниже.
Вам также нужны понятные роли. Один человек ведёт тренировку и решает, когда делать каждый шаг. Другой записывает хронологию: когда основной узел объявили недоступным, когда началось переключение резервного, когда изменился DNS, когда приложение переподключилось и когда пользовательские действия снова заработали.
Эта запись важна, потому что память быстро подводит. Письменная хронология показывает, где вы потеряли три минуты, где кто-то гадал, а где нужно доработать runbook.
Успех — это не «база данных поднялась». Это значит, что пользователи снова видят, что приложение работает. Перед началом договоритесь о нескольких проверках, которые это подтверждают:
- поднятый сервер принимает и чтение, и запись
- приложение переподключается без ручных правок на каждом машине
- вход в систему работает для обычного пользователя
- проходит одно типичное действие с записью, например создание записи или сохранение изменения
- ошибки и задержка возвращаются к обычному уровню
Если одна из этих проверок не прошла, тренировка всё равно принесла пользу. Она нашла слабое место, пока все были спокойны, бодры и могли всё исправить.
Сначала задайте правила
Тест failover разваливается, если люди относятся к нему как к обычному эксперименту. Нужны несколько правил ещё до того, как кто-то коснётся базы данных, иначе команда потратит время на споры о том, что считать нормальным поведением.
Выберите тихое окно. Избегайте дней запуска, расчётов по биллингу, маркетинговых рассылок и демонстраций клиентам. Сообщите всем участникам, что произойдёт, когда всё начнётся, кто будет запускать команды и кто будет только наблюдать. Если во время тренировки кто-то увидит всплеск ошибок, он уже должен знать, что это ожидаемо и куда об этом сообщить.
Заморозьте всё, что может исказить результат. Не выкатывайте изменения в приложении во время теста. Не запускайте миграции схемы. Не меняйте настройки пула подключений посередине, потому что «это, наверное, поможет». Если приложение сломается, вы хотите разбирать одну причину, а не четыре.
Перед стартом проверьте несколько базовых вещей. Нужна свежая резервная копия, и кто-то должен точно знать, где она хранится. Кто-то также должен проверить доступ к восстановлению, а не только создание копии. Откройте логи и метрики для базы данных, приложения и балансировщика или прокси. Держите открытой одну общую заметку, чтобы команда могла фиксировать время, команды, ошибки и решения в одном месте.
Эту проверку восстановления легко пропустить, а потом дорого сожалеть. Резервная копия, которую никто не может восстановить, — это всего лишь успокаивающая идея. Даже быстрая проверка учётных данных, доступа к хранилищу и шагов восстановления снижает стресс.
Откройте всё нужное до начала тренировки: логи базы данных, логи приложения, количество подключений, частоту ошибок и графики задержки. Назначьте одного человека, который будет записывать хронологию. В шумном десятиминутном инциденте память быстро мутнеет.
Простое правило помогает: если шаг не записан, значит, его не было.
Проводите переключение по порядку
Не начинайте переключение резервного PostgreSQL, пока не убедитесь, что реплика достаточно близка к старому основному узлу. Проверьте задержку репликации, последнюю применённую позицию WAL и нет ли ошибок восстановления на резервном сервере. Если задержка уже 20–30 секунд, тренировка расскажет вам меньше, чем кажется.
Если приложение ещё может писать данные во время переключения, сначала остановите именно это. Некоторые команды на две-три минуты включают режим обслуживания. Другие блокируют запись в приложении и оставляют только чтение. Подойдёт любой вариант. Важно лишь, чтобы он был последовательным. Если вы начнёте импровизировать посреди тренировки, вы не поймёте, какие записи дошли до нового основного узла.
Держите последовательность простой. Убедитесь, что резервный узел здоров. Приостановите запись, если ваша схема не защищает её во время failover. Поднимите резервный узел и зафиксируйте точное время вплоть до секунды. Подключитесь к поднятому узлу и проверьте одно чтение и одну запись. Затем оставьте старый основной узел офлайн или переведите его в режим только для чтения до конца теста.
Эта отметка времени потом поможет сопоставить логи базы данных с ошибками приложения. Она покажет, произошёл ли сбой до переключения, во время него или уже после подъёма нового основного узла.
Проверка должна быть простой. Сначала убедитесь, что новый узел больше не находится в режиме восстановления. Потом выполните обычный запрос на чтение. После этого запишите маленькую тестовую строку в временную таблицу и прочитайте её обратно. Если чтение работает, а запись — нет, у вас ещё нет нового основного узла.
Не возвращайте старый основной узел в строй только потому, что новый выглядит здоровым. Оставьте старый узел офлайн или заблокируйте его в режиме только для чтения до конца тренировки и до тех пор, пока не будет плана, как вернуть его в кластер. Так вы избежите самого неприятного сценария: два сервера принимают разные записи одновременно.
Рассматривайте DNS как часть failover
Часто failover на уровне базы проходит успешно, а потом всё ломается, потому что часть стека всё ещё указывает на старый хост. Этот разрыв превращает аккуратную тренировку в грязный инцидент.
Если можете, заранее уменьшите TTL. Сделайте это за несколько часов или накануне, чтобы кеши успели обновиться. Если обычный TTL равен часу, а вы снижаете его до 60 секунд за пять минут до переключения, многие клиенты всё равно будут хранить старый ответ.
Когда резервный узел становится основным, обновите запись на новый узел записи или на database proxy, который использует приложение. Для небольшой команды прокси часто проще, потому что приложение продолжает видеть один стабильный hostname, а трафик вы переключаете за ним. Если вы подключаетесь напрямую к хосту базы данных, проверьте, что во всех окружениях используется одно и то же имя DNS, а не жёстко прописанный IP, спрятанный в каком-нибудь воркере или cron-задаче.
Не проверяйте разрешение имени только со своего ноутбука и не считайте, что на этом всё. Сверяйте с нескольких сетей, потому что кеши отличаются. Проверьте со своего ноутбука, из второй сети — например через точку доступа телефона или VPN, как минимум с одного app server и с фонового воркера или job runner.
Это важно, потому что устаревший DNS чаще всего всплывает в самом неочевидном месте. Сайт может выглядеть здоровым в браузере, а воркер ещё десять минут будет писать на старый основной узел. Так и появляется расхождение: приложение выглядит нормально, а задания падают или данные тихо исчезают.
Следите за устаревшими записями на ноутбуках, серверах, в контейнерах и у управляемых воркеров. Некоторые системы кешируют дольше, чем вы ожидаете. Некоторые приложения разрешают имя хоста только один раз при запуске и не обновляют его, пока процесс не перезапустится.
Запишите три момента: когда изменился DNS, когда каждая сеть впервые увидела новый ответ и каким процессам потребовался перезапуск. Эти заметки ускорят следующую тренировку.
Следите, как приложение переподключается
База данных может уже вернуться, а приложение — ещё нет. Первое, за чем стоит смотреть, — это разрыв между переключением и моментом, когда приложение снова начинает вести себя нормально. Сразу откройте логи и ищите ошибки пула подключений, устаревшие сессии, «connection refused», «read-only transaction» и растущие повторы.
Первые несколько минут говорят очень много. Если приложение само восстановилось — отлично. Если кому-то пришлось вручную перезапускать web server, worker или consumer очереди, запишите это в runbook вместо того, чтобы делать вид, будто восстановление было автоматическим.
Используйте несколько проверок, видимых пользователю. Войдите в систему с существующим аккаунтом. Создайте новый аккаунт. Выполните одно действие по биллингу в тестовом режиме. Запустите одну фоновую задачу, например отправку письма или синхронизацию записи.
Не останавливайтесь на том, что web app выглядит нормально. Фоновые воркеры и запланированные задачи часто держат старые подключения дольше, чем основной сервис. Проверьте каждый worker process, cron job и queue consumer и убедитесь, что они открыли свежие сессии к новому основному узлу.
Таймауты и повторы могут сделать восстановление гораздо медленнее, чем оно есть на самом деле. Пул, который ждёт 60 секунд перед сдачей, может превратить короткое переключение базы в долгий простой. Проверьте время кеширования DNS, настройки обновления пула, connect timeout и retry backoff. Короткие ожидания обычно лучше, чем одна длинная пауза.
Закончите проверкой, которую команды чаще всего пропускают: убедитесь, что на старый узел больше никто не пишет. Заблокируйте там запись, наблюдайте за обоими узлами на предмет write activity и сделайте небольшое изменение, которое можно отследить в логах. Если воркер, административный скрипт или забытый сервис всё ещё обращается к старой базе, тренировка нашла настоящую проблему.
Простой пример из небольшой SaaS-команды
У одной небольшой SaaS-команды были веб-приложение, фоновой worker и один основной PostgreSQL-узел с одной резервной репликой. Продукт был простым, но слабое место было очевидно: если основной узел падал, двум уставшим людям нужно было быстро всё исправить.
Их тренировка началась с правдоподобного алерта. Основной узел перестал отвечать на health checks, запросы входа замедлились, а система отслеживания ошибок наполнилась ошибками подключения к базе данных. Основатель открыл runbook, пока единственный инженер на дежурстве проверял задержку репликации на резервном узле.
Они убедились, что резервный узел достаточно близок для переключения, выполнили promotion и проверили, что он принимает записи. Затем они изменили DNS-запись, которую приложение использовало для хоста базы данных. Веб-приложение восстановилось меньше чем за минуту. Новый вход в систему заработал, дашборд загрузился, и короткий тестовый заказ прошёл успешно.
Потом случился сюрприз.
Worker всё равно падал на каждой задаче. Он запустился несколько часов назад, один раз разрешил старый хост базы данных и держал этот адрес в памяти. DNS уже изменился, но worker это не волновало. Он продолжал стучаться в мёртвый основной узел и заполнил очередь повторными попытками.
Именно это изменило их взгляд на failover-тренировки. База данных восстановилась, но система — нет.
Они обновили runbook несколькими практичными улучшениями: перезапускать воркеры после failover, если они кешируют адрес базы данных; уменьшить TTL DNS, чтобы изменения распространялись быстрее; заставить приложение переподключаться после короткой серии ошибок; перед завершением тренировки проверять одну запись через веб-приложение и одну реальную задачу воркера; и записывать, сколько времени занял каждый шаг.
На следующей тренировке они подняли резервный узел, переключили DNS, перезапустили worker и заново проверили оба пути. Общее время восстановления сократилось примерно с 18 минут до 7.
Вот такой скучный результат и нужен. Когда придёт настоящий сбой, скучное побеждает.
Частые ошибки на первой тренировке
Большинство failover-тренировок идут не так по скучным причинам, а не из-за драматичных. Команды хотят увидеть, как резервный узел поднимается, приложение возвращается и на этом всё заканчивается. Обычно именно тут и начинаются проблемы.
Одна частая ошибка — начинать без точки остановки и плана отката. Заранее решите, что считается успехом, кто может остановить тренировку и что вы будете делать, если приложение начнёт вести себя странно. Внесите это в runbook до того, как кто-то коснётся основного узла. Если никто не владеет этим решением, команда начинает гадать.
Другая ошибка — проверять только основное приложение. Приложение важно, но оно редко единственное, что разговаривает с PostgreSQL. Воркеры, cron-задачи, скрипты импорта, админские инструменты и скрипты поддержки часто используют свои настройки подключения. Один забытый процесс может продолжать бить по старому серверу и создавать ложную картину происходящего.
Полезно также пройтись по обычным скрытым клиентам: queue workers, запланированные скрипты, backup jobs, панели администрирования, инструменты поддержки, файлы локального окружения и health checks, которые всё ещё обращаются к старому хосту.
DNS даёт много ложной уверенности. Вы обновляете одну запись, проверяете со своей машины и думаете, что теперь все системы видят новый основной узел. Реальные системы так аккуратно не работают. Один клиент может увидеть изменение через секунды, а другой ещё долго держать старый адрес и ломать задачи или путать команду.
Команды также пропускают единственный тест, который действительно доказывает, что failover сработал: реальную запись. Поднятие узла показывает лишь, что резервный сервер может стать основным. Это не доказывает, что ваше приложение может создавать, обновлять и читать данные через новый путь. Сделайте одно реальное изменение в приложении и убедитесь, что оно сохранилось на поднятом узле. Если продукт работает с фоновыми задачами, запустите одну из них и проверьте, что она тоже пишет туда.
Старые hostname чаще всего остаются самым упорным хвостом. Они прячутся в файлах окружения, настройках деплоя, shell-скриптах и backup jobs. Вы можете закончить тренировку с живым приложением и всё равно иметь один скрытый процесс, который разговаривает не с тем сервером.
Хорошая тренировка ощущается почти скучной. Каждый клиент переподключается, реальные записи проходят, и команда знает, когда нужно остановиться или откатиться. Если какой-то шаг всё ещё зависит от памяти, следующий сбой найдёт его первым.
Что проверить перед тем, как считать задачу выполненной
Тренировка не закончена, когда удалось поднять резервный узел. Она закончена, когда приложение ведёт себя так, как в обычный день. Команды часто останавливаются слишком рано и находят настоящую поломку в сессиях, воркерах или пути записи уже через десять минут.
Начните с нового основного узла. Не останавливайтесь на health check или SQL prompt. Приложение должно и читать из него, и писать в него. Создайте одну небольшую запись через приложение, отредактируйте её, а затем удалите. Это доказывает, что работает весь путь, а не только база данных отдельно.
Дальше идёт пользовательский трафик. Оставьте один тестовый аккаунт в системе до переключения, а потом используйте ту же сессию после переподключения. Если сессия сохранилась и пользователь всё ещё может сохранять данные, искать и открывать обычную страницу, вы избежали одного из самых частых сюрпризов при failover.
Используйте короткий чек-лист:
- убедитесь, что приложение читает свежие данные с нового основного узла
- убедитесь, что оно может писать новые данные и обновлять существующие строки
- оставьте одну пользовательскую сессию открытой во время переключения и выполните обычное действие
- проверьте, что хотя бы одна запланированная задача стартовала вовремя и завершилась без лавины повторов
- убедитесь, что ошибки и задержка вернулись к обычному уровню
Фоновые задачи заслуживают отдельного внимания. Queue worker, который всё ещё указывает на старый основной узел, может тихо падать, пока основное приложение выглядит нормально. Проверяйте одну реальную задачу, а не фальшивый ping. Если вы отправляете письма, строите отчёты, синхронизируете данные или обрабатываете webhooks, проследите один полный запуск от начала до конца.
Прежде чем кто-то скажет, что тренировка прошла успешно, запишите время восстановления и примерную хронологию: когда началось переключение, когда изменился DNS, когда приложение переподключилось и когда ошибки вернулись к норме. Зафиксируйте каждую странность, даже маленькую. «Одному worker понадобился перезапуск» — это именно та заметка, которая экономит час во время настоящего сбоя.
Что делать после тренировки
Тренировка действительно полезна только тогда, когда команда сразу что-то меняет. Пока хронология ещё свежа, превратите свои заметки в короткий runbook по восстановлению базы данных. Сделайте его достаточно коротким, чтобы уставший человек мог воспользоваться им в 2 ночи без догадок.
Запишите точные шаги, которые вы реально выполняли, а не те, которые надеялись выполнить. Включите команды, порядок действий и проверки, которые доказали, что новый основной узел может принимать записи.
Короткий runbook обычно нуждается в пяти вещах: кто запускает переключение, как вы подтверждаете, что старый основной узел больше не мешает, где происходит изменение DNS и какой TTL вы ожидаете, как вы проверяете приложение и фоновые задачи после переключения, и когда нужно остановиться или откатиться вместо того, чтобы тянуть дальше.
Не спешите сразу всё автоматизировать. Исправьте первую проблему с переподключением, из-за которой вы потеряли время. Это может быть залипающий connection pool, worker, который держал старый socket открытым, или процесс приложения, которому нужен был перезапуск, хотя он должен был восстановиться сам.
Одна аккуратная правка лучше, чем куча недоделанных скриптов. Если вашему приложению нужно 12 минут на восстановление, потому что один сервис игнорирует изменения DNS, сначала решите именно это и повторите эту часть тренировки. Такие упражнения становятся лучше, когда каждый раунд убирает одну настоящую точку отказа.
Затем поставьте следующую репетицию в календарь до того, как люди забудут неприятные ощущения. Для небольшой команды хороший интервал — две–четыре недели. В следующий раз измените одно условие, чтобы упражнение оставалось честным. Попросите другого человека провести его, сократите TTL или оставьте фоновые задачи активными во время failover.
Если вам нужен второй взгляд на план, Oleg Sotnikov на oleg.is работает с небольшими командами как Fractional CTO по инфраструктуре, runbook и практичным AI-first engineering workflows. Такой внешний разбор помогает подтянуть процесс, не превращая его во что-то тяжёлое.
Когда случится следующий инцидент, вам нужно меньше сюрпризов, меньше ручных перезапусков и команда, которая уже знает сценарий.