Why use ETS? When not to use it?

You could simulate the complete interface of the :ets module with GenServer. However, such implementation would be very inefficient in some cases.

A typical example is in memory k-v. If you do this with a single process (GenServer or Agent), then all access is serialized. If you have thousands of client processes interacting with such k-v, operations are performed one at a time.

In contrast, an ETS powered table with proper knobs turned on (:public, :read_concurrency, :write_concurrency) will allow different clients to interact with the same table simultaneously. Multiple processes can issue reads and writes to the same table at the same time, and the operations can be performed simultaneously, unless both processes are writing to the same row.

An example of this in practice is Registry, which is powered by ETS tables. This ensures that client processes can quickly find desired processes, and even get some of their properties, without needing to message some process.

The thing @rvirding and @Nilithus mentioned about avoiding large process heap is also an interesting feature. ETS data is “off-heap”, so it doesn’t put any pressure on GC. If you have a single process with a large, and frequently changing, active memory set, ETS table might improve your performance significantly.

So basically, ETS is mostly an optimization technique. If your needs are simple, you can probably start with a GenServer, and consider ETS if you find performance problems.

26 Likes