This week's LFE Friday was translated with permission from the Erlang Thursday series by Steven Proctor. This week's translator: Robert Virding.

Today's LFE Friday 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.

> (set table (ets:new 'some-name ()))
> (ets:info table)
(#(read_concurrency false)
 #(write_concurrency false)
 #(compressed false)
 #(memory 305)
 #(owner <0.28.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.

> (set fun (lambda () (receive (after 'infinity 'ok))))
> (set some-process (spawn fun))

We create a new ETS table and specify it is protected, and we also specify that it is a named_table as a bonus.

> (ets:new 'protected-named-ets '(protected named_table))

The result of that evaluation 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))

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 some-process ())

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: badarg
  in (: ets insert protected-named-ets #(barbaz foo))

An exception, and it is of type badarg, 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: badarg
  in (: 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.

(ets:new 'public-named-ets '(public named_table))

We will do an insert and a match from our current process, which is the owner.

> (ets:insert 'public-named-ets #(foo bar))
> (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 some-process and try to read and write.

> (ets:give_away 'public-named-ets some-process ())

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))
> (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.

> (ets:new 'private-named-ets '(private named_table))

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))
> (ets:match 'private-named-ets '$1)
((#(fizz buzz)))

Time to give this table away to some-process again.

> (ets:give_away 'private-named-ets some-process ())

Now that the ETS table is owned by a different process, time to try a read.

> (ets:match 'private-named-ets '$1)
exception error: badarg
  in (: ets match private-named-ets $1)

badarg 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: badarg
  in (: ets insert private-named-ets #(buzz fizz))

A badarg 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, Robert



22 December 2015