AshOnetime.Cache.Ets (ash_onetime v0.6.0)

Copy Markdown View Source

A reference ETS adapter for the optional completed-response cache.

Bounded and TTL-aware. :ets has no native TTL, so each entry carries an expiry monotonic-time deadline and get/1 rejects expired entries (and lazily deletes them). A max-entries cap bounds steady-state memory; when the cap is reached put/3 evicts the single oldest-by-deadline entry before inserting, which keeps the hot set resident under churn without a background sweeper.

The adapter is NOT admission authority — AshOnetime validates every field of a returned entry against the authoritative PostgreSQL claim before using the payload (see AshOnetime.Cache). Treat every entry as untrusted.

Supervision

The ETS table is owned by a GenServer started via start_link/1. Add it to your supervision tree (it owns the table; if it terminates the table is destroyed, which is safe — the cache is optional and a miss falls through to PostgreSQL):

children = [
  # ...your repo...
  {AshOnetime.Cache.Ets, max_entries: 10_000}
]

Then configure it as the cache module:

config :ash_onetime, cache: AshOnetime.Cache.Ets

The table is named __MODULE__ (one ETS cache per VM). A multi-instance deployment (e.g. one cache per tenant prefix) wraps this module behind a per-instance module so each gets its own __MODULE__-named table; the reference adapter is single-instance by design.

Options

  • :max_entries — the bounded cap; put/3 evicts the oldest-by-deadline entry when the cap is reached before inserting (default 10_000). The cap bounds entry COUNT only — the per-entry byte ceiling is enforced upstream by AshOnetime.Cache (max_entry_bytes).

Limitations

This is a reference adapter tuned for simplicity and correctness, not throughput: the eviction is a single oldest-by-deadline scan, and expired entries are reaped lazily on get/1 rather than by a background sweeper. A high-throughput deployment should reach for Redis or a dedicated cache with sampled-LRU eviction and active expiry; this adapter exists so the cache-degradation path in AshOnetime.Cache is reachable without a third-party dependency.

Summary

Functions

A supervisor child specification for the owning GenServer.

Drops every entry from the cache (the table and config are retained).

Returns the configured max-entries cap.

Starts the owning GenServer for the ETS table.

Functions

child_spec(opts)

@spec child_spec(keyword()) :: Supervisor.child_spec()

A supervisor child specification for the owning GenServer.

Options

  • :max_entries — the bounded entry cap (default 10_000).

Add it to your supervision tree:

children = [
  {AshOnetime.Cache.Ets, max_entries: 10_000}
]

clear()

@spec clear() :: :ok

Drops every entry from the cache (the table and config are retained).

Safe to call while the cache is serving; get/1 calls during a wipe may race with the delete and return :miss, which is always safe (a miss falls through to PostgreSQL).

max_entries()

@spec max_entries() :: non_neg_integer()

Returns the configured max-entries cap.

start_link(opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()

Starts the owning GenServer for the ETS table.

Options

  • :max_entries — the bounded entry cap (default 10_000).

Returns {:ok, pid} or {:error, {:already_started, pid}}.