View Source Pluggable caching
The default ETS-based caches supplied by nostrum should work for most of your
needs, but all of the caches can be exchanged for your own implementations. For
this, implement the behaviours exported by the cache modules under
Nostrum.Cache
.
Exception
The exception to the above is the
Nostrum.Cache.MessageCache
, which does not include an ETS-based implementation, and defaults to a NoOp cache. This is an intentional design decision because caching messages consumes a lot more memory than other objects, and is often not needed by most users.
Use the [:nostrum, :caches]
configuration for configuring which cache
implementation you want to use. This can only be set at dependency compilation
time. A common situation is that you don't want to cache presences in your bot,
most likely you don't care about user's status, so you can disable it altogether
by using the NoOp
presence cache:
config :nostrum,
caches: %{
presences: Nostrum.Cache.PresenceCache.NoOp
}
In addition to regular caches that associate Discord snowflakes with the proper
"full" object, nostrum also maintains junction table-like mappings that allow
you to find the matching object from one cache in another. One example for this
is Nostrum.Cache.ChannelGuildMapping
.
Nostrum also ships with Mnesia-based caches. These are only compiled in when mnesia is available: they may not be available on Nerves or when Mnesia was not installed with OTP.
Implementations
ETS caching
Caching based on :ets
is used by default. No configuration is required. Fast,
light on memory, but does not support any form of distribution or secondary
indexing: queries such as fetching all guild members for a guild by its ID will
perform a full table scan. For smaller bots, this is perfectly acceptable.
Mnesia caching
Mnesia-based caching is mainly suggested for larger bots that require features such as cache distribution, fragmentation, secondary indexing and more.
The caches will attempt to create their tables automatically at startup:
therefore, Mnesia must be started ahead of nostrum. Caches expose a function
table/0
that can be called to retrieve the table name used by the cache and
perform schema operations on it, such as adding replicas or fragmenting them.
Access to Mnesia is presently done in sync_transaction
mode for best
consistency. If needed, a compile-time configuration option for the cache to
switch this can be added.
Mnesia-based caching assumes the user is familar with usage and maintenance of Mnesia: the Mnesia User's Guide is a good starting point.
NoOp caching
The NoOp cache adapters are supplied for the case where you do not want to cache specific data from Discord at all.
Cache invalidation
Nostrum does not invalidate most caches in any special way: it will maintain it in response to gateway events (for instance by deleting a guild and its members upon leaving it), but won't regularly prune caches or associate expiration times with entries. For volatile (RAM-based) caches this is perfectly fine, however, when implementing your own cache backend that persists to disk in some way, you need to take care of this yourself.
The exception to this is the Nostrum.Cache.MessageCache.Mnesia
module, which has a
default size limit of 10,000 and will automatically remove the 100 oldest
messages when this limit is reached as well as delete all cached messages for a
channel when the channel is deleted.
Cache performance
nostrum strives to provide the most performant caches on the Discord bot caching market. If you run into performance issues with caches that you feel are not adequately documented as such, please feel free to open an issue.
Benchmarks for caches can be found in the benchmarks/
directory of the
source code tree. If you want to get a feeling for how the caches perform or
implement optimizations, check them out.