Current section
Files
Jump to
Current section
Files
README.md
# kura_ets
ETS backend for [kura](https://github.com/Taure/kura) — an in-memory,
zero-dependency backend in the spirit of [etso](https://hex.pm/packages/etso)
(Ecto's ETS adapter), for tests, caches, and small in-memory datasets.
Unlike the SQL backends (`kura_postgres`, `kura_sqlite`), this backend does
not compile queries to SQL. The dialect wraps the portable `#kura_query{}`
AST into an opaque `{kura_ets, Plan}` term, and the driver interprets that
plan directly against per-table ETS tables.
## Usage
```erlang
%% rebar.config
{deps, [kura, kura_ets]}.
%% sys.config
[{kura, [
{repos, #{
my_repo => #{
backend => kura_backend_ets
}
}}
]}].
```
Everything else is standard kura:
```erlang
CS = kura_changeset:cast(my_user, #{}, #{id => 1, name => ~"alice"}, [id, name]),
{ok, Row} = kura_repo_worker:insert(my_repo, CS),
Q = kura_query:order_by(
kura_query:where(kura_query:from(my_user), {active, true}),
[{name, asc}]
),
{ok, Rows} = kura_repo_worker:all(my_repo, Q).
```
No migrations are needed (or possible): tables are created on first use and
data lives only as long as the pool. `kura_ets_pool:reset(PoolName)` clears
every table, which is handy between tests.
## Modules
| Module | Role |
|---|---|
| `kura_backend_ets` | Backend aggregator (`backend => kura_backend_ets`) |
| `kura_ets_pool` | `kura_pool` impl; gen_server-owned table registry |
| `kura_ets_driver` | `kura_driver` impl; executes plans |
| `kura_ets_dialect` | `kura_dialect` impl; emits `{kura_ets, Plan}` terms |
| `kura_ets_query` | Plan interpreter (filter / sort / project / aggregate) |
## Supported
- `insert` / `insert_all`, including `on_conflict` (`nothing`,
`replace_all`, `{replace, Fields}`) — conflict detection is on the
primary key only
- `get` / `get_by` / `one` / `all` / `exists` / `reload`
- Where conditions: `{F, V}`, `=`, `!=`, `<`, `>`, `<=`, `>=`, `in`,
`not_in`, composite-column `in`, `between`, `like`, `ilike`, `is_nil`,
`is_not_nil`, nested `and` / `or` / `not`
- `order_by`, `limit`, `offset`, `distinct`, field selection
- Primary-key point lookups short-circuit to `ets:lookup/2` (O(1); the
storage key is the primary key), including composite keys
- Aggregates: `count`, `sum`, `avg`, `min`, `max`
- `update` / `delete` by primary key, `update_all` / `delete_all`
- Soft delete (`deleted_at`), schema defaults, composite primary keys
- Attribute-based multitenancy
- Sandbox checkout (`kura_sandbox`)
## Not supported
Queries using these return `{error, {kura_ets_unsupported, _}}`; raw SQL
returns `{error, {kura_ets, raw_sql_unsupported}}`:
- Joins, `group_by` / `having`, CTEs, `union` / `intersect` / `except`,
subqueries, fragments, window functions, full-text `matches`, row locks,
prefix-based multitenancy
- Raw SQL — including migrations, `many_to_many` association persistence,
and optimistic locking (kura builds SQL strings directly for those)
- Transactions: `transaction/2` runs the fun without atomicity — there is
no rollback. The backend does not declare the `transactions` capability.
## SQL semantics notes
- Rows store kura-*dumped* values; comparison values in queries are dumped
through the schema's types before comparing, matching how a SQL client
encodes parameters.
- `NULL` (`undefined` / missing) never satisfies a comparison; only
`is_nil` / `is_not_nil` match it.
- Duplicate primary keys produce a PG-shaped `23505` unique-violation
error, so `kura_changeset` constraint mapping behaves as with SQL
backends.
## Development
```sh
rebar3 compile
rebar3 eunit
rebar3 fmt
```
kura is pulled from hex.pm. To develop against an unreleased local kura,
a `_checkouts/kura` symlink works, but note rebar3 then treats kura as a
project app and `rebar3 eunit` will also run kura's own test suite
(which needs a Postgres container) — scope with `rebar3 eunit
--app=kura_ets` in that case.
Guides in `docs/`:
Looking for caching? That is application policy, not backend
capability — see the sibling library
[kura_cache](https://github.com/longlene/kura_cache) (keyed memoization
with TTL, schema-keyed row cache, cluster invalidation hook).