MAATRIX / Блог / Заморозить запись на две секунды: согласованный бэкап приложения

Заморозить запись на две секунды: согласованный бэкап приложения

MAATRIX

Снапшот тома снимается за долю секунды, приложение всё это время продолжает работать — и именно поэтому внутри снимка может оказаться каша: часть транзакции уже на диске, часть ещё в памяти. Полная остановка сервиса решает проблему гарантированно, но стоит простоя, который пользователи заметят. Между этими крайностями есть работающий компромисс: на пару секунд приостановить запись, дать снапшоту зафиксировать состояние, и сразу отпустить приложение дальше — так, что никто, кроме мониторинга задержек, этого даже не увидит.

Почему снапшот сам по себе не даёт согласованности

Снапшот на уровне хранилища — будь то LVM, ZFS, снапшот облачного диска или снимок гипервизора — работает с блоками, а не с логикой приложения. Он честно фиксирует, что лежит на диске в момент выполнения команды, но ничего не знает о том, что в этот же момент происходит на уровне СУБД или файловой системы: часть данных может лежать в буферах ОС и ещё не быть сброшена (flush) на диск, часть транзакции — быть частично записана, а журнал (WAL, redo-лог, binlog) — не совпадать по состоянию с файлами данных.

Если снять снапшот без какой-либо координации с приложением, результат — снимок в случайной, произвольной точке между двумя согласованными состояниями. В лучшем случае восстановление отработает штатным механизмом (СУБД доиграет журнал и приведёт данные в порядок сама, как при аварийном выключении). В худшем — журнал и данные разойдутся настолько, что автоматическое восстановление не справится, и придётся разбирать проблему руками, уже без права на ошибку, потому что это единственная копия, которая есть.

Мы разбирали эту механику подробно на примере обычного копирования файлов: бэкап баз данных без остановки сервиса — там же показано, почему cp и rsync по живым файлам БД работают по такому же принципу лотереи. Freeze/quiesce — это способ убрать элемент случайности не за счёт остановки сервиса, а за счёт короткой синхронизации момента снапшота с моментом, когда приложение само подтверждает: "сейчас всё сброшено на диск, можно снимать".

Что такое freeze и зачем нужна пауза именно в приложении

Идея простая: перед тем как инфраструктура нажимает "снять снапшот", приложение (или СУБД, или файловая система) получает команду "заморозиться" — временно приостановить запись новых данных на диск. За эту паузу происходит три вещи:

  • завершаются уже начатые операции записи (не обрываются на середине);
  • всё, что накопилось в буферах, принудительно сбрасывается на диск (flush);
  • новые операции записи ставятся в очередь и ждут, вместо того чтобы выполняться.

В этот момент диск находится в согласованном состоянии — ровно так, как будто приложение штатно остановилось. Инфраструктура снимает снапшот блоков (это происходит быстро, обычно доли секунды — точное время зависит от технологии снапшота и объёма изменённых данных), после чего приложению отправляется команда "разморозиться": очередь накопленных операций разгребается, запись продолжается как ни в чём не бывало.

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Где искать встроенный механизм заморозки

Прежде чем городить собственное решение, стоит проверить, что уже есть под рукой — многие современные СУБД, файловые системы и системы виртуализации умеют это из коробки:

  • Файловые системы. Ряд журналируемых файловых систем в Linux поддерживает команду заморозки на уровне точки монтирования: запись приостанавливается, буферы сбрасываются, файловая система переходит в согласованное состояние до команды разморозки. Это низкоуровневый механизм — он ничего не знает о логике конкретного приложения, но гарантирует целостность самой файловой системы под снапшотом.
  • СУБД с режимом "горячего бэкапа". У большинства промышленных баз данных есть штатный режим для консистентного снятия копии на живой системе: он либо переводит систему в специальный режим резервного копирования на время снятия снимка, либо использует журнал транзакций так, чтобы снимок можно было привести к согласованному состоянию при восстановлении без прямой заморозки записи. Какой именно вариант доступен и как он называется — зависит от конкретной СУБД и её версии, это стоит проверить в актуальной документации, а не полагаться на память или на статью из интернета трёхлетней давности.
  • Гипервизоры и агенты в виртуальных машинах. В инфраструктуре с виртуализацией снапшот тома часто идёт в связке с guest-агентом внутри виртуальной машины, который умеет координированно замораживать файловую систему и уведомлять зарегистрированные приложения о необходимости сбросить буферы перед тем, как гипервизор фиксирует снимок диска.
  • Облачные снапшоты дисков. У managed-дисков в облаках обычно есть API для снятия снапшота, и отдельно — рекомендация (иногда встроенный хук) выполнить freeze/quiesce приложения до вызова этого API. Автоматической согласованности "из коробки" здесь чаще всего нет — её обеспечивает вызывающая сторона.

