Секция pre_start в Docker Compose 5.3 не даёт транзакционности между шагами: если второй шаг из трёх упадёт, первый не откатится, а при повторном docker compose up все шаги запустятся заново с нуля — включая уже успешно отработавший первый.
Конкретный пример проблемы
services:
app:
image: myapp:latest
pre_start:
- command: ["./manage.py", "seed-admin-user"]
- command: ["./manage.py", "load-initial-data"]
Если seed-admin-user создаёт запись через обычный INSERT без защиты от дубля, а второй шаг после этого падает, повторный запуск после исправления причины сбоя второго шага снова выполнит первый шаг — и получит ошибку уникальности или, того хуже, тихий дубликат записи, если уникальность не защищена на уровне базы.
Что значит идемпотентность здесь конкретно
Повторный запуск той же команды должен приводить к тому же результату, что и один запуск — без накопления побочных эффектов. Это не абстрактный принцип хорошего тона, а прямое следствие того, как pre_start реально устроен.
| Операция | Неидемпотентный вариант | Идемпотентный вариант |
|---|---|---|
| Вставка записи | INSERT INTO users VALUES (…) | INSERT … ON CONFLICT DO NOTHING |
| Создание таблицы | CREATE TABLE logs (…) | CREATE TABLE IF NOT EXISTS logs (…) |
| Создание директории | mkdir /data/cache | mkdir -p /data/cache |
| Применение миграций | Ручной SQL-скрипт без трекинга | Alembic/Prisma Migrate — уже идемпотентны по дизайну |
Почему готовые инструменты миграций обычно безопасны
Большинство современных инструментов миграций спроектированы идемпотентными изначально — они трекают, какие миграции уже применены, и пропускают их при повторном запуске. Риск в основном в самописных shell-командах и разовых SQL-скриптах внутри pre_start, а не в стандартных инструментах экосистемы.
Почему это редко замечают заранее
Неидемпотентный шаг молча работает нормально месяцами, пока однажды не случится частичный сбой соседнего шага в цепочке — и тогда повторный запуск создаёт проблему, которую тяжело диагностировать постфактум, потому что сама последовательность шагов выглядела рабочей всё это время.
Как проверить свои шаги заранее
Compose pre_start Idempotency Risk Analyzer ищет в командах эвристические признаки неидемпотентного поведения — не как гарантированный вердикт, а как список мест, которые стоит перепроверить перед боевым использованием.
Итоговый чеклист
pre_start не даёт транзакционности между шагами по дизайну — сбой второго шага не откатывает первый, а повторный запуск выполняет все шаги заново.
Готовые инструменты миграций обычно уже идемпотентны сами по себе, риск сосредоточен в самописных SQL-скриптах и shell-командах.
Неидемпотентный шаг обычно молча работает до первого частичного сбоя — стоит проверять идемпотентность заранее, а не полагаться на то, что “оно же работало”.