16 KiB
Анализ рисков миграции на golang-migrate с baseline
Критические риски
1. Потеря данных при неправильном применении baseline
Риск: При применении baseline миграции на существующую БД может произойти:
- Попытка создать уже существующие таблицы (ошибка)
- Потеря данных при DROP/CREATE операциях
- Конфликты с существующими данными
Вероятность: Средняя
Влияние: Критическое
Решения:
-
Обязательный backup перед применением
# Создать backup перед миграцией ./dump-db.sh --env-file .env baseline-migration-backup -
Использование
migrate forceвместоmigrate upдля существующих БД# Для существующих БД - установить версию без применения migrate -path ./migrations -database "postgres://..." force 1 -
Проверка существования таблиц в baseline миграции
- Использовать
CREATE TABLE IF NOT EXISTS(но это не идеально для baseline) - Или создать скрипт проверки перед применением
- Использовать
-
Тестирование на dev окружении
- Сначала применить на dev БД
- Проверить целостность данных
- Только потом применять на production
2. Ошибки в baseline миграции (неполная схема)
Риск: Baseline миграция может не включать:
- Некоторые таблицы или колонки
- Индексы или constraints
- Materialized views
- Начальные данные (словарь с id=0)
- Sequences с правильными значениями
Вероятность: Высокая
Влияние: Критическое
Решения:
-
Автоматическая проверка полноты схемы
# Создать скрипт для сравнения текущей схемы с baseline # Использовать pg_dump --schema-only для сравнения pg_dump --schema-only -h $DB_HOST -U $DB_USER -d $DB_NAME > current_schema.sql # Сравнить с baseline миграцией -
Пошаговая сборка baseline
- Собрать схему из всех init*DB функций
- Добавить все миграции 012-029
- Проверить через
pg_dump --schema-onlyна актуальной БД
-
Тестирование baseline на чистой БД
# Создать тестовую БД createdb test_baseline # Применить baseline migrate -path ./migrations -database "postgres://.../test_baseline" up # Сравнить схему с production -
Валидация через SQL проверки
- Добавить в baseline проверки существования всех таблиц
- Использовать
DO $$ BEGIN ... END $$;блоки для валидации
3. Проблемы с sequences и начальными данными
Риск:
- Sequences могут быть не синхронизированы
- Начальные данные (словарь id=0) могут конфликтовать
- Автоинкременты могут начаться с неправильного значения
Вероятность: Средняя
Влияние: Среднее
Решения:
-
Правильная настройка sequences в baseline
-- После создания таблицы и вставки данных SELECT setval('dictionaries_id_seq', (SELECT MAX(id) FROM dictionaries), true); -
Использование ON CONFLICT для начальных данных
INSERT INTO dictionaries (id, name) VALUES (0, 'Все слова') ON CONFLICT (id) DO NOTHING; -
Проверка sequences после baseline
-- Скрипт для проверки всех sequences SELECT schemaname, sequencename, last_value FROM pg_sequences;
4. Проблемы с materialized views
Риск:
- Materialized view может не создаться корректно
- Зависимости от таблиц могут быть нарушены
- Данные в MV могут быть неактуальными
Вероятность: Средняя
Влияние: Среднее
Решения:
-
Создание MV после всех таблиц
- Убедиться, что все таблицы созданы до создания MV
- Использовать
DROP MATERIALIZED VIEW IF EXISTSперед созданием
-
Обновление данных после создания
-- После создания MV REFRESH MATERIALIZED VIEW weekly_report_mv; -
Проверка зависимостей
-- Проверить зависимости MV SELECT * FROM pg_depend WHERE objid = 'weekly_report_mv'::regclass;
5. Конфликты версий миграций
Риск:
- Таблица
schema_migrationsможет быть в неправильном состоянии - Версия может быть установлена неправильно
- Конфликт между старой и новой системой миграций
Вероятность: Средняя
Влияние: Высокое
Решения:
-
Проверка состояния schema_migrations перед применением
// Проверить, существует ли таблица schema_migrations // Если да - проверить текущую версию var version uint err := db.QueryRow("SELECT version FROM schema_migrations LIMIT 1").Scan(&version) -
Очистка старой таблицы (если была)
-- Если была старая таблица миграций DROP TABLE IF EXISTS old_migrations_table; -
Использование
migrate forceтолько для существующих БД- Новые БД должны использовать
migrate up - Существующие БД -
migrate force 1
- Новые БД должны использовать
6. Проблемы с окружениями (dev/prod различия)
Риск:
- Различия в схемах между dev и prod
- Разные версии PostgreSQL
- Разные настройки БД
Вероятность: Средняя
Влияние: Высокое
Решения:
-
Проверка версии PostgreSQL
SELECT version(); -
Тестирование на всех окружениях
- Dev окружение
- Staging (если есть)
- Production (после успешного тестирования)
-
Документирование различий
- Зафиксировать версию PostgreSQL
- Зафиксировать настройки БД
7. Проблемы с откатом (rollback)
Риск:
- Baseline миграция не может быть откачена
- Ошибки при откате последующих миграций
- Потеря данных при откате
Вероятность: Низкая
Влияние: Высокое
Решения:
-
Baseline не откатывается (по дизайну)
- Пустой
000001_baseline.down.sql - Документировать это ограничение
- Пустой
-
Правильные down миграции для новых миграций
- Каждая новая миграция должна иметь корректный down файл
- Тестировать откат на dev окружении
-
Backup перед откатом
- Всегда создавать backup перед откатом
- Особенно на production
8. Проблемы при старте приложения
Риск:
- Миграции могут не примениться при старте
- Ошибки подключения к БД во время миграций
- Таймауты при применении миграций
Вероятность: Средняя
Влияние: Высокое
Решения:
-
Обработка ошибок миграций
m, err := migrate.NewWithDatabaseInstance( "file://migrations", "postgres", driver) if err != nil { log.Fatal("Failed to initialize migrations:", err) } if err := m.Up(); err != nil { if err != migrate.ErrNoChange { log.Fatal("Failed to apply migrations:", err) } log.Println("Database is up to date") } -
Retry логика для подключения к БД
- Уже есть в коде (10 попыток)
- Применить перед миграциями
-
Таймауты для миграций
// Установить таймаут для миграций ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() -
Логирование процесса миграций
- Логировать каждую применяемую миграцию
- Логировать ошибки с деталями
9. Проблемы с Docker и путями к миграциям
Риск:
- Миграции могут не найтись в контейнере
- Неправильные пути к файлам миграций
- Проблемы с правами доступа
Вероятность: Низкая
Влияние: Среднее
Решения:
-
Проверка путей в Dockerfile
# Убедиться, что миграции копируются COPY play-life-backend/migrations /migrations -
Использование абсолютных путей
migrationsPath := "/migrations" if _, err := os.Stat(migrationsPath); os.IsNotExist(err) { // Fallback для локальной разработки migrationsPath = "play-life-backend/migrations" } -
Проверка доступности миграций при старте
// Проверить, что папка миграций существует if _, err := os.Stat(migrationsPath); os.IsNotExist(err) { log.Fatal("Migrations directory not found:", migrationsPath) }
10. Проблемы с параллельным доступом
Риск:
- Несколько инстансов приложения могут пытаться применить миграции одновременно
- Конфликты при применении миграций
Вероятность: Низкая
Влияние: Высокое
Решения:
-
Блокировки на уровне БД
- golang-migrate использует транзакции
- PostgreSQL блокирует таблицу schema_migrations
-
Применение миграций только в одном инстансе
- Использовать флаг
--migrateдля запуска миграций - Или применять миграции отдельным процессом
- Использовать флаг
-
Проверка версии перед применением
version, dirty, err := m.Version() if dirty { log.Fatal("Database is in dirty state, manual intervention required") }
План митигации рисков
Этап 1: Подготовка (до применения baseline)
- ✅ Создать backup всех БД (dev, staging, prod)
- ✅ Собрать полную схему через
pg_dump --schema-only - ✅ Создать baseline миграцию на основе схемы
- ✅ Протестировать baseline на чистой БД
- ✅ Сравнить схему после baseline с текущей схемой
Этап 2: Тестирование (на dev окружении)
- ✅ Применить baseline через
migrate force 1 - ✅ Проверить целостность данных
- ✅ Проверить работу приложения
- ✅ Проверить sequences и начальные данные
- ✅ Проверить materialized views
Этап 3: Применение (на production)
- ✅ Создать backup production БД
- ✅ Применить baseline через
migrate force 1 - ✅ Проверить работу приложения
- ✅ Мониторинг в течение первых часов
Этап 4: Мониторинг (после применения)
- ✅ Проверить логи приложения
- ✅ Проверить ошибки БД
- ✅ Проверить производительность
- ✅ Собрать обратную связь от пользователей
Чеклист перед применением baseline
- Backup всех БД создан и проверен
- Baseline миграция протестирована на чистой БД
- Схема после baseline совпадает с текущей схемой
- Тестирование на dev окружении успешно
- Инструкции по применению baseline готовы
- Команда проинформирована о миграции
- Окно для миграции запланировано (для production)
- План отката подготовлен (если что-то пойдет не так)
Скрипты для проверки
Скрипт проверки схемы
#!/bin/bash
# check_schema.sh - Проверка полноты baseline миграции
DB_HOST=${DB_HOST:-localhost}
DB_PORT=${DB_PORT:-5432}
DB_USER=${DB_USER:-playeng}
DB_PASSWORD=${DB_PASSWORD:-playeng}
DB_NAME=${DB_NAME:-playeng}
echo "Проверка схемы БД..."
# Получить список всех таблиц
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "
SELECT tablename
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY tablename;
" > current_tables.txt
echo "Таблицы в БД сохранены в current_tables.txt"
echo "Сравните с таблицами в baseline миграции"
Скрипт применения baseline
#!/bin/bash
# apply_baseline.sh - Безопасное применение baseline
set -e
DB_HOST=${DB_HOST:-localhost}
DB_PORT=${DB_PORT:-5432}
DB_USER=${DB_USER:-playeng}
DB_PASSWORD=${DB_PASSWORD:-playeng}
DB_NAME=${DB_NAME:-playeng}
DATABASE_URL="postgres://$DB_USER:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DB_NAME?sslmode=disable"
echo "⚠️ ВНИМАНИЕ: Это применит baseline миграцию!"
echo "База данных: $DB_NAME"
echo "Хост: $DB_HOST:$DB_PORT"
read -p "Вы уверены? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
echo "Отменено"
exit 1
fi
# Создать backup
echo "Создание backup..."
./dump-db.sh --env-file .env baseline-backup-$(date +%Y%m%d_%H%M%S)
# Применить baseline
echo "Применение baseline..."
migrate -path ./play-life-backend/migrations -database "$DATABASE_URL" force 1
echo "✅ Baseline применен успешно"
echo "Проверьте работу приложения"