Saltar al contenido principal

Migraciones versionadas

[Traducción Beta No Oficial]

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Guía rápida

Aquí tienes unos pasos rápidos que explican cómo generar y ejecutar automáticamente archivos de migración contra una base de datos. Para una explicación más detallada, continúa leyendo la siguiente sección.

Generación de migraciones

[Traducción Beta No Oficial]

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Para instalar la última versión de Atlas, simplemente ejecuta uno de los siguientes comandos en tu terminal, o visita el sitio web de Atlas:

curl -sSf https://atlasgo.sh | sh

Luego, ejecuta el siguiente comando para generar automáticamente archivos de migración para tu esquema de Ent:

[Traducción Beta No Oficial]

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

atlas migrate diff migration_name \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "docker://mysql/8/ent"

Atlas carga el estado actual ejecutando los archivos SQL almacenados en el directorio de migraciones sobre la base de datos de desarrollo proporcionada. Luego compara este estado con el estado deseado definido por el paquete ent/schema y escribe un plan de migración para pasar del estado actual al deseado.

Aplicación de migraciones

Para aplicar los archivos de migración pendientes en la base de datos, ejecuta el siguiente comando:

[Traducción Beta No Oficial]

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

atlas migrate apply \
--dir "file://ent/migrate/migrations" \
--url "mysql://root:pass@localhost:3306/example"

Para más información, consulta la documentación de Atlas.

Estado de la migración

Utiliza el siguiente comando para obtener información detallada sobre el estado de migración de la base de datos conectada:

atlas migrate status \
--dir "file://ent/migrate/migrations" \
--url "mysql://root:pass@localhost:3306/example"

Guía en profundidad

Si estás usando el motor de migraciones Atlas, puedes utilizar el flujo de trabajo de migraciones versionadas. En lugar de aplicar los cambios calculados directamente a la base de datos, Atlas genera un conjunto de archivos de migración que contienen las sentencias SQL necesarias para migrar la base de datos. Estos archivos pueden luego editarse según tus necesidades y aplicarse mediante muchas herramientas de migración existentes, como golang-migrate, Flyway y Liquibase.

Generación de archivos de migración versionados

Los archivos de migración se generan calculando la diferencia entre dos estados. Llamamos deseado al estado reflejado por tu esquema de Ent, y actual al último estado de tu esquema antes de tus cambios más recientes. Existen dos formas de que Ent determine el estado actual:

  1. Reproducir el directorio de migraciones existente e inspeccionar el esquema (predeterminado)

  2. Conectarse a una base de datos existente e inspeccionar el esquema

Recomendamos utilizar la primera opción, ya que tiene la ventaja de no requerir conexión a una base de datos de producción para crear un diff. Además, este enfoque también funciona si tienes múltiples despliegues en diferentes estados de migración.

proceso de migración versionado de atlas

Para generar automáticamente archivos de migración, puedes usar uno de estos dos enfoques:

  1. Usa el comando migrate diff de Atlas contra tu paquete ent/schema.

  2. Habilita la feature flag sql/versioned-migration y escribe un pequeño script de generación de migraciones que utilice Atlas como paquete para generar los archivos de migración.

Opción 1: Usar el comando atlas migrate diff

[Traducción Beta No Oficial]

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

atlas migrate diff migration_name \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "docker://mysql/8/ent"
nota

Para habilitar la opción GlobalUniqueID en migraciones versionadas, añade el parámetro de consulta globalid=1 al estado deseado. Por ejemplo: --to "ent://ent/schema?globalid=1".

Ejecuta ls ent/migrate/migrations tras completar el comando anterior correctamente, y observarás que Atlas ha creado 2 archivos:

