ETS-backed storage for Botica.Flags.Flag structs.
Architecture
- The ETS table
:botica_flagsis:publicandread_concurrency: trueso reads are O(1) and lock-free, even under heavy concurrent load. The:publicchoice is deliberate: it lets callers read directly without bouncing through the GenServer, which is the whole point of having an ETS backend. Mutations still go through the GenServer (put/1,delete/1) so the table stays consistent. - The GenServer is also where flag lifecycle events (defined, enabled, disabled, rollout-changed) are emitted via telemetry.
Usage
In most cases you should not call this module directly — use the
Botica.Flags facade instead. Direct access is allowed when you need
the cheapest possible read and you're certain the Store is up:
iex> Botica.Flags.Store.get(:my_flag)
{:ok, %Botica.Flags.Flag{...}}
iex> Botica.Flags.Store.all()
[%Botica.Flags.Flag{...}, ...]
Summary
Functions
Direct ETS read of all flags. Returns a list (may be empty), sorted
by updated_at descending so most recently touched flags appear first.
Returns a specification to start this module under a supervisor.
Total number of registered flags. Cheap ETS count.
Remove a flag from the registry. 5s timeout — see put/1 for rationale.
Direct ETS read — no GenServer round-trip. Returns {:ok, flag} or :error.
GenServer-mediated write. Serialised to avoid race conditions between concurrent definitions / enable / disable / set calls. 5s timeout is generous for an in-memory ETS write and surfaces a stalled server fast.
Starts the Botica.Flags.Store GenServer. Called automatically by
Botica.Application. Use start_link/1 from custom supervision
trees when you need to override the default application config.
Diagnostic snapshot: total writes since the GenServer started
plus the current ETS size. Useful for mix botica:config and
for debugging flag registration.
Returns the underlying ETS table name. Useful in tests for clearing the
registry between cases: Store.table() |> :ets.delete_all_objects().
Functions
@spec all() :: [Botica.Flags.Flag.t()]
Direct ETS read of all flags. Returns a list (may be empty), sorted
by updated_at descending so most recently touched flags appear first.
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec count() :: non_neg_integer()
Total number of registered flags. Cheap ETS count.
@spec delete(atom()) :: :ok
Remove a flag from the registry. 5s timeout — see put/1 for rationale.
@spec get(atom()) :: {:ok, Botica.Flags.Flag.t()} | :error
Direct ETS read — no GenServer round-trip. Returns {:ok, flag} or :error.
@spec put(Botica.Flags.Flag.t()) :: :ok
GenServer-mediated write. Serialised to avoid race conditions between concurrent definitions / enable / disable / set calls. 5s timeout is generous for an in-memory ETS write and surfaces a stalled server fast.
@spec start_link(keyword()) :: GenServer.on_start()
Starts the Botica.Flags.Store GenServer. Called automatically by
Botica.Application. Use start_link/1 from custom supervision
trees when you need to override the default application config.
@spec stats() :: %{writes: non_neg_integer(), count: non_neg_integer()}
Diagnostic snapshot: total writes since the GenServer started
plus the current ETS size. Useful for mix botica:config and
for debugging flag registration.
@spec table() :: :ets.tab()
Returns the underlying ETS table name. Useful in tests for clearing the
registry between cases: Store.table() |> :ets.delete_all_objects().