Elixir:从 Elixir 任务访问 ETS 时出现参数错误
Elixir: Argument Error while accessing ETS from an Elixir Task
我目前正在尝试在 Elixir 中构建 Redis 克隆。作为这项工作的一部分,我正在使用一个任务来处理对 get/set 的请求,并且我正在使用 ETS 来存储键和值。
** (ArgumentError) argument error
(stdlib 3.13.2) :ets.insert(:kv, {"doomspork", "Sean", ["Elixir", "Ruby", "Java"]})
以下是我对相关代码部分执行的步骤:
- 我创建了一个 public ETS table
def init(_) do
:ets.new(:kv, [:set, :public, :named_table])
end
- 根据 example in Elixir School 我尝试插入和读取 ETS table
:ets.insert(:kv, {"doomspork", "Sean", ["Elixir", "Ruby", "Java"]})
IO.inspect(:ets.lookup(:kv, "doomspork"))
- 不幸的是,这给了我这里描述的错误:
[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 文件以供参考。也欢迎关于整个代码的一般反馈——我对 Elixir 比较陌生,很想听听你的建议,以便我可以改进。
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
您正在尝试访问创建之前的 ETS。 init/1
回调在进程启动后调用,但第一次访问尝试发生在 start
的任务 运行 中,这可能发生得更早。在您的情况下,这是一个 Application
,根本没有 init/1
回调 .
先将 :ets.new(:kv, [:set, :public, :named_table])
移动到 start/2
。
我目前正在尝试在 Elixir 中构建 Redis 克隆。作为这项工作的一部分,我正在使用一个任务来处理对 get/set 的请求,并且我正在使用 ETS 来存储键和值。
** (ArgumentError) argument error
(stdlib 3.13.2) :ets.insert(:kv, {"doomspork", "Sean", ["Elixir", "Ruby", "Java"]})
以下是我对相关代码部分执行的步骤:
- 我创建了一个 public ETS table
def init(_) do
:ets.new(:kv, [:set, :public, :named_table])
end
- 根据 example in Elixir School 我尝试插入和读取 ETS table
:ets.insert(:kv, {"doomspork", "Sean", ["Elixir", "Ruby", "Java"]})
IO.inspect(:ets.lookup(:kv, "doomspork"))
- 不幸的是,这给了我这里描述的错误:
[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 文件以供参考。也欢迎关于整个代码的一般反馈——我对 Elixir 比较陌生,很想听听你的建议,以便我可以改进。
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
您正在尝试访问创建之前的 ETS。 init/1
回调在进程启动后调用,但第一次访问尝试发生在 start
的任务 运行 中,这可能发生得更早。在您的情况下,这是一个 Application
,根本没有 init/1
回调 .
先将 :ets.new(:kv, [:set, :public, :named_table])
移动到 start/2
。