Elixir: ошибка аргумента при доступе к ETS из задачи Elixir

В настоящее время я пытаюсь создать клон Redis в Эликсире. В рамках этих усилий я использую задачу для обработки запроса на получение/установку и использую ETS для хранения ключей и значений.

** (ArgumentError) argument error
    (stdlib 3.13.2) :ets.insert(:kv, {"doomspork", "Sean", ["Elixir", "Ruby", "Java"]})

Вот шаги, которые я предпринял с соответствующими разделами кода:

  1. Я создал общедоступную таблицу ETS
 def init(_) do
    :ets.new(:kv, [:set, :public, :named_table])
  end
  1. В соответствии с примером в Elixir School я пытаюсь вставить и прочитать из таблицы ETS
 :ets.insert(:kv, {"doomspork", "Sean", ["Elixir", "Ruby", "Java"]})
    IO.inspect(:ets.lookup(:kv, "doomspork"))
  1. К сожалению, это дает мне ошибку, описанную здесь:
[error] Task #PID<0.159.0> started from #PID<0.156.0> terminating
** (ArgumentError) argument error
    (stdlib 3.13.2) :ets.insert(:kv, {"doomspork", "Sean", ["Elixir", "Ruby", "Java"]})
    (redis 1.0.0) lib/server.ex:61: Server.write_line/2
    (redis 1.0.0) lib/server.ex:41: Server.serve/1
    (elixir 1.11.0) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
    (stdlib 3.13.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
Function: #Function<0.28410304/0 in Server.loop_acceptor/1>
    Args: []

Я надеялся понять, почему возникает ошибка или как получить предложения о том, как я могу отладить эту проблему. Любая помощь будет глубоко оценена.

Я перечислил весь файл .ex для справки. Общие отзывы о коде в целом также приветствуются — я относительно новичок в Эликсире и хотел бы услышать ваши предложения, чтобы я мог его улучшить.


defmodule Server do
  @moduledoc """
  Your implementation of a Redis server
  """
  use Application
  require Logger

  def start(_type, _args) do
    Supervisor.start_link(
      [{Task.Supervisor, name: Server.TaskSupervisor}, {Task, fn -> Server.listen() end}],
      strategy: :one_for_one
    )
  end

  def init(_) do
    :ets.new(:kv, [:set, :public, :named_table])
  end

  @doc """
  Listen for incoming connections
  """
  def listen() do
    {:ok, socket} = :gen_tcp.listen(6379, [:binary, active: false, reuseaddr: true])
    Logger.info("Accepting connections on 6379")
    loop_acceptor(socket)
  end

  defp loop_acceptor(socket) do
    {:ok, client} = :gen_tcp.accept(socket)
    {:ok, pid} = Task.Supervisor.start_child(Server.TaskSupervisor, fn -> serve(client) end)
    :ok = :gen_tcp.controlling_process(client, pid)
    loop_acceptor(socket)
  end

  defp serve(client) do
    client
    |> read_line()
    |> write_line(client)

    serve(client)
  end

  defp read_line(socket) do
    {:ok, data} = :gen_tcp.recv(socket, 0)
    data
  end

  defp echo(socket, command_arr) do
    echo_statement = Enum.at(command_arr, -2)
    IO.inspect(echo_statement)
    :gen_tcp.send(socket, "+#{echo_statement}\r\n")
  end
  defp write_line(line, socket) do
    command_arr = String.split(line, "\\r\\n")
    command = Enum.at(command_arr, 2) |>String.downcase
    IO.inspect(command_arr)
    IO.inspect(command)
    :ets.insert(:kv, {"doomspork", "Sean", ["Elixir", "Ruby", "Java"]})
    IO.inspect(:ets.lookup(:kv, "doomspork"))
    case command do
      "ping" -> :gen_tcp.send(socket, "+PONG\r\n")
      "set"-> :ets.insert(:kv, {"samplekey", "sampleresp"})
      "get"-> :ets.lookup(:kv, "samplekey")
      "echo"-> echo(socket,command_arr)
      _ -> :gen_tcp.send(socket, "Nah")
    end
  end
end

person Joel Lee    schedule 11.12.2020    source источник


Ответы (1)


Вы пытаетесь получить доступ к ETS до того, как он был создан. Обратный вызов init/1 вызывается внутри процесса после его запуска, но первая попытка доступа происходит из задачи, запущенной start, что может произойти раньше. В вашем случае это Application, который вообще не имеет обратного вызова init/1.

Просто переместите :ets.new(:kv, [:set, :public, :named_table]) в start/2 прежде всего.

person Aleksei Matiushkin    schedule 11.12.2020