在Ecto迁移中使用Repo

时间:2018-01-22 12:24:36

标签: elixir database-migration 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

现在,问题在于,通过调用MyApp.Repo.all,迁移实际上使用了与alter table...语句使用的单独数据库连接(编辑:这个假设是错,看到接受的答案)。因此,没有status列,因此整个迁移都会爆炸!注意,我们使用postgres数据库,因此DDL语句是事务性的。

可以执行此操作作为两个单独的迁移或mix任务来设置数据,只迁移模式进行迁移,但不希望为了确保数据一致性。

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

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

2 个答案:

答案 0 :(得分:3)

您可以通过调用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

答案 1 :(得分:2)

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

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

根据使用情况,有不同的方法可以解决这个问题,但它们通常需要一步一步的方法。例如,如果要添加新列:

  • 第一步是在数据库中引入新列,并确保在创建新条目时填充新列。此步骤仅用于保证将来所有条目都已正确填充。

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

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