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é 的回复以获取一些好的建议。
一般来说,同时进行数据迁移和更改 DDL 是一种不好的做法。如果这是一个实时系统并且迁移需要很长时间,您可能会在很长一段时间内产生大量争用。
如果您的应用程序仍在处理请求,则可以在处理数据时添加新条目,而不会处理这些条目!
有不同的方法来解决这个问题,具体取决于用例,但它们通常需要循序渐进的方法。例如,如果您要添加一个新列:
第一步是在数据库中引入新列,并确保在创建新条目时填充新列。这一步只是为了保证所有未来的条目都将被正确填充。
然后第二步是迁移旧数据
最后,您可以将新数据置于实时状态,因为您可以假设所有条目都已正确填充
您可以通过调用 来执行迁移中当前挂起的更改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)