Store state, as a properties structure, externally to a process.
A process in Elixir stores data in its state that other processes may need -- a current status flag for instance. The standard way of sharing this data is to provide an API that results in a GenServer call. But, what if the GenServer is busy working on a long-running job? You could break your long running jobs into pieces and allow the GenServer to check its message queue. Yuck, cooperative multitasking and added complexity. You could create an ETS table or other external database record. Yuck, verbose, complex, and high-friction solution to what should be a simple problem.
ExternalState helps solve this problem by providing a clean way of stashing some or all of your state in a data structure managed by a different process.
Caveat lector: this works beautifully for named workers but doesn't work well with simple 1-for-1 workers because the state is managed using the module name.
defmodule MyGenserver do
use ExternalState, persist: false, props: [foo: true]
def init(:ok) do
init_ex_state() # external state is now at the defaults specified in use
end
# ...
def do_foo do
# ... something that sets foo to true ...
merge_ex_state(foo: true)
end
def undo_foo do
# ... something that sets foo to false ...
merge_ex_state(foo: false)
# or: merge_ex_state(%{foo: false})
end
def foo? do
get_ex_state().foo
end
end
The following are added to your module when you use
ExternalState:
@ex_state_struct
An atom name for your external state structuredefault_ex_state/0
Get a state structure with default values from propsinit_ex_state/0
Initialize your external state; must call once, multiple calls are okayget_ex_state/0
Get the current external state or nil if no init yetput_ex_state/1
Set the external state, returns the state or nil if no init yetmerge_ex_state/1
Update the external state with values from the parameter, which can be a keyword list of keys and values or a map. Returns the updated state or nil if no init yet.
If ExternalState is use
d with persist: true
, then the external state will
remain valid after the process that calls init_ex_state/0
exits. This
is the default.
The package can be installed by adding external_state
to your list of
dependencies in mix.exs
:
def deps do
[
{:external_state, "~> 1.0.6"}
]
end
The docs can be found at https://hexdocs.pm/external_state.