Migraciones versionadas
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
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:
- macOS + Linux
- Homebrew
- Docker
- Windows
curl -sSf https://atlasgo.sh | sh
brew install ariga/tap/atlas
docker pull arigaio/atlas
docker run --rm arigaio/atlas --help
If the container needs access to the host network or a local directory, use the --net=host flag and mount the desired
directory:
docker run --rm --net=host \
-v $(pwd)/migrations:/migrations \
arigaio/atlas migrate apply
--url "mysql://root:pass@:3306/test"
Download the latest release and move the atlas binary to a file location on your system PATH.
Luego, ejecuta el siguiente comando para generar automáticamente archivos de migración para tu esquema de Ent:
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
- MySQL
- MariaDB
- PostgreSQL
- SQLite
atlas migrate diff migration_name \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "docker://mysql/8/ent"
atlas migrate diff migration_name \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "docker://mariadb/latest/test"
atlas migrate diff migration_name \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "docker://postgres/15/test?search_path=public"
atlas migrate diff migration_name \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "sqlite://file?mode=memory&_fk=1"
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:
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
- MySQL
- MariaDB
- PostgreSQL
- SQLite
atlas migrate apply \
--dir "file://ent/migrate/migrations" \
--url "mysql://root:pass@localhost:3306/example"
atlas migrate apply \
--dir "file://ent/migrate/migrations" \
--url "maria://root:pass@localhost:3306/example"
atlas migrate apply \
--dir "file://ent/migrate/migrations" \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
atlas migrate apply \
--dir "file://ent/migrate/migrations" \
--url "sqlite://file.db?_fk=1"
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:
- MySQL
- MariaDB
- PostgreSQL
- SQLite
atlas migrate status \
--dir "file://ent/migrate/migrations" \
--url "mysql://root:pass@localhost:3306/example"
atlas migrate status \
--dir "file://ent/migrate/migrations" \
--url "maria://root:pass@localhost:3306/example"
atlas migrate status \
--dir "file://ent/migrate/migrations" \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
atlas migrate status \
--dir "file://ent/migrate/migrations" \
--url "sqlite://file.db?_fk=1"
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:
Reproducir el directorio de migraciones existente e inspeccionar el esquema (predeterminado)
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.

