View Source ActiveMemory.Operations (ActiveMemory v0.8.0)
The shared implementation of the table operations and store setup used by
ActiveMemory.Store and ActiveMemory.ActiveRepo.
Every function takes the table module explicitly and dispatches to that table's
configured adapter, applying the common validation, uuid handling, seeding and
before_init logic. This keeps the single-table Store and the multi-table
Repo sharing one implementation rather than duplicating it.
Link to this section Summary
Functions
Get every record in table, optionally ordered and paged.
Run the before_init methods for a store.
Count the records in table without reading them.
Create table in its configured backend.
Delete the record provided.
Delete every record in table, leaving the table itself in place.
Delete every record in table whose expires_at is at or before now
(milliseconds). Used by the Store/ActiveRepo sweep to reclaim memory; reads
already hide expired records, so this is only about freeing them.
Whether any record in table matches the query.
Like get/2 but raises ActiveMemory.NotFoundError when there is no such record.
Get the record whose primary key is key.
Like get_by/2 but raises ActiveMemory.NotFoundError when nothing matches.
Get the single record matching an attributes map.
Like one/2 but raises ActiveMemory.NotFoundError when nothing matches.
Get one record matching an attributes map or a match query. An expired record
is treated as {:error, :not_found}.
Sort and page a list of records.
Like reload/2 but raises ActiveMemory.NotFoundError when the record is gone.
Re-read struct from table by its primary key.
Schedule the calling process's next expiry sweep when any of tables uses a
ttl, and do nothing when none of them do.
Evaluate seed_file and write its records to table.
Get all records matching an attributes map or a match query, optionally ordered
and paged. See order/2 for the options.
Delete every expired record from each of tables that uses a ttl.
Get one record matching the query, delete it, and return it. An expired record
is treated as {:error, :not_found}.
Write a record to table.
Link to this section Functions
Get every record in table, optionally ordered and paged.
See order/2 for the :order_by, :limit and :offset options.
Run the before_init methods for a store.
spec is :default, a single {method, args} tuple, or a list of such tuples.
module is the module the methods are defined on (the Store or Repo).
@spec count( atom(), keyword() ) :: non_neg_integer()
Count the records in table without reading them.
The count comes from the table itself (:ets.info/2, :mnesia.table_info/2), so
it is O(1) and does not copy records out of the table. It is the size of the
backend table, not a count of records matching any application level rule:
- On a table with a
ttlit includes records that have expired but have not been swept yet, so it can exceed what the reads return. Passsweep: trueto delete those first and get a count that agrees with the reads, at the cost of a full pass over the table. - On a replicated Mnesia table it is the size of the replica this node reads from.
Nodes whose replicas have diverged — see the
majorityoption inActiveMemory.Table— will report different counts.
Create table in its configured backend.
Called by a Store or ActiveRepo as it starts. Returns {:ok, :created} for a
new table, or {:ok, :recovered} when an existing one was reclaimed — an ETS table
held by ActiveMemory.TableHeir across a crash, or a Mnesia table that was already
loaded.
The table's configuration is validated first, so a schema that cannot work raises
here rather than misbehaving later: a ttl with no expires_at field, a primary
key ActiveMemory cannot generate or that is not the table key, or a composite
primary key.
Delete the record provided.
The record is matched in full by the adapter (:ets.delete_object/2,
:mnesia.delete_object/3), so a struct that has diverged from the stored copy
removes nothing and still returns :ok — deleting is idempotent and never
reports whether a record was present. withdraw/2 is the query based,
atomic alternative that returns {:error, :not_found} when nothing matched.
Returns :ok for a struct matching table or for nil, and
{:error, :bad_schema} when the struct does not match table.
Delete every record in table, leaving the table itself in place.
Returns :ok. This is not transactional with respect to concurrent writes: a
record written while the clear is in flight may survive it.
Delete every record in table whose expires_at is at or before now
(milliseconds). Used by the Store/ActiveRepo sweep to reclaim memory; reads
already hide expired records, so this is only about freeing them.
Whether any record in table matches the query.
Unlike count/2 this is a scan, not an indexed lookup: a query has to be matched
against every record's fields. It is a convenience over select/3, not a cheap
existence check. Accepts sweep: true to reclaim expired records first; the answer
itself is unaffected, since reads already ignore an expired record.
Like get/2 but raises ActiveMemory.NotFoundError when there is no such record.
Get the record whose primary key is key.
The primary key is the table's first field — :uuid on a table using
auto_generate_uuid: true, an Ecto schema's declared key, or the first field
declared. Returns {:error, :not_found} when there is no such record.
Like get_by/2 but raises ActiveMemory.NotFoundError when nothing matches.
Get the single record matching an attributes map.
Raises ActiveMemory.MultipleResultsError when more than one record matches, as
Ecto.Repo.get_by/3 does.
Like one/2 but raises ActiveMemory.NotFoundError when nothing matches.
Get one record matching an attributes map or a match query. An expired record
is treated as {:error, :not_found}.
Raises ActiveMemory.MultipleResultsError when the query matches more than one
record, mirroring Ecto.Repo.one/2. Use select/3 when many records are
expected.
Sort and page a list of records.
Neither ETS nor Mnesia can order a result for us, so this sorts in the caller
after reading — O(n log n) over the matched records, not an index backed sort.
:limit and :offset are convenience pagination, not indexed pagination: every
matched record is read and sorted first, and the offset records are then thrown
away, so offset: 10_000, limit: 10 pays for all 10,010.
Options:
:order_bya field,{:asc | :desc, field}, or a list of either to break ties:offsetrecords to drop after ordering:limitrecords to keep after the offset
Values are compared with the struct's own compare/2 when it has one, so
Decimal, DateTime, NaiveDateTime, Date and Time fields order correctly
rather than by Erlang term order.
Like reload/2 but raises ActiveMemory.NotFoundError when the record is gone.
Re-read struct from table by its primary key.
Reads and writes match a record in full, so a struct held across a change can go
stale. reload/2 gets the current copy. Returns {:error, :not_found} when the
record is gone.
Schedule the calling process's next expiry sweep when any of tables uses a
ttl, and do nothing when none of them do.
The ttl lookup happens here, at runtime, rather than while a Store or
ActiveRepo compiles. Reading it at compile time would make every table a
compile time dependency of its store, which breaks tooling that compiles the
store's file without the table module loaded.
Evaluate seed_file and write its records to table.
A nil seed_file is a no-op. Returns {:ok, :seed_success} or
{:error, reason}.
Get all records matching an attributes map or a match query, optionally ordered
and paged. See order/2 for the options.
Returns {:error, :bad_select_query} for any other query shape.
Delete every expired record from each of tables that uses a ttl.
Tables without a ttl are skipped, so a repo holding a mix of both only pays
for the ones that expire.
Get one record matching the query, delete it, and return it. An expired record
is treated as {:error, :not_found}.
@spec write(map() | Ecto.Changeset.t(), atom()) :: {:ok, map()} | {:error, Ecto.Changeset.t() | any()}
Write a record to table.
Takes a struct or an Ecto.Changeset. A valid changeset is applied and its
struct written; an invalid one returns {:error, changeset} with the changeset's
action set to :insert, mirroring Ecto.Repo.insert/2 so a Phoenix form
renders the errors.
Any field the table declares as autogenerated and which is still nil is
populated: a uuid attribute, or an Ecto schema's autogenerated primary key and
timestamps(). When the table has a ttl the record's expires_at is stamped
from the current time. Returns {:error, :bad_schema} when the struct does not
match table.