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

Popular posts from this blog

asynchronous - C# WinSCP .NET assembly: How to upload multiple files asynchronously -

aws api gateway - SerializationException in posting new Records via Dynamodb Proxy Service in API -

asp.net - Problems sending emails from forum -