在 Ecto 迁移中使用 Repo

sed*_*ddy 5 database-migration elixir ecto

我有一个 Ecto 迁移,我想修改一些列,但也迁移一些数据。例如:

import Ecto.Query

defmodule MyApp.Repo.Migrations.AddStatus do
  alter table(:foo) do
    add(:status, :text)
  end

  foos = from(f in MyApp.Foo, where: ...)
         |> MyApp.Repo.all

  Enum.each(foos, fn(foo) ->
    # There's then some complex logic here to work 
    # out how to set the status based on other attributes of `foo`
  end)
end
Run Code Online (Sandbox Code Playgroud)

现在,这里的问题是,通过调用MyApp.Repo.all迁移本质上使用单独的数据库连接到alter table...语句使用的数据库连接(编辑:此假设是错误的,请参阅已接受的答案)。因此,没有status列所以整个迁移爆炸!请注意,我们使用的是postgres数据库,因此 DDL 语句是事务性的。

可以将其作为两个单独的迁移或mix设置数据的任务来执行,只为架构更改保留迁移,但为了确保数据一致性,我不希望这样做。

关于如何以MyApp.Repo这种方式使用相同的数据库连接进行查询的任何想法?

编辑:请注意,我正在处理一小组数据,在我的用例中可以接受停机时间。如果情况并非如此,请参阅下面 José 的回复以获取一些好的建议。

Jos*_*lim 6

一般来说,同时进行数据迁移和更改 DDL 是一种不好的做法。如果这是一个实时系统并且迁移需要很长时间,您可能会在很长一段时间内产生大量争用。

如果您的应用程序仍在处理请求,则可以在处理数据时添加新条目,而不会处理这些条目!

有不同的方法来解决这个问题,具体取决于用例,但它们通常需要循序渐进的方法。例如,如果您要添加一个新列:

  • 第一步是在数据库中引入新列,并确保在创建新条目时填充新列。这一步只是为了保证所有未来的条目都将被正确填充。

  • 然后第二步是迁移旧数据

  • 最后,您可以将新数据置于实时状态,因为您可以假设所有条目都已正确填充


Dog*_*ert 6

您可以通过调用 来执行迁移中当前挂起的更改Ecto.Migration.flush/0。之后的任何代码都将具有可用的状态字段。

defmodule MyApp.Repo.Migrations.AddStatus do
  alter table(:foo) do
    add(:status, :text)
  end

  flush()

  foos = from(f in MyApp.Foo, where: ...)
         |> MyApp.Repo.all

  ...
end
Run Code Online (Sandbox Code Playgroud)