Caching and memory

Athena caches Discord objects in memory using Collection (a typed Map). Defaults are fine for most bots. For large bots the cache can dominate memory;...

Athena caches Discord objects in memory using Collection (a typed Map). Defaults are fine for most bots. For large bots the cache can dominate memory; this page covers the knobs. Everything here is opt-in. With no cache option set, behaviour is exactly the historical default (unbounded in-memory caches).

For the cluster-scale Redis story, continue to Scaling to millions.

What is cached

CacheScopeDefault
client.usersglobalunbounded
client.guildsglobalunbounded
client.privateChannelsglobalunbounded
guild.membersper guildunbounded
guild.channels / threads / rolesper guildunbounded
guild.voiceStates / stageInstancesper guildunbounded
channel.messagesper channelbounded by messageLimit (default 100)

Within one process, users are not duplicated: client.users holds each user once and members/messages reference it. Duplication across separate cluster processes is inherent to multi-process sharding and only a shared store removes it (see Scaling to millions).

Free win: presence defaults

A Member no longer pre-allocates an empty clientStatus object and activities array per instance. Until a real presence update arrives, members share one frozen default. Reads return identical values, and presence updates replace it. Bots without the presence intent stop paying for empty placeholders on every cached member. This is automatic; do not mutate member.clientStatus / member.activities in place.

Config-only knobs

  • Do not enable GuildPresences unless you use presence; it is the biggest source of churn.
  • disableEvents: { TYPING_START: true, PRESENCE_UPDATE: true, VOICE_STATE_UPDATE: true } skips parsing events you ignore.
  • Lower messageLimit, or set 0 to keep no messages.

Pluggable caches

Override how any cache is constructed. Unset entries keep the default.

import { Client, LRUCollection, NullCollection, User, Member, VoiceState, StageInstance } from 'athena';
 
new Client(token, {
  cache: {
    users: () => new LRUCollection(User, 100_000),
    members: (guild) => new LRUCollection(Member, 50_000),
    voiceStates: () => new NullCollection(VoiceState),
    stageInstances: () => new NullCollection(StageInstance)
  }
});
  • LRUCollection(Class, max) keeps at most max entries and evicts the least recently used. A successful get marks an entry as recently used.
  • NullCollection(Class) constructs and returns objects but stores nothing, so get/has always miss and size stays 0. Use it to disable a cache you never read back. Only disable caches you do not read.

Overridable cache keys: users, guilds, privateChannels, members, channels, threads, roles, voiceStates, stageInstances, messages.

Choosing

  • Small or medium bot: do nothing.
  • Memory-conscious single process: LRUCollection caps, NullCollection for unused caches, lower messageLimit.
  • Huge multi-cluster bot: add a shared remote store on top; see Scaling to millions.