如何防止麻烦的查询在Postgrex中超时?

时间:2019-07-08 22:37:22

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

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

1 个答案:

答案 0 :(得分:0)

  

我在以下位置了解了: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);

错误提示:

  

连接不可用,并且请求从队列中删除后   2950ms

因为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加倍。如果退房   连接所需的时间比新目标要长,然后我们开始删除   消息。

     

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

     

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

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