Приложение не стартует: блокировка миграций базы
Приложения, применяющие миграции при старте, берут блокировку в базе. Если прежний запуск был прерван — например по таймауту systemd, — блокировка остаётся, и следующий запуск ждёт её или падает.
Что это значит
Это типичное последствие слишком короткого таймаута запуска: systemd прерывает миграцию на середине, а приложение не успевает снять блокировку. Дальше каждая попытка запуска упирается в неё.
Вероятные причины
По порядку: сверху то, что встречается чаще.
-
Блокировка осталась от прерванного запуска
Прерывание по таймауту или сигналом не даёт приложению убрать за собой. Запись о блокировке остаётся в базе.
-
Миграции применяются несколькими экземплярами одновременно
При запуске приложения на нескольких серверах все они пытаются применить миграции. Блокировка для этого и нужна, но ожидание может превысить таймаут.
-
База недоступна в момент миграции
Приложение ждёт базу, не укладывается в таймаут и прерывается на середине.
Диагностика
Команды идут в том порядке, в котором их стоит выполнять: каждая следующая проверяет то, что осталось после предыдущей.
Что происходило с миграциями при последних запусках.
journalctl -u myapp.service -n 50 --no-pager | grep -iE "migrat|lock|timeout"Таймаут и число прерванных попыток.
systemctl show myapp.service -p TimeoutStartSec -p NRestartsРешение
Решение своё для каждой причины. Сначала определите, какая из них ваша, — иначе правки наложатся друг на друга.
- Почему происходит
- Прерывание по таймауту или сигналом не даёт приложению убрать за собой. Запись о блокировке остаётся в базе.
- Как проверить
-
Посмотрите журнал приложения и таблицу блокировок в базе.
journalctl -u myapp.service -n 40 --no-pager | grep -iE "migrat|lock"
- Как исправить
-
Снимите блокировку средствами приложения (у большинства есть команда) и увеличьте таймаут запуска, чтобы миграция успевала завершиться.
sudo systemctl edit myapp.service # [Service]\nTimeoutStartSec=10min
- Почему происходит
- При запуске приложения на нескольких серверах все они пытаются применить миграции. Блокировка для этого и нужна, но ожидание может превысить таймаут.
- Как проверить
-
Посмотрите, сколько экземпляров стартует одновременно.
systemctl list-units "myapp*" --no-pager
- Как исправить
- Выделите миграции в отдельную одноразовую службу и запускайте её до приложения: остальные экземпляры тогда не будут конкурировать.
- Почему происходит
- Приложение ждёт базу, не укладывается в таймаут и прерывается на середине.
- Как проверить
-
Посмотрите доступность базы и порядок запуска.
systemctl show myapp.service -p After | tr " " "\n" | grep -i sql pg_isready 2>/dev/null || mysqladmin ping 2>/dev/null
- Как исправить
- Добавьте зависимость от базы и повторные попытки подключения в приложении.
Пример вывода
Блокировка миграций осталась от прерванного запуска. Пример показательный: он собран на тестовой машине специально для этой страницы, а не взят из чужого журнала.
myapp[4300]: waiting for migration lock (held by instance 7f2a, since 14:02:11)
systemd[1]: myapp.service: start operation timed out. Terminating.
systemd[1]: myapp.service: Failed with result 'timeout'.
Связанные ошибки
- Failed with result 'timeout' Состояние timeout: служба не уложилась в отведённое время при запуске, остановке или перезагрузке настроек.
- Job for … failed because a timeout was exceeded Задание на запуск прервано по таймауту. Как отличить медленный старт от заблокированного и правильно настроить TimeoutStartSec.
- Connection refused в журнале службы Соединение отклонено: служба не может подключиться к базе, кешу или другому сервису. Порядок запуска, адрес, брандмауэр.
- A start job is running: загрузка висит на одной службе Загрузка останавливается с обратным отсчётом: служба не укладывается в таймаут. Как найти виновника и не ждать.
- A stop job is running: выключение висит Перезагрузка останавливается с обратным отсчётом: служба не завершается. Как найти и сократить ожидание.
- Connection timed out в журнале службы Соединение не устанавливается по таймауту: пакеты отбрасываются, узел недоступен, перегружен сервер на другой стороне.
- Gunicorn: WORKER TIMEOUT в журнале службы Рабочие процессы Gunicorn убиваются по таймауту: медленные запросы, блокирующие вызовы, неверный тип обработчика.
- PostgreSQL: долгий запуск и таймаут восстановления Кластер восстанавливается после аварийного завершения дольше таймаута systemd. Как правильно увеличить TimeoutStartSec.
Где встречается чаще всего
Источники
-
systemd.service(5)
TimeoutStartSec= и прерывание запуска. -
Воспроизведено на тестовой машине, systemd 255 (Ubuntu 24.04)
Воспроизведено прерыванием миграции по таймауту на тестовом приложении.