-- create "users" table
CREATE TABLE `users` (`id` bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`)) CHARSET utf8mb4 COLLATE utf8mb4_bin;

Dirígete a la sección Aplicación de archivos de migración para aprender cómo ejecutar los archivos de migración generados en la base de datos.

Opción 2: Crear un script de generación de migraciones

El primer paso es habilitar la función de migración versionada pasando el flag sql/versioned-migration. Dependiendo de cómo ejecutes el generador de código de Ent, debes usar una de estas dos opciones:

If you are using the default go generate configuration, simply add the --feature sql/versioned-migration to the ent/generate.go file as follows:

package ent

//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature sql/versioned-migration ./schema

Tras ejecutar la generación de código con go generate, se habrán añadido nuevos métodos para crear archivos de migración a tu paquete ent/migrate. Los siguientes pasos son:

1. Proporcionar una URL a una base de datos de desarrollo de Atlas para reproducir el directorio de migraciones y calcular el estado actual. Usemos docker para ejecutar un contenedor local de base de datos:

docker run --name migration --rm -p 3306:3306 -e MYSQL_ROOT_PASSWORD=pass -e MYSQL_DATABASE=test -d mysql

2. Crear un archivo llamado main.go y un directorio llamado migrations dentro del paquete ent/migrate, y personalizar la generación de migraciones para tu proyecto.

ent/migrate/main.go
//go:build ignore

package main

import (
"context"
"log"
"os"

"<project>/ent/migrate"

atlas "ariga.io/atlas/sql/migrate"
"entgo.io/ent/dialect"
"entgo.io/ent/dialect/sql/schema"
_ "github.com/go-sql-driver/mysql"
)

func main() {
ctx := context.Background()
// Create a local migration directory able to understand Atlas migration file format for replay.
dir, err := atlas.NewLocalDir("ent/migrate/migrations")
if err != nil {
log.Fatalf("failed creating atlas migration directory: %v", err)
}
// Migrate diff options.
opts := []schema.MigrateOption{
schema.WithDir(dir), // provide migration directory
schema.WithMigrationMode(schema.ModeReplay), // provide migration mode
schema.WithDialect(dialect.MySQL), // Ent dialect to use
schema.WithFormatter(atlas.DefaultFormatter),
}
if len(os.Args) != 2 {
log.Fatalln("migration name is required. Use: 'go run -mod=mod ent/migrate/main.go <name>'")
}
// Generate migrations using Atlas support for MySQL (note the Ent dialect option passed above).
err = migrate.NamedDiff(ctx, "mysql://root:pass@localhost:3306/test", os.Args[1], opts...)
if err != nil {
log.Fatalf("failed generating migration file: %v", err)
}
}

3. Ejecutar la generación de migraciones con go run -mod=mod ent/migrate/main.go <name> desde la raíz del proyecto. Por ejemplo:

go run -mod=mod ent/migrate/main.go create_users

Ejecuta ls ent/migrate/migrations tras completar el comando anterior correctamente, y observarás que Atlas ha creado 2 archivos:

-- create "users" table
CREATE TABLE `users` (`id` bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`)) CHARSET utf8mb4 COLLATE utf8mb4_bin;

Puedes consultar el ejemplo completo de referencia en el repositorio de GitHub.

Verificar y analizar migraciones

Tras generar nuestros archivos de migración con Atlas, podemos ejecutar el comando atlas migrate lint que valida y analiza el contenido del directorio de migraciones, generando información y diagnósticos sobre los cambios seleccionados:

  1. Garantizar que el historial de migraciones pueda reproducirse desde cualquier punto temporal.

  2. Proteger contra cambios inesperados en el historial cuando varias migraciones concurrentes se escriben en el directorio por múltiples miembros del equipo. Más información en la sección siguiente.

  3. Detectar si se han realizado cambios destructivos o irreversibles, o si dependen del contenido de las tablas y pueden causar fallos en la migración.

