capri/projection
Define and query typed, derived views over repository records.
Khepri maintains projections in node-local ETS tables. They are ephemeral
query caches rather than authoritative state, and follower visibility may
lag. Cardinality is tracked in the type: only unique projections support
get, while all supports both unique and many-valued projections.
Types
A projection definition awaiting its globally unique ETS table name.
pub opaque type Builder(record, key, cardinality)
Projection construction, lifecycle, and lookup failures.
pub type Error {
StoreError(khepri_error.Error)
RuntimeError(reason: dynamic.Dynamic)
RepositoryMismatch(
expected_id: String,
expected_version: Int,
actual_id: String,
actual_version: Int,
)
InvalidData(errors: List(decode.DecodeError))
ProjectionUnavailable
UnexpectedProjectionRows(rows: dynamic.Dynamic)
}
Constructors
-
StoreError(khepri_error.Error) -
RuntimeError(reason: dynamic.Dynamic) -
RepositoryMismatch( expected_id: String, expected_version: Int, actual_id: String, actual_version: Int, ) -
InvalidData(errors: List(decode.DecodeError)) -
ProjectionUnavailable -
UnexpectedProjectionRows(rows: dynamic.Dynamic)
A projection cardinality that permits multiple records per key.
pub type Many {
Many
}
Constructors
-
Many
A typed, derived ETS view maintained by Khepri.
Projections are ephemeral caches, not authoritative repositories. They are immediately consistent on the writer and leader, and eventually consistent on other followers.
pub opaque type Projection(record, key, cardinality)
Values
pub fn all(
projection: Projection(record, key, cardinality),
key: key,
) -> Result(List(record), Error)
Read and decode all repository records for a projection key.
This works for both set and bag projections. Missing keys return an empty list; an unavailable projection remains an explicit error.
pub fn get(
projection: Projection(record, key, Unique),
key: key,
) -> Result(option.Option(record), Error)
Read exactly one projected repository record from the local ETS table.
Use all for bag projections. None means the key is absent;
ProjectionUnavailable means the local table does not currently exist.
pub fn many(
builder: Builder(record, key, Unique),
) -> Builder(record, key, Many)
Allow multiple repository records to share one projection key.
Bag projections are appropriate for one-to-many query views. Duplicate
encoded records are coalesced according to ETS bag semantics.
pub fn named(
builder: Builder(record, key, cardinality),
name: atom.Atom,
) -> Result(Projection(record, key, cardinality), Error)
Finish a projection definition with a bounded, globally unique atom.
Khepri uses this atom as a node-global named ETS table. Do not create names from unbounded or user-controlled input.
pub fn new(
repository: types.Repository(record),
extractor: fn(record) -> key,
) -> Builder(record, key, Unique)
Begin building a typed projection derived from a repository.
Khepri evaluates the extractor directly against decoded repository records.
pub fn rebuild(
store: types.StoreHandle,
prefix: types.Prefix(record),
projection: Projection(record, key, cardinality),
) -> Result(Nil, Error)
Unregister and synchronously register a projection again.
This is intended for application startup and rebuilds the view from existing repository records. The projection table is briefly unavailable between the two commands.
pub fn register(
store: types.StoreHandle,
prefix: types.Prefix(record),
projection: Projection(record, key, cardinality),
) -> Result(Nil, Error)
Register a projection over every direct record beneath a repository prefix.
Existing records are projected retroactively. The prefix must carry the same repository identity and version as the projection definition.
pub fn unregister(
store: types.StoreHandle,
projection: Projection(record, key, cardinality),
) -> Result(Nil, Error)
Idempotently unregister a projection and delete its local ETS table.
This is destructive only to the derived view; repository records are not removed.