Если готового встроенного механизма нет или он не подходит по архитектуре — например, приложение пишет не в файлы напрямую, а в собственное хранилище с нестандартным форматом — заморозку реализуют на уровне самого приложения: отдельная команда или сигнал, который переводит писательный поток в режим "поставить в очередь, но не исполнять", и симметричная команда на возврат в обычный режим.

Практическая последовательность: как это выглядит по шагам

Вне зависимости от того, встроенный механизм используется или самописный, последовательность действий одна и та же:

  1. Подготовка. Инфраструктурная сторона (тот, кто снимает снапшот) уведомляет сторону приложения о времени операции — не с запросом разрешения "можно ли вообще", а с конкретным окном, чтобы обе стороны были готовы одновременно, а не гадали, кто первый начал.
  2. Команда заморозки. Отправляется сигнал на паузу записи. Здесь важно дождаться подтверждения, что заморозка реально применена, а не просто отправить команду и сразу переходить к следующему шагу — иначе теряется весь смысл координации.
  3. Снятие снапшота. Инфраструктура снимает снимок тома. Это должно произойти как можно быстрее после подтверждения заморозки — каждая лишняя секунда паузы это либо накапливающаяся очередь на запись, либо риск, что клиентские таймауты начнут срабатывать.
  4. Команда разморозки. Сразу после того как снапшот зафиксирован (не после того как он полностью скопировался куда-то — а именно после фиксации самого снимка, это разные по времени вещи для многих технологий снапшотов), отправляется сигнал на возобновление записи.
  5. Подтверждение разморозки. Приложение подтверждает, что вернулось в обычный режим и накопленная очередь операций обрабатывается. Это отдельный шаг, который часто забывают — без него легко узнать о зависшей заморозке только по жалобам пользователей.
  6. Проверка снимка. Полученный снапшот проверяется на согласованность — хотя бы минимально, например, монтированием в отдельном месте и базовой проверкой целостности, а не только по факту "команда снапшота вернула успех".

Здесь же уместно вспомнить про общий принцип: сам факт, что операция отработала без ошибок, ничего не гарантирует по поводу качества результата. Мы подробно разбирали эту логику применительно к бэкапам в статье как проверить, что бэкап рабочий, не восстанавливая всё — тот же подход применим и к снапшотам, снятым через freeze.

Координация между командой снапшота и командой приложения

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

Кто отвечаетЗа что отвечаетТипичная ошибка
Инфраструктурная команда / система бэкаповИнициирует снапшот, знает расписаниеСнимает снимок по расписанию, не дожидаясь подтверждения заморозки от приложения
Команда приложения / СУБДРеализует и поддерживает механизм заморозкиМеняет логику записи (например, добавляет новый писательный поток), не обновив скрипт заморозки под него
МониторингВидит задержки и таймаутыНе отличает штатную паузу заморозки от реальной деградации, шлёт ложные алерты или, хуже, не шлёт настоящие

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

Отдельная договорённость нужна про допустимую длительность заморозки. Она должна быть заведомо меньше, чем таймауты клиентов приложения и чем интервалы проверок здоровья сервиса в мониторинге и балансировщике — иначе заморозка сама становится причиной инцидента, который она должна была предотвратить.

Тестирование: заморозка должна снова отпускать, а не подвешивать

