Elixir ETS Ownership
ETS ownership is the rule that one process owns a table and controls its life. Ownership can move to another process while the rows stay in place.
The creator owns the table
A new table reports its owner as the creating process. A lookup reads a row the owner stored.
table = :ets.new(:owned, [:ordered_set, :public])
:ets.insert(table, [{1, "one"}, {2, "two"}])
IO.inspect(:ets.info(table, :owner) == self())
IO.inspect(:ets.lookup(table, 1))
:ets.delete(table)Give a table to another process
A spawned receiver waits for the transfer message. After the handover, the old owner no longer owns the table.
parent = self()
receiver = spawn(fn ->
receive do
{:"ETS-TRANSFER", tid, _from, gift} ->
IO.inspect(gift)
IO.inspect(:ets.info(tid, :owner) == self())
send(parent, {:took, gift})
receive do
:stop ->
:ets.delete(tid)
send(parent, :cleaned)
end
end
end)
table = :ets.new(:handover, [:set, :public])
:ets.insert(table, {:key, "value"})
:ets.give_away(table, receiver, :for_you)
receive do
{:took, gift} -> IO.inspect(gift)
end
IO.inspect(:ets.info(table, :owner) == self())
send(receiver, :stop)
receive do
:cleaned -> IO.puts("cleaned")
endThe new owner controls writes on a protected table
A protected table lets any process read but only the owner write. After the handover, the new owner adds a row the old owner can read.
parent = self()
receiver = spawn(fn ->
receive do
{:"ETS-TRANSFER", tid, _from, _gift} ->
:ets.insert(tid, {3, "three"})
send(parent, {:added, :ets.lookup(tid, 3)})
receive do
:stop ->
:ets.delete(tid)
send(parent, :cleaned)
end
end
end)
table = :ets.new(:guarded, [:ordered_set, :protected])
:ets.insert(table, [{1, "one"}, {2, "two"}])
:ets.give_away(table, receiver, :yours)
receive do
{:added, row} -> IO.inspect(row)
end
IO.inspect(:ets.lookup(table, 3))
send(receiver, :stop)
receive do
:cleaned -> IO.puts("cleaned")
endExample
parent = self()
worker = spawn(fn ->
receive do
{:"ETS-TRANSFER", tid, _from, _gift} ->
:ets.insert(tid, [{3, "three"}])
send(parent, :ready)
receive do
:stop ->
:ets.delete(tid)
send(parent, :cleaned)
end
end
end)
table = :ets.new(:shared, [:ordered_set, :public])
:ets.insert(table, [{1, "one"}, {2, "two"}])
:ets.give_away(table, worker, :yours)
receive do
:ready -> IO.inspect(:ets.tab2list(table))
end
send(worker, :stop)
receive do
:cleaned -> IO.puts("cleaned")
endKey points
- Owner: the process that created a table and decides when it ends.
- Use :ets.info(table, :owner) to check which process owns a table.
- Use :ets.give_away/3 to hand a table to another process with a gift value.
- The new owner receives a transfer message naming the table and the gift.
- Ownership moves the control of the table, not the rows.
In this example, the parent creates a table with two rows and hands it to a worker with a gift value. The worker adds a third row and reports ready, and the parent reads the full table in key order. The parent then stops the worker, which deletes the table and reports cleaned.
Note
a table ends when its owner ends, unless ownership moved first.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
After :ets.give_away(table, worker, :yours), which process owns the table?