Today’s Erlang Thursday continues the introduction to ETS and takes a look at the different access levels that ETS supports.
The different access levels that ETS supports are: public
, protected
, and private
.
Each of these different types can be passed in when creating a new ETS table, but let’s see what type of ETS table we get when we don’t specify an access level.
Table = ets:new(some_name, []). % 20501 ets:info(Table). % [{read_concurrency,false}, % {write_concurrency,false}, % {compressed,false}, % {memory,305}, % {owner,<0.81.0>}, % {heir,none}, % {name,some_name}, % {size,0}, % {node,nonode@nohost}, % {named_table,false}, % {type,set}, % {keypos,1}, % {protection,protected}]
So the default access level is protected
when not specified.
So what does it mean for a ETS table to be protected
then? The documentation states that protected
tables can be written to by only the owning process, but read by other processes.
So let’s see that at work then.
First let’s create a process that we can give ETS tables away to.
Fun = fun() -> receive after infinity -> ok end end. % #Fun<erl_eval.20.54118792> SomeProcess = spawn(Fun). % <0.58.0>
We create a new ETS table and specify it is protected
, and we also specify that it is a named_table
as a bonus.
ProtectedNamedETS = ets:new(protected_named_ets, [protected, named_table]). % protected_named_ets
The result of that match is protected_named_ets
and not a number like the call to ets:new/2
above, so we should be able to use the name of the table to access the table instead of just the identifier.
We will insert an entry into the ETS table, and we will use the name of the ETS table as the ETS table reference since we said the table is a named_table
.
ets:insert(protected_named_ets, {foobar, baz}). % true
ets:insert/2
returned true
so we should now have some data in the table. Let’s pull it out using ets:match/2
, and let’s match everything while we are at it by using a $1
for the pattern.
ets:match(protected_named_ets, '$1'). % [[{foobar,baz}]]
So as the owner process of the ETS table, since this was the process that created it, we can read an write to the table.
Now time to give our table away.
ets:give_away(protected_named_ets, SomeProcess, []). % true
Since the documentation says is is available for reads, we will do the same match
we just did before giving it away.
ets:match(protected_named_ets, '$1'). % [[{foobar,baz}]]
We get our results back.
What does a write look like then, since it says only the owning process has access to write, and the return value of calling ets:insert/2
is always true
.
ets:insert(protected_named_ets, {barbaz, foo}). % ** exception error: bad argument % in function ets:insert/2 % called as ets:insert(protected_named_ets,{barbaz,foo})
An exception, and it is of type bad argument
, which does hold that it doesn’t allow writes from non-owning processes, but doesn’t exactly make it clear that is what is happening.
How about if we see what we get if we try to call ets:insert/2
on a table that doesn’t exist?
ets:insert(no_such_table, {foo, bar}). % ** exception error: bad argument % in function ets:insert/2 % called as ets:insert(no_such_table,{foo,bar})
Same exception and same format of the error with just the name of the table and the tuple being different.
Thinking about this some, it does make sense that these two difference cases would be the same error. As far as the inserting process knows, there is no such table when trying to do an insert if no table exists, or if it is set to be protected
. Either way, the caller passed in a bad ETS table reference for the call to ets:insert/2
.
So we have now seen how protected
behaves, which is the default access level, so let’s take a look at public
next.
PublicNamedETS = ets:new(public_named_ets, [public, named_table]). % public_named_ets
We will do an insert and a match from our current process, which is the owner.
ets:insert(public_named_ets, {foo, bar}). % true ets:match(public_named_ets, '$1'). % [[{foo,bar}]]
All looks good there.
The documentation states that public
allows any process to read from and write to the table, so let’s give the public table away to SomeProcess
and try to read and write.
ets:give_away(public_named_ets, SomeProcess, []). % true
Now that we have given it away, time to try to add a new entry to the table, and see if we can read that write back out.
ets:insert(public_named_ets, {bar, baz}). % true ets:match(public_named_ets, '$1'). % [[{foo,bar}],[{bar,baz}]]
There we go. We have just inserted new data into that table, and when we do the ets:match/2
on everything, we see the new data in the result.
Now let’s create a private
table. The documentation states that for private
ETS tables, only the owner is allowed to read or write to the ETS table.
PrivateNamedETS = ets:new(private_named_ets, [private, named_table]). private_named_ets
Again, while this process still owns the table, we will add an item and do a read from the table.
ets:insert(private_named_ets, {fizz, buzz}). % true ets:match(private_named_ets, '$1'). % [[{fizz,buzz}]]
Time to give this table away to SomeProcess
again.
ets:give_away(private_named_ets, SomeProcess, []). % true
Now that the ETS table is owned by a different process, time to try a read.
ets:match(private_named_ets, '$1'). % ** exception error: bad argument % in function ets:match/2 % called as ets:match(private_named_ets,'$1')
bad argument
exception, just like the attempted ets:insert/2
we tried on the protected ETS table above when it was owned by a different process.
And time for a write.
ets:insert(private_named_ets, {buzz, fizz}). % ** exception error: bad argument % in function ets:insert/2 % called as ets:insert(private_named_ets,{buzz,fizz})
A bad argument
exception here as well, which should not be a surprise at this point, as both the protected write, and this private read both raised that same exception.
So in total, for this introduction so far, we have seen the Type, Access, Named Table, Heir, and Owner settings of an ETS table, and how they relate.
Next week, we will conclude the introduction of ETS by going over the Key Position option and the Tweaks that an ETS table can take when being setup.
–Proctor
Pingback: Erlang Thursday – ETS Introduction Part 5: keypos, compressed, read_conncurrency, and write_concurrencyProctor It | Proctor It