Самая опасная ошибка с freeze/quiesce — не то, что он не сработает, а то, что он сработает наполовину: запись остановится, а разморозка не придёт вовремя из-за сетевой проблемы, упавшего процесса-координатора или бага в скрипте. Тогда вместо паузы в пару секунд сервис получает зависание неопределённой длительности, и снапшот, ради согласованности которого всё затевалось, оказывается наименьшей из проблем.

Поэтому механизм заморозки нужно регулярно тестировать так же, как тестируют сам процесс восстановления из бэкапа — не доверять факту "настроено и вроде работает":

  • Проверка на staging, а не только в проде. Прогонять полный цикл заморозка → снапшот → разморозка на тестовом окружении с приближённой к продакшену нагрузкой, чтобы увидеть реальное время паузы, а не теоретическое.
  • Watchdog на разморозку. Отдельный таймер, независимый от основного скрипта заморозки: если подтверждение разморозки не пришло за заранее заданное время (с запасом относительно ожидаемой длительности операции), watchdog принудительно снимает заморозку сам — лучше отдать снимок чуть менее аккуратным способом, чем держать сервис в подвешенном состоянии.
  • Учения "что если заморозка зависла". Периодически, не дожидаясь реального инцидента, имитировать сбой на шаге разморозки и смотреть, срабатывает ли аварийный сценарий и замечает ли его мониторинг за разумное время.
  • Контроль реальной длительности паузы. Замерять и логировать фактическое время между заморозкой и разморозкой при каждой операции — если оно начинает расти со временем (например, потому что вырос объём данных для сброса на диск), это сигнал пересмотреть подход раньше, чем пауза станет заметна пользователям.
  • Проверка, что запись действительно продолжилась. После разморозки не полагаться на отсутствие ошибок — явно убедиться, что новые операции записи проходят и очередь, накопленная во время паузы, разгребается, а не растёт бесконечно из-за какой-то вторичной блокировки, которая осталась висеть.

Отдельно стоит помнить, что заморозка приложения — не панацея, если сам снапшот тома не мгновенный технически (например, некоторые сетевые хранилища или облачные диски фиксируют снимок не за миллисекунды, а за заметное время). В таком случае даже идеально скоординированная заморозка вынуждена держаться дольше, и порог "две секунды, которых никто не заметит" может не выполняться — это стоит замерить заранее для конкретной инфраструктуры, а не закладывать по умолчанию.

Если для конкретной СУБД штатного режима согласованного бэкапа нет или он не устраивает по требованиям к паузе, есть смежный и часто более простой путь — снимать логическую копию средствами самой СУБД, где согласованность обеспечивается транзакционно, без заморозки на уровне диска вообще. Разбор одного такого механизма — mysqldump без --single-transaction: бэкап, который не согласован; там же видно, чем логический подход отличается от блочного снапшота и в каких случаях он удобнее.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Чем freeze/quiesce отличается от полной остановки сервиса?

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

Что будет, если снапшот занимает больше времени, чем ожидалось, а заморозка уже активна?

Именно для этого нужен независимый watchdog на разморозку с собственным таймаутом — он должен снять заморозку принудительно, даже если основной сценарий снапшота ещё не завершился, чтобы не превращать паузу в неопределённо долгое зависание.

Можно ли обойтись без координации с приложением и просто полагаться на журнал (WAL/redo-лог) СУБД?

Во многих случаях да — если СУБД умеет восстанавливаться из своего журнала после "грязного" снимка так же, как после аварийного отключения питания. Но это стоит явно проверить тестовым восстановлением из такого снимка, а не считать гарантированным по умолчанию: не у всех систем хранения и не у всех конфигураций журналирования это работает одинаково надёжно.

Нужна ли заморозка, если данные и так реплицируются на резервный узел?

Реплика решает другую задачу — доступность при отказе узла, а не согласованность конкретного снимка. Снапшот с реплики без заморозки подвержен той же проблеме несогласованности, что и снапшот с основного узла — реплика просто более удобное место, откуда снимать, потому что не нагружает боевой трафик.

Как понять, что два секунды паузы действительно безопасны для конкретного приложения?

Смотреть на реальные таймауты клиентов и настройки health check в балансировщике и оркестраторе — пауза должна быть заведомо короче самого строгого из этих порогов, и это нужно проверить нагрузочным тестом, а не предположением.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →