LemonCore. Store. ReadCache
(lemon_core v0.1.0)
View Source
Public ETS read-through cache for high-traffic Store domains.
This module maintains a set of public ETS tables that mirror the backing store for domains that receive heavy read traffic (chat state, runs, progress mappings, session index, Telegram target index). Reads are served directly from ETS without going through the Store GenServer, eliminating mailbox contention on the read path.
Writes still go through the Store GenServer, which updates both the backend and this cache atomically within its process.
Cached Domains
Intrinsic domains, mirrored for the store's own typed APIs:
:chat— chat state by scope:runs— run records by run_id:progress— progress mappings by {scope, msg_id}
Generic tables are added per store instance — :sessions_index by default,
plus whatever owners register with LemonCore.Store.register_cached_table/2.
The store does not know what those tables hold.
Instances
Every cache is scoped to the name of the LemonCore.Store that owns it.
The default store (LemonCore.Store) keeps the historical table names
(:lemon_store_cache_chat and friends); any other store name gets its own
prefixed set, so several stores can run in one node without sharing data.
init/1 returns a map of domain => table reference. The owning Store keeps
that map in its state and passes it back on the server side; client-side
callers pass the store name and the table references are resolved from
:persistent_term instead of being rebuilt from atoms on every call.
Usage
Callers should use get/2 for reads. The Store GenServer calls
put/3 and delete/2 to keep the cache in sync with the backend.
Summary
Functions
Add one generic table to a store's cache and return the updated map.
Check whether store currently caches domain.
List the domains this store currently caches.
Remove an entry from the cache. Called by the Store GenServer.
Read a value, distinguishing "not cached" from "cached but absent".
Read a value from the cache. Returns nil if not found.
Initialize the cache ETS tables for store. Called during Store init.
Domains the Store always caches for its typed APIs.
List all cached entries for a domain as {key, value} tuples.
Update the cache after a backend write. Called by the Store GenServer.
Return the ETS table name for a domain of a store instance.
Return the published domain => table map for a store, or nil when the
store has never initialized its cache.
Types
@type store() :: atom()
@type tables() :: %{optional(atom()) => :ets.table()}
Functions
Add one generic table to a store's cache and return the updated map.
Must be called from the process that owns the store's tables.
Check whether store currently caches domain.
List the domains this store currently caches.
Remove an entry from the cache. Called by the Store GenServer.
Read a value, distinguishing "not cached" from "cached but absent".
Returns :uncached when the store does not mirror domain at all, :miss
when it does but has no entry for key, and {:ok, value} on a hit. Callers
use this to decide whether a cache miss is worth populating.
Read a value from the cache. Returns nil if not found.
This bypasses the GenServer entirely for O(1) ETS lookup.
Initialize the cache ETS tables for store. Called during Store init.
Creates a table for each intrinsic domain plus each of generic_tables, and
publishes the domain => table map to :persistent_term so client processes
can resolve tables without re-deriving atom names.
Additive: domains already published for this store whose tables are still alive are carried over, so a caller that re-initializes with a smaller set never silently strips another caller's tables out of the shared map.
Raises LemonCore.Store.ReadCache.CollisionError when a table with the
derived name already exists and is owned by a process other than this store
instance — silently reusing another instance's table would merge two stores'
data.
@spec intrinsic_domains() :: [atom()]
Domains the Store always caches for its typed APIs.
List all cached entries for a domain as {key, value} tuples.
Update the cache after a backend write. Called by the Store GenServer.
Return the ETS table name for a domain of a store instance.
The default store keeps the historical names; other instances are prefixed with their (underscored) store name.
Return the published domain => table map for a store, or nil when the
store has never initialized its cache.