Para generar automáticamente archivos de migración, puedes usar uno de estos dos enfoques:
Usa el comando
migrate diffde Atlas contra tu paqueteent/schema.Habilita la feature flag
sql/versioned-migrationy 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
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
- MySQL
- MariaDB
- PostgreSQL
- SQLite
atlas migrate diff migration_name \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "docker://mysql/8/ent"
atlas migrate diff migration_name \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "docker://mariadb/latest/test"
atlas migrate diff migration_name \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "docker://postgres/15/test?search_path=public"
atlas migrate diff migration_name \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "sqlite://file?mode=memory&_fk=1"
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:
- 20220811114629_create_users.sql
- atlas.sum
-- create "users" table
CREATE TABLE `users` (`id` bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`)) CHARSET utf8mb4 COLLATE utf8mb4_bin;
In addition to the migration directory, Atlas maintains a file name atlas.sum which is used
to ensure the integrity of the migration directory and force developers to deal with situations
where migration order or contents were modified after the fact.
h1:vj6fBSDiLEwe+jGdHQvM2NU8G70lAfXwmI+zkyrxMnk=
20220811114629_create_users.sql h1:wrm4K8GSucW6uMJX7XfmfoVPhyzz3vN5CnU1mam2Y4c=
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:
- Using Ent CLI
- Using the entc package
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
If you are using the code generation package (e.g. if you are using an Ent extension like entgql),
add the feature flag as follows:
//go:build ignore
package main
import (
"log"
"entgo.io/ent/entc"
"entgo.io/ent/entc/gen"
)
func main() {
err := entc.Generate("./schema", &gen.Config{
Features: []gen.Feature{gen.FeatureVersionedMigration},
})
if err != nil {
log.Fatalf("running ent codegen: %v", err)
}
}
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:
- MySQL
- MariaDB
- PostgreSQL
docker run --name migration --rm -p 3306:3306 -e MYSQL_ROOT_PASSWORD=pass -e MYSQL_DATABASE=test -d mysql
docker run --name migration --rm -p 3306:3306 -e MYSQL_ROOT_PASSWORD=pass -e MYSQL_DATABASE=test -d mariadb
docker run --name migration --rm -p 5432:5432 -e POSTGRES_PASSWORD=pass -e POSTGRES_DB=test -d postgres
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.
- Atlas
- golang-migrate/migrate
- pressly/goose
- amacneil/dbmate
- Flyway
- Liquibase
//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)
}
}
//go:build ignore
package main
import (
"context"
"log"
"os"
"<project>/ent/migrate"
"ariga.io/atlas/sql/sqltool"
"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 golang-migrate migration file format for replay.
dir, err := sqltool.NewGolangMigrateDir("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
}
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)
}
}
//go:build ignore
package main
import (
"context"
"log"
"os"
"<project>/ent/migrate"
"ariga.io/atlas/sql/sqltool"
"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 goose migration file format for replay.
dir, err := sqltool.NewGooseDir("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
}
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)
}
}
//go:build ignore
package main
import (
"context"
"log"
"os"
"<project>/ent/migrate"
"ariga.io/atlas/sql/sqltool"
"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 dbmate migration file format for replay.
dir, err := sqltool.NewDBMateDir("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
}
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)
}
}
//go:build ignore
package main
import (
"context"
"log"
"os"
"<project>/ent/migrate"
"ariga.io/atlas/sql/sqltool"
"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 Flyway migration file format for replay.
dir, err := sqltool.NewFlywayDir("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
}
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)
}
}
//go:build ignore
package main
import (
"context"
"log"
"os"
"<project>/ent/migrate"
"ariga.io/atlas/sql/sqltool"
"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 Liquibase migration file format for replay.
dir, err := sqltool.NewLiquibaseDir("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
}
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:
- 20220811114629_create_users.sql
- atlas.sum
-- create "users" table
CREATE TABLE `users` (`id` bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`)) CHARSET utf8mb4 COLLATE utf8mb4_bin;
In addition to the migration directory, Atlas maintains a file name atlas.sum which is used
to ensure the integrity of the migration directory and force developers to deal with situations
where migration order or contents were modified after the fact.
h1:vj6fBSDiLEwe+jGdHQvM2NU8G70lAfXwmI+zkyrxMnk=
20220811114629_create_users.sql h1:wrm4K8GSucW6uMJX7XfmfoVPhyzz3vN5CnU1mam2Y4c=
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:
Garantizar que el historial de migraciones pueda reproducirse desde cualquier punto temporal.
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.
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 defectofile://migrations).--dir-format: formato personalizado del directorio (por defectoatlas).(opcional)
--log: registro personalizado usando plantillas Go.(opcional)
--latest: ejecutar análisis sobre los últimosNarchivos de migración.(opcional)
--git-base: ejecutar análisis contra la rama base de Git.
Instalar Atlas:
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:
- macOS + Linux
- Homebrew
- Docker
- Windows
curl -sSf https://atlasgo.sh | sh
brew install ariga/tap/atlas
docker pull arigaio/atlas
docker run --rm arigaio/atlas --help
If the container needs access to the host network or a local directory, use the --net=host flag and mount the desired
directory:
docker run --rm --net=host \
-v $(pwd)/migrations:/migrations \
arigaio/atlas migrate apply
--url "mysql://root:pass@:3306/test"
Download the latest release and move the atlas binary to a file location on your system PATH.
Ejecutar el comando atlas migrate lint:
- MySQL
- MariaDB
- PostgreSQL
- SQLite
atlas migrate lint \
--dev-url="docker://mysql/8/test" \
--dir="file://ent/migrate/migrations" \
--latest=1
atlas migrate lint \
--dev-url="docker://mariadb/latest/test" \
--dir="file://ent/migrate/migrations" \
--latest=1
atlas migrate lint \
--dev-url="docker://postgres/15/test?search_path=public" \
--dir="file://ent/migrate/migrations" \
--latest=1
atlas migrate lint \
--dev-url="sqlite://file?mode=memory" \
--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
}
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.
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
- MySQL
- MariaDB
- PostgreSQL
- SQLite
atlas migrate apply \
--dir "file://ent/migrate/migrations" \
--url "mysql://root:pass@localhost:3306/example"
atlas migrate apply \
--dir "file://ent/migrate/migrations" \
--url "maria://root:pass@localhost:3306/example"
atlas migrate apply \
--dir "file://ent/migrate/migrations" \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
atlas migrate apply \
--dir "file://ent/migrate/migrations" \
--url "sqlite://file.db?_fk=1"
Para más información, consulta la documentación de Atlas.
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):
| id | type |
|---|---|
| 1 | groups |
| 2 | users |
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:
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.
-- 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;
-- 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.
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:
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.