EDA.Cache.Adapter.Mnesia (EDA v0.5.0-beta.2)

Copy Markdown View Source

An EDA.Cache.Adapter backed by Mnesia, for caches shared across a cluster.

config :eda, cache_adapter: EDA.Cache.Adapter.Mnesia

Mnesia 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, []}]
end

The 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.