How you can use :ets.give_away

The simplest possible example of using :ets.give.away to save a table if the table owner’s process crashes.

Add genservers to the supervisor’s tree

{UsersEtsTableBackup, []},
{UsersEtsTable, []}

UsersEtsTableBackup - is needed only to create a table and transfer the table and the rights to use it to the working process and also gets the table back if the working process crashes

defmodule UsersEtsTableBackup do
use GenServer

######## Client API #########################################################

def give_me_ets(data), do: GenServer.call(__MODULE__, data)

######## Server callbacks ###################################################

def start_link([]), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)

def init(state) do
      with :undefined <- :ets.whereis(:users) do
         :ets.new(:users, [:set, :protected, :named_table, {:heir, self(), {}}])
      else
        _ -> UsersEtsTable.set_me_heir(:set_me_heir)
      end

    {:ok, state}
end
#############################################################################

def handle_call(:give_me_ets, {pid, _}, state) do
      :ets.give_away(:users, pid, {})
    {:reply, :ok, state}
end

def handle_call(_, _from, state) do
    {:reply, :ok, state}
end

#############################################################################

def handle_cast(_, state) do
    {:noreply, state}
end

#############################################################################

def handle_info({:"ETS-TRANSFER", :users, _, {}}, state) do
    {:noreply, state}
end

def handle_info(_, state) do
    {:noreply, state}
end

#############################################################################

end

UsersEtsTable - work process, gets the table and rights from UsersEtsTableBackup and works with it

defmodule UsersEtsTable do
use GenServer

######## Client API #########################################################

def set_me_heir(data), do: GenServer.call(__MODULE__, data)

######## Server callbacks ###################################################

def start_link([]), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)

def init(state), do: {:ok, state, {:continue, :give_me_ets}}

#############################################################################

def handle_call(:set_me_heir, {pid, _}, state) do
      :ets.setopts(:users, {:heir, pid, {}})
    {:reply, :ok, state}
end

def handle_call(_, _from, state) do
    {:reply, :ok, state}
end

#############################################################################

def handle_cast(_, state) do
    {:noreply, state}
end

#############################################################################

def handle_continue(:give_me_ets, state) do
    UsersEtsTableBackup.give_me_ets(:give_me_ets)
    {:noreply, state}
end

def handle_continue(_, state) do
    {:noreply, state}
end

#############################################################################

def handle_info({:"ETS-TRANSFER", :users, _, {}}, state) do
    {:noreply, state}
end

def handle_info(_, state) do
    {:noreply, state}
end

#############################################################################

end

1 Like

While I don’t see anything wrong on a quick glance, I question why do you need this code at all.

Using a supervised process in your application’s tree is the safest way to have a persistent owner of an ETS table. It does not need to do anything else; it should just be there owning the table(s) and should not react to any messages.

If such a process crashes then you have much bigger problems i.e. OTP itself has a bug which is extremely unlikely on that particular code path.

So your code, while not looking to be wrong, is to me unnecessary. Drop the paranoia. If you make one GenServer that initializes by creating ETS tables and registering itself as their owner and then does nothing else, while staying as a part of your application’s supervision tree, then you will have no issues with crashes. No need for backup owner, no need for heirs or giving ETS tables away.

If there is a function in a language, then someone needs it.
The process that creates the table does not have to be a genserver, this is just an example.
Processes in elixir are easy and cheap to use and data is always important.

Regards

P.S Paranoia is a keen sense of reality o_O