【问题标题】:How do I prevent a cumbersome query from timing out in Postgrex?如何防止繁琐的查询在 Postgrex 中超时?
【发布时间】:2019-07-08 22:37:22
【问题描述】:

我正在运行查询并使用 Postgrex 将结果加载到流中,如下所示:

{:ok, host_pid} = Postgrex.start_link(hostname: "somewhere.hostname.io", username: "myuser", password: "mypass", database: "mydb")

Postgrex.transaction(host_pid, fn(conn) ->
    # do query that takes 5 seconds
    # with timeout set to be really big
    query = Postgrex.prepare!(conn, "", "SELECT pg_sleep(5)", timeout: 50_000)
    stream = Postgrex.stream(conn, query)
    result_to_iodata = fn(%Postgrex.Result{rows: rows}) -> format_query_result(rows) end
    Enum.into(stream, File.stream!("eg"), result_to_iodata)
end)

但我收到以下错误:

localhost$ mix run lib/MyPostgrexScript.exs 
** (DBConnection.ConnectionError) connection not available and request was dropped 
from queue after 2950ms. You can configure how long requests wait in the queue 
using :queue_target and :queue_interval. See DBConnection.start_link/2 for more information
    (db_connection) lib/db_connection.ex:836: DBConnection.transaction/3
    lib/MyPostgrexScript.exs:3: MyPostgrexModule.sleep/0
    (elixir) lib/code.ex:767: Code.require_file/2
    (mix) lib/mix/tasks/run.ex:147: Mix.Tasks.Run.run/5

由于我想做繁琐的查询,这肯定需要超过 2950 毫秒才能运行,我想知道如何配置 Postgrex 以让我的查询花费更多时间。我在https://hexdocs.pm/postgrex/Postgrex.html#transaction/3 阅读了有关:timeout 选项的信息,但我不确定如何包含它,或者它是否是我正在寻找的。

非常感谢任何指导,谢谢!

【问题讨论】:

    标签: elixir ecto postgrex


    【解决方案1】:

    我阅读了关于 :timeout 选项的信息 https://hexdocs.pm/postgrex/Postgrex.html#transaction/3 但我不是 确定如何包含它,

    像这样(见最后一行):

    Postgrex.transaction(
        host_pid,
    
        fn(conn) ->
            # do query that takes 5 seconds
            # with timeout set to be really big
            query = Postgrex.prepare!(conn, "", "SELECT pg_sleep(5)", timeout: 50_000)
            stream = Postgrex.stream(conn, query)
            result_to_iodata = fn(%Postgrex.Result{rows: rows}) ->
                                     format_query_result(rows) 
                               end
            Enum.into(stream, File.stream!("eg"), result_to_iodata)
        end,
    
        timeout: 30_000  #30 seconds
    )
    

    每当 elixir 文档定义这样的函数时:

    func_name(arg1, ...argN, opts \\ [] )
    

    opts是关键字列表,例如:

    [{:a, 1}, {:b, 2}]
    

    但是,如果关键字列表是函数调用中的最后一个参数,则关键字列表可以这样写:

    func(arg1, arg2, a: 1, b: 2)
    

    并且函数定义将接收三个参数

    arg1, arg2, [{:a, 1}, {:b, 2}]
    

    无论如何,:timeout 的默认值是:

    :timeout - Transaction timeout (default: 15000);
    

    错误提示:

    连接不可用,请求从队列中删除后 2950毫秒

    因为2950 < 15000 似乎 :timeout 值不是错误的根源。

    错误信息继续:

    连接不可用.... 您可以配置请求在队列中等待多长时间使用 :queue_target 和 :queue_interval。请参阅 DBConnection.start_link/2 了解 更多信息

    This 解释了如何配置这些超时:

    config/<env>.exs 中(其中<env> 是开发、测试或产品):

    config :my_app, MyApp.Repo,
      adapter: Ecto.Adapters.Postgres,
      pool_size: 10,
      migration_timestamps: [type: :utc_datetime_usec],
      migration_lock: nil,
      queue_target: 5000
    

    这是我们最近由于错误数量增加而不得不做的事情 生产。

    Also,

    处理请求是通过队列完成的。当 DBConnection 是 开始,有两个相关的选项来控制队列:

    :queue_target in milliseconds, defaults to 50ms
    :queue_interval in milliseconds, defaults to 1000ms
    

    我们的目标是最多等待 :queue_target 连接。我摔倒 在 :queue_interval 期间签出的连接需要超过 :queue_target,然后我们将 :queue_target 加倍。如果退房 连接比新目标花费更长的时间,然后我们开始丢弃 消息。

    例如,默认情况下我们的目标是 50 毫秒。如果所有连接 结帐时间超过 50 毫秒一整秒,我们加倍 目标为 100 毫秒,如果结帐时间到了,我们就会开始丢弃消息 超过了新的限制。

    这使我们可以更好地计划过载,因为我们可以拒绝请求 在它们被发送到数据库之前,否则会增加 数据库的负担,使过载更加严重。

    但是,如果您还没有触及这些默认值,那么我想知道您为什么会看到 2950ms 在错误消息中,而不是接近 50 毫秒或 1000 毫秒。

    【讨论】:

    • 事实证明 pg_sleep 甚至在 Amazon Redshift 上都不存在(这是我连接的)。尽管错误消息坚持错误出在事务中,但我相信超时是在尝试连接期间。但是这个错误会自行消失,所以谁知道呢。不过,很棒的答案,感谢您抽出宝贵的时间!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-21
    • 1970-01-01
    • 1970-01-01
    • 2010-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多