Public ETS / Shared across processes

Hi,

I’m trying to make some ETS table accessible from multiple processes and I don’t see what I’m doing wrong…

iex(1)> spawn fn -> :ets.new(:test, [:bag, :public, :named_table]) end
#PID<0.290.0>
iex(2)> :ets.tab2list(:test)                                          
** (ArgumentError) argument error
    (stdlib) :ets.match_object(:test, :_)
    (stdlib) ets.erl:763: :ets.tab2list/1

→ doesn’t work, while :

 iex(2)> :ets.new(:test, [:bag, :public, :named_table])                
:test
iex(3)> :ets.tab2list(:test)                          
[]
iex(4)> 

… works.

Cheers,

François

line 1 spawns a process so its work is happening asynchronously. This means that it’s possible fore line 2 to run before the ets table is actually created.

EDIT: @nicd also points out that the ets table could also be dead, which is another great point. Basically, the ets table is only alive for an incredibly brief period of time, and it’s quite possible that your tab2list will happen before or after that time.

3 Likes

The process you spawned is the owner of the ETS table. When the process ends (right after it created the table), the table will be deleted. You will need to keep a GenServer that keeps the table alive or transfer ownership to another process. You can also set an heir that inherits the table when the owning process dies.

5 Likes

Don’t Lose Your ets Tables

2 Likes

What others said, also if you want to just have a stupid simple global ETS table that lives as long as your VM lives here’s how to do that (don’t do that in production though):

  defp get_table() do
    if :ets.info(@table) == :undefined do
      :ets.new(@table, [:public, :named_table])
      :ets.give_away(@table, get_lively_pid(), nil)
      # avoid races
      Process.sleep(5)
      @table
    else
      @table
    end
  end

  defp get_lively_pid() do    
    spawn(fn -> Process.sleep(:infinity) end)
  end

you can then do stuff like :ets.first(get_table()) etc.

1 Like

Or if you want to put this into broader use then have one process which owns all the tables.

2 Likes

There are many wonderful suggestions but I’m curious why you need this ets table accessible from multiple processes. It sounds like using ets as a gasp global variable.

ets doesn’t support transactions, does read/write collisions matter ? Data in ets doesn’t survive a reboot nor is it distributed. Are these something you need ?

Usually after some prodding we realize that mnesia is a better fit.

2 Likes

Thank you all for your answers, I thought “:public” would be enough / I mixed access and process ownership.

As for why use ETS here, it is to do some RAM prefetching, before assembling data, in some context where transactions are not needed (this isn’t live data and data is heavily partitionned) so I think ETS is a good fit. It would also be a bit faster than in-memory Mnesia.

1 Like