Ejecutemos atlas migrate lint con los parámetros necesarios para el análisis de migraciones:

  • --dev-url: URL a una base de datos de desarrollo que se usará para reproducir cambios.

  • --dir: URL al directorio de migraciones (por defecto file://migrations).

  • --dir-format: formato personalizado del directorio (por defecto atlas).

  • (opcional) --log: registro personalizado usando plantillas Go.

  • (opcional) --latest: ejecutar análisis sobre los últimos N archivos de migración.

  • (opcional) --git-base: ejecutar análisis contra la rama base de Git.

Instalar Atlas:

[Traducción Beta No Oficial]

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Para instalar la última versión de Atlas, simplemente ejecuta uno de los siguientes comandos en tu terminal, o visita el sitio web de Atlas:

curl -sSf https://atlasgo.sh | sh

Ejecutar el comando atlas migrate lint:

atlas migrate lint \
--dev-url="docker://mysql/8/test" \
--dir="file://ent/migrate/migrations" \
--latest=1

La salida de esta ejecución podría verse así:

20221114090322_add_age.sql: data dependent changes detected:

L2: Adding a non-nullable "double" column "age" on table "users" without a default value implicitly sets existing rows with 0

20221114101516_add_name.sql: data dependent changes detected:

L2: Adding a non-nullable "varchar" column "name" on table "users" without a default value implicitly sets existing rows with ""

Nota sobre IDs globales únicos

Esta sección solo aplica a usuarios de MySQL que empleen la función de ID global único.

Cuando se utilizan identificadores únicos globales, Ent asigna un rango de valores enteros de 1<<32 para cada tabla. Esto se realiza asignando a la primera tabla un valor de inicio autoincremental de 1, a la segunda un valor inicial de 4294967296, a la tercera 8589934592, y así sucesivamente. El orden en que las tablas reciben el valor inicial se guarda en una tabla adicional llamada ent_types. En MySQL 5.6 y 5.7, el valor de inicio autoincremental solo se guarda en memoria (documentación, encabezado InnoDB AUTO_INCREMENT Counter Initialization) y se recalcula al iniciar examinando el último ID insertado para cada tabla. Si tienes una tabla sin filas aún, el valor de inicio autoincremental se establece en 0 para todas las tablas sin entradas. Con la migración online esto no era un problema porque el motor de migración consultaba la tabla ent_types y actualizaba el contador si no estaba configurado correctamente. Sin embargo, con las migraciones versionadas esto ya no ocurre. Para garantizar que todo se configure correctamente tras un reinicio del servidor, asegúrate de llamar al método VerifyTableRange en la estructura de Atlas:

package main

import (
"context"
"log"

"<project>/ent"
"<project>/ent/migrate"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/schema"

_ "github.com/go-sql-driver/mysql"
)

func main() {
drv, err := sql.Open("mysql", "user:pass@tcp(localhost:3306)/ent")
if err != nil {
log.Fatalf("failed opening connection to mysql: %v", err)
}
defer drv.Close()
// Verify the type allocation range.
m, err := schema.NewMigrate(drv, nil)
if err != nil {
log.Fatalf("failed creating migrate: %v", err)
}
if err := m.VerifyTableRange(context.Background(), migrate.Tables); err != nil {
log.Fatalf("failed verifyint range allocations: %v", err)
}
client := ent.NewClient(ent.Driver(drv))
// ... do stuff with the client
}
[Importante]

Tras una actualización a MySQL 8 desde una versión anterior, aún debes ejecutar este método una vez para actualizar los valores iniciales. Desde MySQL 8 el contador ya no se almacena solo en memoria, por lo que las ejecuciones posteriores del método no son necesarias después de la primera.

Aplicar archivos de migración

Ent recomienda usar la CLI de Atlas para aplicar los archivos de migración generados en la base de datos. Si prefieres usar otras herramientas de gestión de migraciones, Ent soporta generar migraciones para varias de ellas directamente.

[Traducción Beta No Oficial]

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

atlas migrate apply \
--dir "file://ent/migrate/migrations" \
--url "mysql://root:pass@localhost:3306/example"

Para más información, consulta la documentación de Atlas.

información

En versiones anteriores de Ent, golang-migrate/migrate era el motor de ejecución de migraciones predeterminado. Para facilitar la transición, Atlas puede importar el formato de migraciones de golang-migrate. Puedes obtener más información en la documentación de Atlas.

Migrar de Auto-Migración a Migraciones Versionadas

Si ya tienes una aplicación de Ent en producción y quieres cambiar de migración automática a migraciones versionadas, necesitarás seguir algunos pasos adicionales.

Crear un archivo de migración inicial que refleje el estado desplegado actual

Para esto, asegúrate de que tu definición de esquema esté sincronizada con tu versión(es) desplegada. Luego crea una base de datos vacía y ejecuta el comando diff como se describió anteriormente. Esto generará las sentencias necesarias para crear el estado actual de tu esquema. Si tenías IDs universales habilitadas previamente, cualquier despliegue tendrá una tabla especial llamada ent_types. El comando anterior creará las sentencias SQL necesarias para crear esa tabla y su contenido (similar a lo siguiente):

CREATE TABLE `users` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT);
CREATE TABLE `groups` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT);
INSERT INTO sqlite_sequence (name, seq) VALUES ("groups", 4294967296);
CREATE TABLE `ent_types` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `type` text NOT NULL);
CREATE UNIQUE INDEX `ent_types_type_key` ON `ent_types` (`type`);
INSERT INTO `ent_types` (`type`) VALUES ('users'), ('groups');

