Versioned Migrations

If you are using the Atlas migration engine you are able to use the versioned migrations feature of it. Instead of applying the computed changes directly to the database, it will generate a set of migration files containing the necessary SQL statements to migrate the database. These files can then be edited to your needs and be applied by any tool you like (like golang-migrate, Flyway, liquibase).

atlas-versioned-migration-process

Configuration

In order to have Ent make the necessary changes to your code, you have to enable this feature with one of the two options:

  1. If you are using the default go generate configuration, simply add the --feature sql/versioned-migration to the ent/generate.go file as follows:
  1. package ent
  2. //go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature sql/versioned-migration ./schema
  1. If you are using the code generation package (e.g. if you are using an Ent extension), add the feature flag as follows:
  1. //go:build ignore
  2. package main
  3. import (
  4. "log"
  5. "entgo.io/ent/entc"
  6. "entgo.io/ent/entc/gen"
  7. )
  8. func main() {
  9. err := entc.Generate("./schema", &gen.Config{}, entc.FeatureNames("sql/versioned-migration"))
  10. if err != nil {
  11. log.Fatalf("running ent codegen: %v", err)
  12. }
  13. }

Generating Versioned Migration Files

From Client

After regenerating the project, there will be an extra Diff method on the Ent client that you can use to inspect the connected database, compare it with the schema definitions and create sql statements needed to migrate the database to the graph.

  1. package main
  2. import (
  3. "context"
  4. "log"
  5. "<project>/ent"
  6. "ariga.io/atlas/sql/migrate"
  7. "entgo.io/ent/dialect/sql/schema"
  8. )
  9. func main() {
  10. client, err := ent.Open("mysql", "root:pass@tcp(localhost:3306)/test")
  11. if err != nil {
  12. log.Fatalf("failed connecting to mysql: %v", err)
  13. }
  14. defer client.Close()
  15. ctx := context.Background()
  16. // Create a local migration directory.
  17. dir, err := migrate.NewLocalDir("migrations")
  18. if err != nil {
  19. log.Fatalf("failed creating atlas migration directory: %v", err)
  20. }
  21. // Write migration diff.
  22. err = client.Schema.Diff(ctx, schema.WithDir(dir))
  23. if err != nil {
  24. log.Fatalf("failed creating schema resources: %v", err)
  25. }
  26. }

You can then create a new set of migration files by simply calling go run -mod=mod main.go.

From Graph

You can also generate new migration files without an instantiated Ent client. This can be useful if you want to make the migration file creation part of a go generate workflow.

  1. package main
  2. import (
  3. "context"
  4. "log"
  5. "ariga.io/atlas/sql/migrate"
  6. "entgo.io/ent/dialect/sql"
  7. "entgo.io/ent/dialect/sql/schema"
  8. "entgo.io/ent/entc"
  9. "entgo.io/ent/entc/gen"
  10. )
  11. func main() {
  12. // Load the graph.
  13. graph, err := entc.LoadGraph("/.schema", &gen.Config{})
  14. if err != nil {
  15. log.Fatalln(err)
  16. }
  17. tbls, err := graph.Tables()
  18. if err != nil {
  19. log.Fatalln(err)
  20. }
  21. // Create a local migration directory.
  22. d, err := migrate.NewLocalDir("migrations")
  23. if err != nil {
  24. log.Fatalln(err)
  25. }
  26. // Open connection to the database.
  27. dlct, err := sql.Open("mysql", "root:pass@tcp(localhost:3306)/test")
  28. if err != nil {
  29. log.Fatalln(err)
  30. }
  31. // Inspect it and compare it with the graph.
  32. m, err := schema.NewMigrate(dlct, schema.WithDir(d))
  33. if err != nil {
  34. log.Fatalln(err)
  35. }
  36. if err := m.Diff(context.Background(), tbls...); err != nil {
  37. log.Fatalln(err)
  38. }
  39. }

Apply Migrations

The Atlas migration engine does not support applying the migration files onto a database yet, therefore to manage and execute the generated migration files, you have to rely on an external tool (or execute them by hand). By default, Atlas generates one “up” and one “down” migration file for the computed diff. These files are compatible with the popular golang-migrate/migrate package, and you can use that tool to manage the migrations in you deployments.

  1. migrate -source file://migrations -database mysql://root:pass@tcp(localhost:3306)/test up

Moving from Auto-Migration to Versioned Migrations

In case you already have an Ent application in production and want to switch over from auto migration to the new versioned migration, you need to take some extra steps.

  1. Create an initial migration file (or several files if you want) reflecting the currently deployed state.

    To do this make sure your schema definition is in sync with your deployed version. Then spin up an empty database and run the diff command once as described above. This will create the statements needed to create the current state of your schema graph.

  2. Configure the tool you use to manage migrations to consider this file as applied.

    In case of golang-migrate this can be done by forcing your database version as described here.