首页 > 解决方案 > 如何防止繁琐的查询在 Postgrex 中超时?

问题描述

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

{: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选项的信息,但我不确定如何包含它,或者它是否是我正在寻找的。

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

标签: elixirectopostgrex

解决方案


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

像这样(见最后一行):

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

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

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

由于生产中的错误数量增加,这是我们最近必须做的事情。

还有

处理请求是通过队列完成的。当 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 毫秒。


推荐阅读