Что произойдёт, если миграция помечена как default?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Миграции с флагом default (Goose)
Это специфичный вопрос про инструмент миграций Goose. Флаг default имеет критическое значение для работы.
Что такое Goose
Goose — это инструмент для управления миграциями БД на raw SQL (не ORM).
# Установка
go install github.com/pressly/goose/v3/cmd/goose@latest
# Использование
goose postgres "user=... dbname=..." up
goose postgres "user=... dbname=..." down
Миграции хранятся как .sql файлы в папке migrations/.
Флаг default в миграции
-- +goose Up
-- +goose StatementBegin
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE users;
-- +goose StatementEnd
Вот так выглядит базовая миграция. Но что такое флаг default?
Понимание флага default
Это про контроль версии миграций:
Goose версия в базе = 1001
Файлы миграций:
- 0001_create_users.sql
- 0002_add_email_column.sql
- ...
- 1001_add_password_field.sql
- 1002_add_audit_log.sql (помечена как default)
Флаг default указывает на "целевую версию по умолчанию".
Использование default флага
-- миграция 1002_add_audit_log.sql
-- +goose Up
-- +goose StatementBegin
CREATE TABLE audit_log (
id SERIAL PRIMARY KEY,
action VARCHAR(255),
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE audit_log;
-- +goose StatementEnd
Если запустить просто goose up БЕЗ указания версии:
goose postgres "..." up
# Будут применены ВСЕ миграции ДО последней помеченной как default
# Или ВСЕ миграции если default не указана
Практический сценарий: управление версиями
Разработка:
├─ 0001_create_users.sql
├─ 0002_add_posts.sql
├─ 0003_add_comments.sql ← помечена как default для разработки
└─ 0004_experimental_feature.sql (не применяется по умолчанию)
Production:
├─ 0001_create_users.sql
├─ 0002_add_posts.sql ← помечена как default для продакшена
└─ 0003_add_comments.sql (еще не готово)
Это позволяет:
- Разработчикам работать со всеми миграциями
- Production использовать только стабильные
- Избежать применения экспериментальных изменений
Синтаксис default в Goose
В Python проектах с Goose (используется raw SQL):
-- +goose Up
-- +goose StatementBegin
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
price DECIMAL(10, 2)
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE products;
-- +goose StatementEnd
По умолчанию ВСЕ миграции применяются. Но можно управлять через версию:
# Применить только до версии 0002
goose postgres "..." up 2
# Откатить до версии 0001
goose postgres "..." down to 1
# Применить все доступные
goose postgres "..." up
Схема версионирования
Goose отслеживает примененные миграции в таблице:
-- Таблица, которую Goose создает автоматически
CREATE TABLE goose_db_version (
id SERIAL PRIMARY KEY,
version_id BIGINT NOT NULL UNIQUE,
is_applied BOOLEAN NOT NULL,
tstamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- После apply миграции 0001:
INSERT INTO goose_db_version (version_id, is_applied) VALUES (1, true);
-- После apply миграции 0002:
INSERT INTO goose_db_version (version_id, is_applied) VALUES (2, true);
Взаимодействие с Python кодом (SQLAlchemy)
# В проекте с Goose и SQLAlchemy:
from sqlalchemy import create_engine, text
from alembic import command
from alembic.config import Config
class Database:
def __init__(self, db_url):
self.engine = create_engine(db_url)
def get_current_migration_version(self):
"""Получить текущую версию миграции из goose_db_version"""
with self.engine.connect() as conn:
result = conn.execute(
text("""
SELECT version_id FROM goose_db_version
WHERE is_applied = true
ORDER BY version_id DESC
LIMIT 1
""")
)
row = result.fetchone()
return row[0] if row else None
def create_models_in_sync_with_migrations(self):
"""SQLAlchemy модели должны соответствовать миграциям"""
from models import Base
# Убедитесь, что модели соответствуют последней миграции
Base.metadata.reflect(bind=self.engine)
Практический пример: deployment процесс
#!/bin/bash
# deploy.sh
set -e
DB_URL="postgres://user:pass@host/db"
MIGRATIONS_DIR="./migrations"
echo "Current migration version:"
goose postgres "$DB_URL" status
echo "\nApplying migrations..."
# Применяем ВСЕ миграции (или до default если указана)
goose postgres "$DB_URL" up
echo "\nFinal migration version:"
goose postgres "$DB_URL" status
echo "\nDeployment complete!"
Сценарий: откат миграции
# Текущее состояние
$ goose postgres "..." status
goose: status for environment 'production'
Applied At Migration
=============== ============
2024-03-23 10:00:00 0001_create_users.sql
2024-03-23 10:05:00 0002_add_posts.sql
2024-03-23 10:10:00 0003_add_comments.sql
# Нужно откатиться на 0002
$ goose postgres "..." down to 2
goose: DOWN 0003_add_comments.sql
# Новое состояние
$ goose postgres "..." status
goose: status for environment 'production'
Applied At Migration
=============== ============
2024-03-23 10:00:00 0001_create_users.sql
2024-03-23 10:05:00 0002_add_posts.sql
Integration с Python приложением
from subprocess import run
import logging
logger = logging.getLogger(__name__)
def ensure_migrations_applied(db_url: str, migrations_dir: str = "./migrations"):
"""Убедиться что все миграции применены перед запуском приложения"""
try:
logger.info("Checking migration status...")
result = run(
["goose", "postgres", db_url, "status"],
capture_output=True,
text=True,
check=True
)
logger.info(f"Migration status:\n{result.stdout}")
# Если нужны обновления
if "pending" in result.stdout or result.returncode != 0:
logger.info("Applying pending migrations...")
run(
["goose", "postgres", db_url, "up"],
check=True
)
logger.info("Migrations applied successfully")
except Exception as e:
logger.error(f"Migration check failed: {e}")
raise
# Вызывается при запуске приложения
if __name__ == "__main__":
from config import DATABASE_URL
ensure_migrations_applied(DATABASE_URL)
# Затем запускаем приложение
app.run()
Лучшие практики с Goose
-
Нумеруй последовательно
✓ 0001_create_users.sql ✓ 0002_add_posts.sql ✓ 0003_add_comments.sql ❌ 1_create_users.sql ❌ 002_add_posts.sql ❌ 10_add_comments.sql (создает путаницу) -
Каждая миграция = одна логическая часть
-- ✓ Правильно: одно изменение -- +goose Up CREATE TABLE users (...); -- +goose Down DROP TABLE users; -- ❌ Неправильно: много изменений -- +goose Up CREATE TABLE users (...); CREATE TABLE posts (...); CREATE TABLE comments (...); -
Всегда кода UP и DOWN блоки
-- ✓ Правильно -- +goose Up CREATE TABLE ... -- +goose Down DROP TABLE ... -- ❌ Неправильно: нет Down -- +goose Up CREATE TABLE ... -- нет Down! -
Никогда не меняй примененные миграции
❌ Никогда не редактируй 0001_create_users.sql если она уже applied ✓ Создай новую миграцию 0004_fix_users_table.sql -
Тестируй откаты
# Примени goose postgres "..." up # Откати goose postgres "..." down # Примени снова goose postgres "..." up # Если все работает — миграция хорошая
Отличие от Alembic (Python ORM)
# Alembic (для SQLAlchemy) — в Python
from alembic.operations import Operations
def upgrade():
op.create_table(
'users',
sa.Column('id', sa.Integer, primary_key=True),
)
# Goose (raw SQL) — в SQL файлах
-- migrations/0001_create_users.sql
-- +goose Up
CREATE TABLE users (id SERIAL PRIMARY KEY);
# Goose проще для raw SQL, Alembic лучше интегрируется с SQLAlchemy
Заключение
Флаг default в контексте Goose контролирует:
- Версию БД по умолчанию
- Какие миграции применяются автоматически
- Целевое состояние при развертывании
Главное: Goose отслеживает примененные миграции и гарантирует их консистентность через таблицу goose_db_version.