Как предотвратить задержку громоздкого запроса в Postgrex? - PullRequest
1 голос
/ 09 июля 2019

Я выполняю запрос и загружаю результаты в поток с помощью 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, чтобы мой запрос занимал больше времени. Я прочитал о параметре :timeout в https://hexdocs.pm/postgrex/Postgrex.html#transaction/3, но я не уверен, как включить его, или это то, что я ищу.

Любое руководство очень ценится, спасибо!

1 Ответ

0 голосов
/ 09 июля 2019

Я читал о параметре: 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
)

Всякий раз, когда документы по эликсиру определяют функцию, подобную этой:

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 не похоже на: значение таймаута является источником ошибки.

Сообщение об ошибке продолжается:

соединение недоступно .... Вы можете настроить время ожидания запросов в очереди, используя: queue_target и: queue_interval.См. DBConnection.start_link / 2 для получения дополнительной информации

В этом объясняется, как настроить эти тайм-ауты:

В config/<env>.exs (где <env>это dev, test или prod):

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 мс.

...