View Source ActiveMemory.ActiveRepo (ActiveMemory v0.7.0)
The ActiveRepo
An ActiveRepo manages multiple ActiveMemory.Tables from a single process. It
is the multi-table counterpart to ActiveMemory.Store (which manages a single
table), giving you one supervised entry point and a unified API over many tables.
It is named
ActiveReporather thanReposo it does not collide with an application'sEcto.Repowhile keeping the familiar "repo" terminology.
defmodule MyApp.ActiveRepo do
use ActiveMemory.ActiveRepo,
tables: [
MyApp.People.Person,
{MyApp.Dogs.Dog, seed_file: Path.expand("dog_seeds.exs", __DIR__), before_init: [{:warm, []}]}
]
endAdd the ActiveRepo to your supervision tree like any other process:
children = [MyApp.ActiveRepo]Tables may freely mix :ets and :mnesia adapters; each operation dispatches to
the adapter configured on the given table.
activerepo-api
ActiveRepo API
Reads and withdraw take the table module as the first argument; writes and
deletes infer the table from the struct.
ActiveRepo.all/1Get all records stored in a tableActiveRepo.delete/1Delete the record providedActiveRepo.delete_all/1Delete all records stored in a tableActiveRepo.one/2Get one record from a table matching an attributes search ormatchqueryActiveRepo.select/2Get all records from a table matching an attributes search ormatchqueryActiveRepo.withdraw/2Get, delete and return one record from a tableActiveRepo.write/1Write a record into its table
An operation for a struct or table that is not part of the ActiveRepo returns
{:error, :unknown_table}.
concurrency
Concurrency
Like a Store, an ActiveRepo is a GenServer, but the data functions above are
not routed through that process and are not serialized by it. They run in
the caller's process and delegate straight to each table's adapter, so reads
and writes execute with ETS/Mnesia concurrency — the single GenServer is not a
bottleneck. Only lifecycle and metadata operations (init, state/0,
reload_seeds/1) use the GenServer.
These functions live on the GenServer module purely for organization: the
ActiveRepo is the single place responsible for how the application talks to its
tables, following the Single Responsibility Principle. See the
S.T.O.N.E principles for
the broader design philosophy.
tables-and-per-table-options
Tables and per-table options
Each entry of tables: is either a table module or a {table, opts} tuple. The
supported per-table options mirror the single-table ActiveMemory.Store:
seed_filea path to a seed file auto loaded when the table is first createdbefore_initmethods (defined on theActiveRepo) run during the table's setup
expiry-ttl
Expiry (TTL)
Any table whose ActiveMemory.Table declares a ttl expires its records
automatically: reads never return an expired record, and the ActiveRepo
periodically sweeps expired records from every ttl table it owns to reclaim
memory. The sweep cadence defaults to one minute and can be set with the
sweep_interval option (milliseconds). Tables without a ttl are left untouched,
and the sweep is only scheduled when at least one table uses a ttl.
initial-state
Initial State
Like a Store, an ActiveRepo is a GenServer with state. The default state is:
%{started_at: "date time when first started", tables: [MyApp.People.Person, ...]}Supply a {method, args} tuple to the initial_state keyword to override it; the
method must return {:ok, new_state}.
resilience
Resilience
ETS tables created by an ActiveRepo get the same ActiveMemory.TableHeir
protection as a Store: they survive an ActiveRepo crash and are reclaimed on
restart, and seed files are not re-run on recovery. See ActiveMemory.Store for
the before_init recovery caveat, which applies here as well.