An EDA.Cache.Adapter backed by Mnesia, for caches shared across a cluster.
config :eda, cache_adapter: EDA.Cache.Adapter.MnesiaMnesia ships with OTP, so this needs no extra dependency — but it does need to be on your application's code path.
Add :mnesia to your own application
EDA does not list :mnesia in its :extra_applications, because that would start
Mnesia for every user of the library including those who never touch this adapter. Your
bot must declare it itself, or this adapter raises :mnesia.start/0 is undefined at
startup:
# your mix.exs
def application do
[extra_applications: [:logger, :mnesia], mod: {MyBot.Application, []}]
endThe adapter starts Mnesia on demand and creates one table per cache the first time it initialises.
Configure it before EDA starts
Mnesia backs a ram_copies table with an ETS table of the same name, so it cannot
claim a name the ETS adapter already took. Set :cache_adapter in config, not at
runtime: swapping adapters in a live VM fails with
{:system_limit, _, {~c"Failed to create ets table", :badarg}}.
What this buys you
A bot running several nodes gets one view of the cache: a GUILD_CREATE handled on
one shard's node is visible to every other node, and a node restarting rejoins a
populated cache instead of a cold one. With the ETS adapter each node caches only
what its own shards saw.
Configuration
config :eda,
cache_adapter: EDA.Cache.Adapter.Mnesia,
cache_mnesia: [
copies: :ram_copies, # or :disc_copies, :disc_only_copies
nodes: [node()], # nodes to place a copy on
wait_timeout: 5_000 # ms to wait for tables at startup
]:ram_copies is the default and is usually right: the cache is reconstructible from
Discord, so paying for disk writes on every PRESENCE_UPDATE buys little.
:disc_copies additionally requires a schema on disk — call
:mnesia.create_schema([node()]) once before starting, or Mnesia keeps everything in
RAM regardless.
Dirty by design
Every operation uses Mnesia's dirty API. That is deliberate: a Discord cache is a
replica of state Discord owns, it is read on the event hot path, and a stale or lost
entry costs one REST call rather than corrupting anything. Transactions would add
coordination latency to every PRESENCE_UPDATE in exchange for a guarantee the data
does not need.
If two nodes write the same key concurrently, last-write-wins. That is the same outcome the gateway itself produces when two shards see the same entity.
Admission policy and eviction still apply
They live above the adapter, so they work here unchanged. Note that eviction is
per-node: each node bounds the memory it holds, but the bookkeeping is local, so a
cluster may hold more entries in total than max_size suggests.