Caching
DiscordRDA provides a sophisticated caching system that stores Discord entities (users, guilds, channels, messages) to reduce API calls and improve performance.
Why Cache?
- Reduce API calls - Avoid hitting rate limits
- Improve performance - Sub-millisecond lookups vs API round-trips
- Offline access - Access data when Discord API is unavailable
- Cross-shard sharing - Redis enables data sharing across shards
Cache Backends
DiscordRDA supports two cache backends:
Memory Cache (Default)
Stores entities in Ruby memory. Fastest but limited to single process.
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
cache: :memory,
max_cache_size: 10000 # Maximum entities to store
)
Pros:
- Fastest access times
- No external dependencies
Cons:
- Memory usage grows with guild count
- No sharing between shards/processes
- Data lost on restart
Redis Cache
Stores entities in Redis. Slightly slower but enables sharing and persistence.
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
cache: :redis,
redis_config: {
host: 'localhost',
port: 6379,
db: 0,
password: nil
}
)
Pros:
- Share data across shards
- Survive bot restarts
- Distributed setups
- Configurable persistence
Cons:
- Requires Redis server
- Network overhead
- Serialization cost
What Gets Cached
By default, DiscordRDA caches:
- Users - User data
- Guilds - Server information
- Channels - Channel data
- Members - Guild member data
- Roles - Role information
- Messages - Recent messages (configurable)
Accessing Cached Data
Bot Helper Methods
# Get guild
guild = bot.guild(guild_id)
# Returns cached guild or fetches from API
# Get channel
channel = bot.channel(channel_id)
# Get user
user = bot.user(user_id)
# Get member
member = bot.guild_member(guild_id, user_id)
Direct Cache Access
# Access cache directly
cache = bot.cache
# Get specific entity
cache[:guild][guild_id]
cache[:channel][channel_id]
cache[:user][user_id]
cache[:member][[guild_id, user_id]]
Cache Configuration
Memory Cache Options
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
cache: :memory,
max_cache_size: 50000, # Maximum entities
message_cache_size: 100 # Messages per channel
)
Redis Cache Options
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
cache: :redis,
redis_config: {
host: 'localhost',
port: 6379,
db: 0,
password: ENV['REDIS_PASSWORD'],
timeout: 5.0,
reconnect_attempts: 3
},
cache_ttl: 3600 # Time-to-live in seconds
)
Cache Invalidation
Manual Invalidation
# Invalidate specific entity
bot.cache.invalidate(:user, user_id)
bot.cache.invalidate(:guild, guild_id)
bot.cache.invalidate(:channel, channel_id)
# Invalidate guild member
bot.cache.invalidate(:member, [guild_id, user_id])
# Clear entire cache
bot.cache.clear
# Clear specific type
bot.cache.clear_type(:message)
Pattern-Based Invalidation
# Invalidate by pattern
bot.cache.invalidate_pattern(:user, '123*') # Users with IDs starting with 123
bot.cache.invalidate_guild(guild_id) # Clear all data for a guild
Custom Cache Store
Implement your own cache store:
class MyCustomCache < DiscordRDA::CacheStore
def initialize(config = {})
@store = {}
@config = config
end
def get(key)
@store[key]
end
def set(key, value, ttl = nil)
@store[key] = value
end
def delete(key)
@store.delete(key)
end
def clear
@store.clear
end
def keys
@store.keys
end
end
# Use custom cache
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
cache: MyCustomCache.new(custom_option: 'value')
)
Configurable Cache
For fine-grained control over what gets cached:
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN']
)
# Configure cache policies
cache = DiscordRDA::ConfigurableCache.new(
store: :redis,
policies: {
user: { ttl: 3600, max_size: 10000 },
guild: { ttl: nil, max_size: 1000 }, # Never expire
channel: { ttl: 1800, max_size: 5000 },
member: { ttl: 600, max_size: 50000 },
message: { ttl: 300, max_size: 1000 } # 5 min TTL
}
)
bot.cache = cache
Cache Events
Monitor cache operations:
bot.cache.on(:hit) do |key, type|
puts "Cache hit: #{type} #{key}"
end
bot.cache.on(:miss) do |key, type|
puts "Cache miss: #{type} #{key}"
end
bot.cache.on(:evict) do |key, type|
puts "Cache evict: #{type} #{key}"
end
Best Practices
Production Recommendations
- Use Redis for multi-shard bots - Enables data sharing
- Set appropriate TTLs - Balance freshness vs API calls
- Monitor hit rates - Optimize TTL values
- Clear on guild removal - Clean up when bot leaves guild
bot.on(:guild_delete) do |event|
# Clear guild data from cache
bot.cache.invalidate_guild(event.id)
end
Memory Management
# Limit memory for memory cache
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
cache: :memory,
max_cache_size: 100_000
)
# Use LRU eviction for messages
cache = DiscordRDA::MemoryStore.new(
max_size: 10000,
eviction_policy: :lru # Least Recently Used
)
Cache Warming
Pre-populate cache on startup:
bot.on(:ready) do |event|
# Warm user cache
event.guilds.each do |guild|
members = bot.guild_members(guild.id, limit: 1000)
members.each do |member|
bot.cache.set(:user, member.user.id, member.user)
end
end
end
Cache Monitoring
# Get cache statistics
stats = bot.cache.stats
puts "Cache hits: #{stats[:hits]}"
puts "Cache misses: #{stats[:misses]}"
puts "Hit rate: #{stats[:hit_rate]}%"
puts "Size: #{stats[:size]}"
# For Redis cache
stats = bot.cache.redis.info
puts "Used memory: #{stats['used_memory_human']}"
Troubleshooting
High Memory Usage
# Reduce cache size
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
cache: :memory,
max_cache_size: 5000, # Reduce from default
message_cache_size: 50 # Fewer messages
)
Stale Data
# Invalidate when you know data changed
bot.on(:guild_member_update) do |event|
# Clear member from cache to fetch fresh data
bot.cache.invalidate(:member, [event.guild_id, event.user.id])
end
Redis Connection Issues
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
cache: :redis,
redis_config: {
host: 'localhost',
port: 6379,
reconnect_attempts: 5,
reconnect_delay: 1.0,
# Fallback to memory on Redis failure
fallback_store: :memory
}
)
Next Steps
- Learn about Sharding - Caching across shards
- Build slash commands - Using cached data
- Explore API reference - Cache class documentation