Recently I tackled a problem which involved updating a large number of key values.
Naturally, I considered using a Map
, with operations like Map.put/3
.
However this seemed insufficient, given the immutable nature of data structures in Elixir:
iex> m = Map.put(%{}, :a, 1)
%{a: 1}
iex> Map.put(m, :b, 2)
%{a: 1, b: 2}
iex> m
%{a: 1}
I then solved the problem by holding the state of the Map
in a GenServer
, and updating it using handle_cast/3
calls.
Generally, is this the right approach, or was this too much here?
I then solved the problem by holding the state of the
Map
in aGenServer
[...] Generally, is this the right approach, or was this too much here?
It heavily depends on your goal. There are many different ways to store the state. Rebinding variables like:
m = Map.put(%{}, :a, 1)
#⇒ %{a: 1}
m = Map.put(m, :b, 2)
#⇒ %{a: 1, b: 2}
Does not store anything. It binds the local variable m
to RHO and as soon as the control flow leaves the scope, this variable becomes garbage collected. Whether you need the aforementioned map within a single scope, GenServer
(and other state holders) is an overkill.
OTOH, if you need to store the state for a long time and share it between different scopes (e. g. between different processes,) GenServer
is the simplest way to accomplish that. In Elixir we have Agent
module to decrease the boilerplate for GenServer
that is used as a simple in-memory storage, but my advice would be to always use GenServer
: sooner or later Agent
will become too tight for your purposes.
Also, one might use ets
module to keep in-memory key-value storage, shared between processes.
dets
is a way to store the state between process restarts.
And, finally, mnesia
is an OTP native approach to share the state between both restarts and different nodes (in distributed environment.)