Reducing Database Load with Elixir ETS for Transient State
Learn how to use Elixir ETS (Erlang Term Storage) to reduce database load by caching transient state in-memory with O(1) lookup performance.
23 Sept 2026, 02:56 UTC

The Cost of Constant Database Roundtrips
Many Elixir applications rely on a database for every piece of state, from user session data to temporary API rate limits. While this ensures persistence, it introduces latency and puts unnecessary pressure on the database connection pool for data that only needs to live for a few minutes or hours.
The solution for this transient state is ETS (Erlang Term Storage). ETS is an in-memory storage system that allows Elixir processes to share data without the overhead of message passing or external network calls. By moving short-lived state into ETS, you can achieve O(1) lookup speeds and significantly reduce your database IOPS.
Choosing the Right Table Type
Before creating a table, you must decide how you intend to access the data. ETS provides three primary table types, each with different performance characteristics:
:set: The most common type. Each key must be unique. It is optimized for fast lookups and updates.:bag: Allows multiple values for the same key. This is ideal for logging events or counting occurrences where you need to retrieve a list of all values associated with a specific identifier.:ordered_set: Maintains keys in a sorted order. This is useful for range queries, though it comes with a performance penalty on insertions compared to a standard:set.
Implementing a Session Cache
A common pattern is using a GenServer to manage the lifecycle of an ETS table. This ensures the table is created when the application starts and provides a controlled interface for data access.
Below is a configuration for a simple session manager. Run this in a module within your project or via iex. Note that :public tables can be accessed by any process on the node, removing the need to pass a table identifier around.
defmodule SessionCache do
use GenServer
def start_link(_opts) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init(_) do
# Create a public :set table named :user_sessions
# :public allows other processes to call ets.lookup/2 directly
:ets.new(:user_sessions, [:set, :public, :named_table])
{:ok, nil}
end
# Wrapper to insert a session
def put_session(user_id, data) do
:ets.insert(:user_sessions, {user_id, data})
end
# Wrapper to retrieve a session
def get_session(user_id) do
case :ets.lookup(:user_sessions, user_id) do
[{^user_id, data}] -> data
[] -> nil
end
end
def handle_call(_msg, _from, state), do: {:reply, :ok, state}
def handle_cast(_msg, state), do: {:noreply, state}
end
Verification and Diagnostics
To verify the implementation, you can run the following commands in your iex shell:
- Start the manager:
SessionCache.start_link([]) - Store data:
SessionCache.put_session(123, %{name: "Alice", role: "admin"}) - Retrieve data:
SessionCache.get_session(123)
If the lookup returns nil, check if the GenServer crashed; if the process creating the table terminates and the table was not marked as :public, the data is wiped instantly.
The Trade-offs of In-Memory Storage
ETS is powerful, but it is not a replacement for a database. The most critical limitation is volatility. Because ETS resides in RAM, all data is lost if the BEAM VM restarts or the node crashes. You should only store data in ETS that can be easily reconstructed from a primary data store or data that is truly ephemeral.
Additionally, ETS is node-local. In a distributed cluster, a table on Node A is not visible to Node B. If your application is scaled across multiple servers, you will need a strategy to synchronize state (such as using a distributed cache like Redis) or use sticky sessions to ensure a user always hits the same node.
Actionable Summary
When deciding whether to use ETS, ask if the data is transient and node-specific. If the answer is yes, move that state out of your database and into a :public ETS table. This reduces latency and prevents your database from becoming a bottleneck during traffic spikes.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.