Первоначальный коммит

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
poignatov-home
2026-02-08 17:01:36 +03:00
commit bad198ce29
217 changed files with 57075 additions and 0 deletions

View File

@@ -0,0 +1,458 @@
# Анализ рисков миграции на golang-migrate с baseline
## Критические риски
### 1. Потеря данных при неправильном применении baseline
**Риск**: При применении baseline миграции на существующую БД может произойти:
- Попытка создать уже существующие таблицы (ошибка)
- Потеря данных при DROP/CREATE операциях
- Конфликты с существующими данными
**Вероятность**: Средняя
**Влияние**: Критическое
**Решения**:
1. **Обязательный backup перед применением**
```bash
# Создать backup перед миграцией
./dump-db.sh --env-file .env baseline-migration-backup
```
2. **Использование `migrate force` вместо `migrate up` для существующих БД**
```bash
# Для существующих БД - установить версию без применения
migrate -path ./migrations -database "postgres://..." force 1
```
3. **Проверка существования таблиц в baseline миграции**
- Использовать `CREATE TABLE IF NOT EXISTS` (но это не идеально для baseline)
- Или создать скрипт проверки перед применением
4. **Тестирование на dev окружении**
- Сначала применить на dev БД
- Проверить целостность данных
- Только потом применять на production
---
### 2. Ошибки в baseline миграции (неполная схема)
**Риск**: Baseline миграция может не включать:
- Некоторые таблицы или колонки
- Индексы или constraints
- Materialized views
- Начальные данные (словарь с id=0)
- Sequences с правильными значениями
**Вероятность**: Высокая
**Влияние**: Критическое
**Решения**:
1. **Автоматическая проверка полноты схемы**
```bash
# Создать скрипт для сравнения текущей схемы с baseline
# Использовать pg_dump --schema-only для сравнения
pg_dump --schema-only -h $DB_HOST -U $DB_USER -d $DB_NAME > current_schema.sql
# Сравнить с baseline миграцией
```
2. **Пошаговая сборка baseline**
- Собрать схему из всех init*DB функций
- Добавить все миграции 012-029
- Проверить через `pg_dump --schema-only` на актуальной БД
3. **Тестирование baseline на чистой БД**
```bash
# Создать тестовую БД
createdb test_baseline
# Применить baseline
migrate -path ./migrations -database "postgres://.../test_baseline" up
# Сравнить схему с production
```
4. **Валидация через SQL проверки**
- Добавить в baseline проверки существования всех таблиц
- Использовать `DO $$ BEGIN ... END $$;` блоки для валидации
---
### 3. Проблемы с sequences и начальными данными
**Риск**:
- Sequences могут быть не синхронизированы
- Начальные данные (словарь id=0) могут конфликтовать
- Автоинкременты могут начаться с неправильного значения
**Вероятность**: Средняя
**Влияние**: Среднее
**Решения**:
1. **Правильная настройка sequences в baseline**
```sql
-- После создания таблицы и вставки данных
SELECT setval('dictionaries_id_seq',
(SELECT MAX(id) FROM dictionaries),
true);
```
2. **Использование ON CONFLICT для начальных данных**
```sql
INSERT INTO dictionaries (id, name)
VALUES (0, 'Все слова')
ON CONFLICT (id) DO NOTHING;
```
3. **Проверка sequences после baseline**
```sql
-- Скрипт для проверки всех sequences
SELECT schemaname, sequencename, last_value
FROM pg_sequences;
```
---
### 4. Проблемы с materialized views
**Риск**:
- Materialized view может не создаться корректно
- Зависимости от таблиц могут быть нарушены
- Данные в MV могут быть неактуальными
**Вероятность**: Средняя
**Влияние**: Среднее
**Решения**:
1. **Создание MV после всех таблиц**
- Убедиться, что все таблицы созданы до создания MV
- Использовать `DROP MATERIALIZED VIEW IF EXISTS` перед созданием
2. **Обновление данных после создания**
```sql
-- После создания MV
REFRESH MATERIALIZED VIEW weekly_report_mv;
```
3. **Проверка зависимостей**
```sql
-- Проверить зависимости MV
SELECT * FROM pg_depend
WHERE objid = 'weekly_report_mv'::regclass;
```
---
### 5. Конфликты версий миграций
**Риск**:
- Таблица `schema_migrations` может быть в неправильном состоянии
- Версия может быть установлена неправильно
- Конфликт между старой и новой системой миграций
**Вероятность**: Средняя
**Влияние**: Высокое
**Решения**:
1. **Проверка состояния schema_migrations перед применением**
```go
// Проверить, существует ли таблица schema_migrations
// Если да - проверить текущую версию
var version uint
err := db.QueryRow("SELECT version FROM schema_migrations LIMIT 1").Scan(&version)
```
2. **Очистка старой таблицы (если была)**
```sql
-- Если была старая таблица миграций
DROP TABLE IF EXISTS old_migrations_table;
```
3. **Использование `migrate force` только для существующих БД**
- Новые БД должны использовать `migrate up`
- Существующие БД - `migrate force 1`
---
### 6. Проблемы с окружениями (dev/prod различия)
**Риск**:
- Различия в схемах между dev и prod
- Разные версии PostgreSQL
- Разные настройки БД
**Вероятность**: Средняя
**Влияние**: Высокое
**Решения**:
1. **Проверка версии PostgreSQL**
```sql
SELECT version();
```
2. **Тестирование на всех окружениях**
- Dev окружение
- Staging (если есть)
- Production (после успешного тестирования)
3. **Документирование различий**
- Зафиксировать версию PostgreSQL
- Зафиксировать настройки БД
---
### 7. Проблемы с откатом (rollback)
**Риск**:
- Baseline миграция не может быть откачена
- Ошибки при откате последующих миграций
- Потеря данных при откате
**Вероятность**: Низкая
**Влияние**: Высокое
**Решения**:
1. **Baseline не откатывается (по дизайну)**
- Пустой `000001_baseline.down.sql`
- Документировать это ограничение
2. **Правильные down миграции для новых миграций**
- Каждая новая миграция должна иметь корректный down файл
- Тестировать откат на dev окружении
3. **Backup перед откатом**
- Всегда создавать backup перед откатом
- Особенно на production
---
### 8. Проблемы при старте приложения
**Риск**:
- Миграции могут не примениться при старте
- Ошибки подключения к БД во время миграций
- Таймауты при применении миграций
**Вероятность**: Средняя
**Влияние**: Высокое
**Решения**:
1. **Обработка ошибок миграций**
```go
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")
}
```
2. **Retry логика для подключения к БД**
- Уже есть в коде (10 попыток)
- Применить перед миграциями
3. **Таймауты для миграций**
```go
// Установить таймаут для миграций
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
```
4. **Логирование процесса миграций**
- Логировать каждую применяемую миграцию
- Логировать ошибки с деталями
---
### 9. Проблемы с Docker и путями к миграциям
**Риск**:
- Миграции могут не найтись в контейнере
- Неправильные пути к файлам миграций
- Проблемы с правами доступа
**Вероятность**: Низкая
**Влияние**: Среднее
**Решения**:
1. **Проверка путей в Dockerfile**
```dockerfile
# Убедиться, что миграции копируются
COPY play-life-backend/migrations /migrations
```
2. **Использование абсолютных путей**
```go
migrationsPath := "/migrations"
if _, err := os.Stat(migrationsPath); os.IsNotExist(err) {
// Fallback для локальной разработки
migrationsPath = "play-life-backend/migrations"
}
```
3. **Проверка доступности миграций при старте**
```go
// Проверить, что папка миграций существует
if _, err := os.Stat(migrationsPath); os.IsNotExist(err) {
log.Fatal("Migrations directory not found:", migrationsPath)
}
```
---
### 10. Проблемы с параллельным доступом
**Риск**:
- Несколько инстансов приложения могут пытаться применить миграции одновременно
- Конфликты при применении миграций
**Вероятность**: Низкая
**Влияние**: Высокое
**Решения**:
1. **Блокировки на уровне БД**
- golang-migrate использует транзакции
- PostgreSQL блокирует таблицу schema_migrations
2. **Применение миграций только в одном инстансе**
- Использовать флаг `--migrate` для запуска миграций
- Или применять миграции отдельным процессом
3. **Проверка версии перед применением**
```go
version, dirty, err := m.Version()
if dirty {
log.Fatal("Database is in dirty state, manual intervention required")
}
```
---
## План митигации рисков
### Этап 1: Подготовка (до применения baseline)
1. ✅ Создать backup всех БД (dev, staging, prod)
2. ✅ Собрать полную схему через `pg_dump --schema-only`
3. ✅ Создать baseline миграцию на основе схемы
4. ✅ Протестировать baseline на чистой БД
5. ✅ Сравнить схему после baseline с текущей схемой
### Этап 2: Тестирование (на dev окружении)
1. ✅ Применить baseline через `migrate force 1`
2. ✅ Проверить целостность данных
3. ✅ Проверить работу приложения
4. ✅ Проверить sequences и начальные данные
5. ✅ Проверить materialized views
### Этап 3: Применение (на production)
1. ✅ Создать backup production БД
2. ✅ Применить baseline через `migrate force 1`
3. ✅ Проверить работу приложения
4. ✅ Мониторинг в течение первых часов
### Этап 4: Мониторинг (после применения)
1. ✅ Проверить логи приложения
2. ✅ Проверить ошибки БД
3. ✅ Проверить производительность
4. ✅ Собрать обратную связь от пользователей
---
## Чеклист перед применением baseline
- [ ] Backup всех БД создан и проверен
- [ ] Baseline миграция протестирована на чистой БД
- [ ] Схема после baseline совпадает с текущей схемой
- [ ] Тестирование на dev окружении успешно
- [ ] Инструкции по применению baseline готовы
- [ ] Команда проинформирована о миграции
- [ ] Окно для миграции запланировано (для production)
- [ ] План отката подготовлен (если что-то пойдет не так)
---
## Скрипты для проверки
### Скрипт проверки схемы
```bash
#!/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
```bash
#!/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 "Проверьте работу приложения"
```