Para garantizar que no se rompa el código existente, asegúrate de que el contenido de ese archivo sea idéntico al contenido presente en la tabla de la base de datos desde la que creaste el diff. Por ejemplo, si consideras el archivo de migración anterior (users,groups) pero tu tabla desplegada luce como la siguiente (groups,users):

idtype
1groups
2users

Puedes observar que el orden difiere. En ese caso, debes cambiar manualmente tanto las entradas en el archivo de migración generado.

Usar una Baseline de Migración de Atlas

Si usas Atlas como motor de ejecución de migraciones, simplemente puedes usar la bandera --baseline. Para otras herramientas, consulta su documentación respectiva.

atlas migrate apply \
--dir "file://migrations"
--url mysql://root:pass@localhost:3306/ent
--baseline "<version>"

Archivo de integridad del directorio de migraciones de Atlas

El problema

Supongamos que tienes varios equipos desarrollando una característica en paralelo y ambos necesitan una migración. Si el Equipo A y el Equipo B no se coordinan, podrían terminar con un conjunto de archivos de migración dañado (como agregar la misma tabla o columna dos veces), ya que los nuevos archivos no generan conflictos de fusión en sistemas de control de versiones como Git. El siguiente ejemplo ilustra este comportamiento:

migraciones versionadas de atlas sin conflicto

Imagina que tanto el Equipo A como el Equipo B añaden un nuevo esquema llamado User y generan un archivo de migración versionado en sus respectivas ramas.

20220318104614_team_A.sql
-- create "users" table
CREATE TABLE `users` (
`id` bigint NOT NULL AUTO_INCREMENT,
`team_a_col` INTEGER NOT NULL,
PRIMARY KEY (`id`)
) CHARSET utf8mb4 COLLATE utf8mb4_bin;
20220318104615_team_B.sql
-- create "users" table
CREATE TABLE `users` (
`id` bigint NOT NULL AUTO_INCREMENT,
`team_b_col` INTEGER NOT NULL,
PRIMARY KEY (`id`)
) CHARSET utf8mb4 COLLATE utf8mb4_bin;

Si ambos fusionan su rama en master, Git no detectará conflictos y todo parecerá correcto. Pero al intentar aplicar las migraciones pendientes, se producirá un fallo:

mysql> CREATE TABLE `users` (`id` bigint NOT NULL AUTO_INCREMENT, `team_a_col` INTEGER NOT NULL, PRIMARY KEY (`id`)) CHARSET utf8mb4 COLLATE utf8mb4_bin;
[2022-04-14 10:00:38] completed in 31 ms

mysql> CREATE TABLE `users` (`id` bigint NOT NULL AUTO_INCREMENT, `team_b_col` INTEGER NOT NULL, PRIMARY KEY (`id`)) CHARSET utf8mb4 COLLATE utf8mb4_bin;
[2022-04-14 10:00:48] [42S01][1050] Table 'users' already exists

Dependiendo del SQL ejecutado, esto podría dejar tu base de datos en un estado inconsistente.

La solución

Afortunadamente, el motor de migraciones de Atlas ofrece un mecanismo para prevenir la creación concurrente de archivos y proteger contra cambios accidentales en el historial de migraciones: el Archivo de Integridad del Directorio de Migraciones. Este es simplemente otro archivo en tu directorio de migraciones llamado atlas.sum. Para el directorio del Equipo A, se vería así:

h1:KRFsSi68ZOarsQAJZ1mfSiMSkIOZlMq4RzyF//Pwf8A=
20220318104614_team_A.sql h1:EGknG5Y6GQYrc4W8e/r3S61Aqx2p+NmQyVz/2m8ZNwA=

El archivo atlas.sum contiene la suma de comprobación de cada archivo de migración (implementada mediante un árbol de hash de Merkle inverso de una rama) y una suma global de todos los archivos. Añadir nuevos archivos modifica este archivo de suma, lo que generará conflictos de fusión en la mayoría de sistemas de control de versiones. Veamos cómo usar este Archivo de Integridad del Directorio de Migraciones para detectar automáticamente el caso anterior.

nota

Necesitarás tener la CLI de Atlas instalada en tu sistema para que esto funcione. Asegúrate de seguir las instrucciones de instalación antes de continuar.

En versiones anteriores de Ent, este archivo de integridad era opcional. Pero consideramos que es una característica fundamental que aporta gran valor y seguridad a las migraciones. Por ello, la generación del archivo de suma es ahora el comportamiento predeterminado, y en el futuro podríamos eliminar la opción para desactivarla. Si realmente necesitas deshabilitar esta función, usa la opción schema.DisableChecksum().

Además de los archivos .sql habituales, el directorio de migraciones contendrá el archivo atlas.sum. Cada vez que generes un nuevo archivo de migración con Ent, este archivo se actualizará automáticamente. Sin embargo, cualquier modificación manual en el directorio desincronizará el contenido con atlas.sum. Con la CLI de Atlas puedes verificar la sincronización y repararla si es necesario:

# If there is no output, the migration directory is in-sync.
atlas migrate validate --dir file://<path-to-your-migration-directory>
# If the migration directory and sum file are out-of-sync the Atlas CLI will tell you.
atlas migrate validate --dir file://<path-to-your-migration-directory>
Error: checksum mismatch

You have a checksum error in your migration directory.
This happens if you manually create or edit a migration file.
Please check your migration files and run

'atlas migrate hash'

to re-hash the contents and resolve the error.

exit status 1

Si estás seguro de que el contenido de tus archivos de migración es correcto, puedes recalcular los hashes en el archivo atlas.sum:

# Recompute the sum file.
atlas migrate hash --dir file://<path-to-your-migration-directory>

Retomando el problema anterior: si el Equipo A integra sus cambios en master primero y el Equipo B intenta hacerlo después, obtendrían un conflicto de fusión como se muestra en este ejemplo:

migraciones versionadas de atlas sin conflicto

Puedes añadir atlas migrate validate a tu pipeline de CI para verificar continuamente el directorio de migraciones. Incluso si algún miembro del equipo olvida actualizar atlas.sum tras una edición manual, el CI no dará luz verde, señalando así un problema.