Elixir/Erlang file_server 消息积压和不可靠的吞吐量导致性能问题

Elixir/Erlang file_server message backlog and unreliable throughput causing performance issues

我正在 运行 开发一个生产应用程序,它可以完成很多 I/O。每当系统被新请求淹没时( 我做了大量的 IO)我看到 Erlang file_server 备份消息。 backup/slowdown 可以持续几个小时,具体取决于我们的音量。

据我了解,很多 File 调用实际上是通过 Erlang file_server 进行的。这似乎具有有限的吞吐量。此外,当消息队列得到备份时,整个应用程序基本上被冻结(锁定)并且它根本无法处理新的 IO 请求。

所有 IO 调用都使用 File 模块。我已经在所有允许的地方指定了 [:raw] 选项。据我了解,传入 :raw 将绕过 file_server.

这对我们来说是一个非常大的问题,我想其他人 运行 在某些时候也会遇到这个问题。我尝试重写 Ruby 中的 IO 逻辑,导致吞吐量大幅增加(我没有确切的数字,但这是一个明显的差异)。

有人知道我还能做些什么来增加 performance/throughput 吗?

示例代码:

defmodule MyModule.Ingestion.Insertion.Folder do
  use MyModule.Poller
  alias MyModule.Helpers

  def perform() do
    Logger.info("#{__MODULE__} starting check")

    for path <- paths() do
      files = Helpers.Path.list_files(path, ".json")

      Task.async_stream(
        files,
        fn file ->
          result =
            file
            |> File.read!()
            |> Jason.decode()

          case result do
            {:ok, data} ->
              file_created_at = Helpers.File.created_time(file)
              data = Map.put(data, :file_created_at, file_created_at)
              filename = Path.basename(file, ".json")
              :ok = MyModule.InsertWorker.enqueue(%{data: data, filename: filename})

              destination =
                Application.fetch_env!(:my_application, :backups) <> filename <> ".json"

              File.copy!(file, destination)
              File.rm!(file)

            _err ->
              nil
          end
        end,
        timeout: 60_000,
        max_concurrency: 10
      )
      |> Stream.run()
    end

    Logger.info("#{__MODULE__} check finished")
  end

  def paths() do
    path = Application.fetch_env!(:my_application, :lob_path)

    [
      path <> "postcards/",
      path <> "letters/"
    ]
  end
end

考虑使用 async_threads

调整虚拟机

对于将来发现此问题的任何人。问题的根源来自于对路径名使用 File.copy! 。当您这样做时,副本将通过 Erlang file_server 是造成极其难以诊断的巨大瓶颈的原因。不要将 File.copy/1 与路径名一起使用,而是使用打开的文件作为输入。像这样

source = "path/to/some/file"
destination = "path/to/destination/"

with {:ok, source} <- File.open(source, [:raw, :read]),
     {:ok, destination} <- File.open(destination, [:raw, :write]),
     {:ok, bytes} <- File.copy(source, destination),
     :ok <- File.close(source),
     :ok <- File.close(destination) do
  {:ok, bytes}
end