Reputation: 1327
I'm trying without much success to implement a really simple example around ETS. I want to have a number of worker
processes write to an ETS table, and then have a single (and different) reader process retrieve values periodically as a sum. I can't seem to insert into the table without a crash, and the reader returns zero when ever it's executed...here's my code, any help very gratefully received:
The Supervisor
module:
defmodule Stackex do
use Application
@noOfWriters 1
def start(_type, _args) do
import Supervisor.Spec, warn: false
Stackex.Reader.start_link
Stackex.Table.start_link
opts = [strategy: :one_for_one, name: Stackex.Supervisor]
children =
for i <- 1..@noOfWriters do
worker(Stackex.Writer, [i], id: i)
end
Supervisor.start_link(children, opts)
end
end
The Writer
module:
defmodule Stackex.Writer do
use GenServer
def start_link(id) do
GenServer.start_link(__MODULE__,{id})
end
def init({id}) do
state = %{writer_id: id, value: value}
schedule_work()
{:ok, state}
end
def handle_info(:update, state) do
Stackex.Table.add_kvp(state)
update = %{writer_id: state.writer_id, value: value}
schedule_work()
{:noreply, update}
end
defp value do
:random.seed(:erlang.now())
:random.uniform(10)
end
defp schedule_work() do
Process.send_after(self, :update, 1000)
end
end
The Table
module:
defmodule Stackex.Table do
use GenServer
def start_link do
GenServer.start_link(__MODULE__,[], name: __MODULE__)
end
def init do
{:ok, :ets.new(:table, [:set, :named_table])}
end
#Client API
def add_kvp(update) do
GenServer.cast __MODULE__, {:load, update}
end
def get_kvp_sum do
GenServer.call __MODULE__, {:sum}
end
###### Server Callback Functions ########
def handle_cast({:load, update}, state) do
%{writer_id: key, value: value} = update
{:noreply, :ets.insert(:table, {key, value})}
end
#Return total system load
def handle_call({:sum}, _from, state) do
sum = :ets.foldl(fn({{_,v}, acc}) -> v + acc end, 0, :table)
{:reply, sum, state}
end
end
The Reader
module:
defmodule Stackex.Reader do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init do
schedule_update
{:ok, nil}
end
def handle_info(:update) do
sum = Stackex.Table.get_kvp_sum
IO.puts("Sum #{sum}")
schedule_update
{:noreply, nil}
end
defp schedule_update do
Process.send_after(self, :update, 2000)
end
end
Upvotes: 1
Views: 868
Reputation: 4284
In answer to the question in the title, you can make a named ets table accessible to other processes by passing :public
in the options when you call :ets.new/2
But according to the code you posted you do not have multiple processes accessing your table. There is only one Stackex.Table
genserver started. Looks like you don't even need it to be a :named_table
since you can just pass the pid in your state
variable with very little change to your code.
Looks like your immediate problem is you are passing the wrong variable to you add_kvp
method (state
instead of update
).
Upvotes: 1