Журнал ожидания сообщения Elixir / Erlang file_server и ненадежная пропускная способность, вызывающие проблемы с производительностью - PullRequest
2 голосов
/ 04 мая 2020

Я запускаю производственное приложение, которое выполняет много операций ввода-вывода. Всякий раз, когда система заполняется новыми запросами (я делаю кучу операций ввода-вывода), я вижу файл-сервер Erlang, резервный с сообщениями. Резервное копирование / замедление может длиться часами в зависимости от нашего объема.

enter image description here

Насколько я понимаю, многие из File на самом деле go вызывают через Erlang file_server. Который, кажется, имеет ограниченную пропускную способность. Кроме того, когда резервная копия очереди сообщений полностью блокируется, приложение полностью не может обрабатывать новые запросы ввода-вывода.

Все вызовы ввода-вывода используют модуль File. Я указал опцию [:raw] везде, где это разрешено. Насколько я понимаю, передача :raw обойдет файловый сервер.

Это действительно большая проблема для нас, и я думаю, что другие столкнулись с ней в какой-то момент. Я экспериментировал с переписыванием логики ввода-вывода c в Ruby ведьма привела к огромному увеличению пропускной способности (у меня нет точных цифр, но это было заметное различие).

Кто-нибудь знает, что еще я можно посмотреть на повышение производительности / пропускной способности?

Пример кода:

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

1 Ответ

1 голос
/ 06 мая 2020

Рассмотрим настройку виртуальной машины с async_threads

...