Sharding
Sharding allows your bot to scale across multiple WebSocket connections. Discord requires sharding when your bot grows beyond a certain number of guilds.
What is Sharding?
Discord's Gateway has limits on how many guilds can be handled per connection. Sharding splits your bot's guilds across multiple Gateway connections (shards).
Without Sharding:
┌─────────────────────────────────────┐
│ One Gateway │
│ ┌─────────────────────────────┐ │
│ │ All Guilds (2500+) │ │
│ │ ├─ Guild 1 │ │
│ │ ├─ Guild 2 │ │
│ │ ├─ ... │ │
│ │ └─ Guild 2500 │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
With Sharding:
┌─────────────────────────────────────┐
│ Multiple Gateways │
│ ┌─────────┐ ┌─────────┐ │
│ │ Shard 0 │ │ Shard 1 │ ... │
│ │ Guilds │ │ Guilds │ │
│ │ 1-1250 │ │1251-2500│ │
│ └─────────┘ └─────────┘ │
└─────────────────────────────────────┘
When to Shard
Discord requires sharding when your bot reaches:
- 2500 guilds (automatic sharding recommended)
- 100+ guilds (optional, for better performance)
Sharding Modes
Automatic Sharding
Let DiscordRDA determine optimal shard count:
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
shards: :auto
)
DiscordRDA will:
- Connect to Gateway
- Get recommended shard count from Discord
- Start all shards automatically
Manual Sharding
Specify exact shard configuration:
# Single shard instance (shard 0 of 4)
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
shards: [[0, 4]]
)
# Multiple shards in one process
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
shards: [[0, 4], [1, 4], [2, 4], [3, 4]]
)
Distributed Sharding
Run shards across multiple processes/machines:
# Process 1 - shards 0-1 of 4
shard_bot_0 = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
shards: [[0, 4], [1, 4]]
)
# Process 2 - shards 2-3 of 4
shard_bot_1 = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
shards: [[2, 4], [3, 4]]
)
How Sharding Works
Guild Distribution
Discord assigns guilds to shards based on a formula:
shard_id = (guild_id >> 22) % num_shards
This ensures:
- Guilds always map to the same shard
- Even distribution across shards
- Consistent routing
Event Handling
Each shard receives events only for its assigned guilds:
bot.on(:message_create) do |event|
# This event is automatically from a guild
# on this shard - no filtering needed
puts "Shard #{event.shard_id}: Message in #{event.guild_id}"
end
Shard Management
Getting Shard Info
bot = DiscordRDA::Bot.new(token: token, shards: :auto)
# After connecting
status = bot.status
puts "Running #{status[:shard_count]} shards"
puts "Total guilds: #{status[:guild_count]}"
# Per-shard info
status[:shards].each do |shard|
puts "Shard #{shard[:id]}: #{shard[:guild_count]} guilds"
puts " Status: #{shard[:status]}"
puts " Latency: #{shard[:latency]}ms"
end
Handling Shard Events
# Shard connects
bot.on(:shard_connect) do |event|
puts "Shard #{event.shard_id} connected"
end
# Shard disconnects
bot.on(:shard_disconnect) do |event|
puts "Shard #{event.shard_id} disconnected"
puts "Will auto-reconnect..."
end
# Shard resumes after disconnect
bot.on(:shard_resume) do |event|
puts "Shard #{event.shard_id} resumed"
end
# Shard encounters error
bot.on(:shard_error) do |event|
puts "Shard #{event.shard_id} error: #{event.error}"
end
Zero-Downtime Resharding
Add shards without stopping your bot:
Manual Resharding
# Current: 4 shards
# Grow to: 8 shards
bot.reshard_to(8)
# DiscordRDA will:
# 1. Start new shards 4-7
# 2. Migrate guilds seamlessly
# 3. Keep old shards until migration complete
# 4. Bot stays online throughout
Auto-Resharding
Automatically add shards as you grow:
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
shards: :auto
)
# Enable auto-resharding
bot.enable_auto_reshard(
max_guilds_per_shard: 1000, # Recommended: 1000
check_interval: 300 # Check every 5 minutes
)
# Bot automatically adds shards when:
# - Guild count / shard_count > max_guilds_per_shard
Session Management
Session IDs
Each shard maintains a session ID for resuming:
bot.on(:shard_resume) do |event|
# Resumed with existing session
puts "Resumed session #{event.session_id}"
puts "Replayed #{event.seq} events"
end
Sequence Numbers
Track event sequence numbers for debugging:
bot.on(:dispatch) do |event|
puts "Shard #{event.shard_id} seq: #{event.seq}"
end
Best Practices
Process per Shard
For large bots, run one shard per process:
# shard_manager.rb
SHARD_COUNT = 8
SHARD_COUNT.times do |shard_id|
fork do
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
shards: [[shard_id, SHARD_COUNT]],
cache: :redis # Shared cache
)
bot.run
end
end
Process.waitall
Load Balancing
Distribute shards across servers:
# Server 1: Shards 0-3
shards = (0..3).map { |i| [i, 8] }
# Server 2: Shards 4-7
shards = (4..7).map { |i| [i, 8] }
Monitoring
Monitor shard health:
bot.on(:ready) do
Thread.new do
loop do
sleep 60
bot.status[:shards].each do |shard|
if shard[:latency] > 500
puts "WARNING: Shard #{shard[:id]} high latency!"
end
if shard[:status] != :connected
puts "WARNING: Shard #{shard[:id]} not connected!"
end
end
end
end
end
Common Patterns
Guild Count per Shard
Check if resharding is needed:
def check_reshard_needed(bot, max_guilds: 1000)
status = bot.status
status[:shards].each do |shard|
if shard[:guild_count] > max_guilds
puts "Shard #{shard[:id]} has #{shard[:guild_count]} guilds"
puts "Consider resharding!"
end
end
end
Broadcasting to All Shards
Send a command to all shards:
# Using Redis pub/sub with shared cache
bot.cache.redis.publish('bot:command', {
type: 'update_presence',
activity: 'New Status'
}.to_json)
Shard-Aware Rate Limiting
Some rate limits are per-shard:
# Discord's Identify rate limit: 5 per 5 seconds per shard
# DiscordRDA handles this automatically
Troubleshooting
Shard Won't Connect
bot.on(:shard_error) do |event|
puts "Shard #{event.shard_id} error: #{event.error}"
# Common issues:
# - Invalid token
# - Connection issues
# - Rate limited (identify limit)
end
Uneven Guild Distribution
Guild distribution follows a hash - it won't be perfectly even:
# This is normal - some shards may have more guilds
# Discord's algorithm ensures consistency over evenness
Memory Issues
Each shard uses memory. For large bots:
bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
shards: :auto,
cache: :redis, # Use Redis to share memory
max_cache_size: 1000 # Limit per-shard cache
)
Complete Example
require 'discord_rda'
class ShardedBot
def initialize
@bot = DiscordRDA::Bot.new(
token: ENV['DISCORD_TOKEN'],
application_id: ENV['DISCORD_APP_ID'],
shards: :auto,
intents: [:guilds, :guild_messages],
cache: :redis,
redis_config: { host: 'localhost', port: 6379 },
log_level: :info
)
setup_handlers
enable_auto_reshard if ENV['ENABLE_AUTOSHARD']
end
def setup_handlers
@bot.on(:ready) do |event|
shard_info = event.shard ? "[Shard #{event.shard.join('/')}]" : ""
puts "#{shard_info} Ready as #{event.user.username}"
end
@bot.on(:shard_connect) do |event|
puts "Shard #{event.shard_id} connected (#{event.guild_count} guilds)"
end
@bot.on(:shard_disconnect) do |event|
puts "Shard #{event.shard_id} disconnected"
end
@bot.on(:message_create) do |event|
handle_message(event)
end
end
def enable_auto_reshard
@bot.enable_auto_reshard(
max_guilds_per_shard: 1000,
check_interval: 300
)
puts "Auto-resharding enabled"
end
def handle_message(event)
return unless event.content.start_with?('!')
case event.content
when '!ping'
event.message.respond(content: 'Pong!')
when '!status'
status = @bot.status
event.message.respond(content: format_status(status))
end
end
def format_status(status)
shards = status[:shards].map do |s|
"Shard #{s[:id]}: #{s[:guild_count]} guilds (#{s[:latency]}ms)"
end
"**Bot Status**\nTotal Guilds: #{status[:guild_count]}\n" +
"Shards: #{status[:shard_count]}\n\n" +
shards.join("\n")
end
def run
@bot.run
end
end
ShardedBot.new.run
Next Steps
- Learn about Caching - Cache sharing across shards
- Explore Rate Limiting - Shard-aware limits
- Set up REST Proxy - Horizontal scaling