【问题标题】:Ecto migrations ownership timeoutEcto 迁移所有权超时
【发布时间】:2019-01-03 13:56:33
【问题描述】:

有时当我的 Ecto 迁移在 CI 中运行时(从头开始构建数据库,有时会出现不是由我们的代码引起的长时间停顿),它们会超时,并出现如下错误:

10:05:01.828 [error] Postgrex.Protocol (#PID) 已断开连接:** (DBConnection.ConnectionError) owner #PID 超时,因为它拥有连接的时间超过15000毫秒

我可以通过将:timer.sleep(15_000) 添加到任何单独的迁移来在本地重现此问题。我还可以通过向几个不同的迁移添加更短的 timer.sleep/1 调用来重现它,这似乎表明运行所有迁移都有一个时间限制。

我可以通过为 repo 配置 ownership_timeout 在本地解决此问题:

config :my_app, MyApp.Repo,
  # ...other configurations...
  ownership_timeout: 60_000 # or whatever

使用此设置,即使使用上面的 :timer.sleep/1 调用,迁移也会成功运行。但我不希望在大多数情况下应用此设置。

如何在运行迁移时指定 :ownership_timeout使用?

【问题讨论】:

    标签: elixir ecto


    【解决方案1】:

    使用Application.put_env/4:

    app_env = Application.get_env(:my_app, MyApp.Repo, [])
    # temporary advance the timeout
    Application.put_env(
      :my_app,
      MyApp.Repo,
      Keyword.merge(app_env, [ownership_timeout: 60_000])
    )
    # run migrations
    path = Application.app_dir(:my_app, "priv/repo/migrations")
    Ecto.Migrator.run(MyApp.Repo, path, :up, all: true)
    # restore setting
    Application.put_env(:my_app, MyApp.Repo, app_env)
    

    【讨论】:

      【解决方案2】:

      另一个解决方案是为您的迁移定义一个新配置。这样你仍然可以通过mix ecto.migrate运行它们

      # config/migrations.exs
      
      use Mix.Config
      import_config("prod.exs")
      
      config :my_app, MyApp.Repo,
        ownership_timeout: 60_000
      

      您现在可以通过MIX_ENV=migrations mix ecto.migrate 运行您的迁移。

      【讨论】:

      • 这在开发过程中更方便,但我看不到在生产中使用这种方法的任何方法。我错过了什么吗?在部署期间无法切换环境,尤其是在热重载部署期间。
      • 嗯,好点,滥用MIX_ENV时无法获取该信息。您可以将“dev”交换为“prod”,因为它似乎只是在 CI 环境中发生。但是您的解决方案可能更适合一般用例
      【解决方案3】:

      自定义 Mix 别名

      在@mudasobwa 的回答的基础上,我做了一个mix alias 临时修改配置,然后运行正常的迁移任务。

      mix.exs:

      defmodule Coach.Umbrella.Mixfile do
      # other stuff...
      
        defp aliases do
          [
            "ecto.migrate": &migrate_with_long_timeout/1
          ]
        end
      
        defp migrate_with_long_timeout(_) do
          IO.inspect "prove that my custom task is running"
          app_env = Application.get_env(:coach_domain, CoachDomain.Repo, [])
          # Temporarily increase the timeout
          Application.put_env(
            :coach_domain,
            CoachDomain.Repo,
            Keyword.merge(app_env, [ownership_timeout: 60_000])
          )
          Mix.Task.run("ecto.migrate")
          # Restore normal configuration
          Application.put_env(:coach_domain, CoachDomain.Repo, app_env)
        end
      end
      

      请注意,别名是特定于项目的。例如,在一个综合项目中,您是在顶级目录中定义它还是在特定于应用程序的目录中定义它,决定了您可以从哪里运行它。

      【讨论】:

      • 这在生产中不起作用(至少不适用于delivery。)如果您想要一个仅限本地的解决方案,请使用@JonasDellinger 的答案,它更干净。
      • 好点。但这在开发和测试中以及在测试中的 CI 中都适用。在 prod 中,我不希望从一开始就一次性运行所有迁移。
      • Ecto 足够聪明,可以跳过已经应用的迁移。这条线可以安全运行,这正是它应该如何部署到生产环境(这就是我的部署方式。)
      • @mudasobwa 抱歉,我的评论不清楚。我在 CI 中遇到此超时的原因是它从头开始运行所有迁移以从头开始构建数据库。我们有时也在开发中这样做。所以在这两种情况下,我们都可能遇到超时。但是在生产中,我们不会运行所有迁移,因为会有现有数据,正如你所说,ecto 只会运行最新的数据。所以我不希望在那里遇到超时。在开发和 CI 中,mix 可用。
      猜你喜欢
      • 2016-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-22
      相关资源
      最近更新 更多