elixir - Initialize ETS cache using GenServer -
i learning ets
, genserver
, trying initialize cache when app starts. it's quite possible designing incorrectly leading issue describe below, feedback on helpful.
when app initializes, :ets
table created via worker
.
def start_link genserver.start_link(__module__, :ok) end def init(:ok) tab = :ets.new(:my_table, [:set, :named_table]) :ets.insert(:my_table, {1, "one"}) {:ok, tab} end def lookup(key) :ets.lookup(:my_table, key) end iex(1)> myapp.datatable.lookup(1) [{1, "one"}]
so far good...but want update table. add call
:
def add genserver.call(self(), :add) end def handle_call(:add, _from, tab) tab = :ets.insert(:my_table, {2, "two"}) {:reply, lookup(2), tab} end iex(1)> myapp.datatable.add ** (exit) exited in: genserver.call(#pid<0.157.0>, :add, 5000) ** (exit) process attempted call (elixir) lib/gen_server.ex:598: genserver.call/3
if try modify call
function genserver.call(:my_table, :add)
or genserver.call(__module__, :add)
, error: ** (exit) no process
. obviously, i'm doing wrong call
.
so try directly update :ets
table:
def add_direct :ets.insert(:my_table, {2, "two"}) end iex(1)> myapp.datatable.add_direct ** (argumenterror) argument error (stdlib) :ets.insert(:my_table, {2, "two"}) (my_app) lib/my_app/data_table.ex:17: myapp.datatable.add_direct/0
when run :ets.all()
, can see :my_table
. resort trying update in iex
:
iex(2)> :ets.insert(:my_table, {2, "two"}) ** (argumenterror) argument error (stdlib) :ets.insert(:my_table, {2, "two"})
just make sure not entirely crazy, run sanity check work:
iex(2)> :ets.new(:my_table2, [:set, :named_table]) :my_table2 iex(3)> :ets.insert(:my_table2, {2, "two"}) true
i must going wrong in server callback , fundamental misunderstanding of how :ets
works inside modules.
there multiple problems this. i'll try explain each one:
iex(1)> myapp.datatable.add
** (exit) exited in: genserver.call(#pid<0.157.0>, :add, 5000)
** (exit) process attempted call itself
(elixir) lib/gen_server.ex:598: genserver.call/3
this because you're calling genserver method on self. should call on pid returned start_link
.
if try modify call function genserver.call(:my_table, :add) or genserver.call(module, :add), error: ** (exit) no process.
the first 1 fails because :my_table
not registered genserver name. second 1 fails because you're not registering genserver name.
so try directly update :ets table:
this because ets tables default not allow except process created table write table. can make table public passing :public
option :ets.new
's last argument. allow process write table.
there many ways fix this. 1 accept pid in add
:
def add(pid) genserver.call(pid, :add) end
and call like:
iex(1)> {:ok, pid} = a.start_link {:ok, #pid<0.86.0>} iex(2)> a.add(pid) 1 [{2, "two"}]
another solution register process name when create it:
def start_link genserver.start_link(__module__, :ok, [name: __module__]) end
and use __module__
in add
:
def add genserver.call(__module__, :add) end
iex(1)> a.start_link {:ok, #pid<0.86.0>} iex(2)> a.add 1 [{2, "two"}]
registering process name means cannot register process same name while first 1 alive, that's fine here since you're using fixed ets table name, uniquely named.
Comments
Post a Comment