The sharding package manages multiple gateway connections for bots in many guilds. Discord requires sharding for bots in 2,500+ guilds.
Overview
Sharding distributes guilds across multiple gateway connections, each identified by a shard ID. The shard manager handles:
- Opening and closing shards
- Distributing guilds to shards using Discord’s formula
- Rate limiting IDENTIFY requests
- Automatic re-sharding when required
- Resuming shards with saved session state
Core types
ShardManager
Manages multiple gateway connections.
type ShardManager interface {
Open(ctx context.Context)
Close(ctx context.Context)
OpenShard(ctx context.Context, shardID int) error
ResumeShard(ctx context.Context, shardID int, state ShardState) error
CloseShard(ctx context.Context, shardID int)
ShardByGuildID(guildID snowflake.ID) gateway.Gateway
Shard(shardID int) gateway.Gateway
Shards() iter.Seq[gateway.Gateway]
}
Methods
Opens all configured shards concurrently
Closes all shards concurrently
Opens a specific shard by ID
ResumeShard(ctx, shardID, state)
Resumes a specific shard with saved session state
Closes a specific shard by ID
Returns the shard that manages the specified guild
Returns the gateway for the specified shard ID
Shards()
iter.Seq[gateway.Gateway]
Returns an iterator over all active shards
ShardState
Session state for resuming a shard.
type ShardState struct {
SessionID string
Sequence int
ResumeURL string
}
The session ID from the gateway READY event
The last sequence number received from the gateway
The resume URL from the gateway READY event
Creating a shard manager
New
Creates a new shard manager.
func New(
token string,
eventHandlerFunc gateway.EventHandlerFunc,
opts ...ConfigOpt,
) ShardManager
The function to handle gateway events from all shards
Configuration options
WithLogger
Sets the logger for the shard manager.
func WithLogger(logger *slog.Logger) ConfigOpt
WithShardIDs
Specifies which shard IDs to manage.
func WithShardIDs(shardIDs ...int) ConfigOpt
The shard IDs to manage (e.g., 0, 1, 2 for managing shards 0-2)
Note: Use this for stateless sharding or when managing a subset of shards.
WithShardIDsWithStates
Specifies shard IDs with session states for resuming.
func WithShardIDsWithStates(shards map[int]ShardState) ConfigOpt
Map of shard ID to session state for resuming
WithShardCount
Sets the total number of shards.
func WithShardCount(shardCount int) ConfigOpt
Total number of shards. Leave at 0 to let Discord determine the count
WithShardSplitCount
Sets how many shards a shard should split into when auto-scaling.
func WithShardSplitCount(shardSplitCount int) ConfigOpt
Number of new shards to create from one shard. Default is 2
WithAutoScaling
Enables automatic re-sharding when Discord requires it.
func WithAutoScaling(autoScaling bool) ConfigOpt
Whether to automatically re-shard when required. Default is false
Note: When enabled and Discord sends a “sharding required” close code, the manager automatically splits the shard according to ShardSplitCount.
WithGatewayCreateFunc
Sets a custom gateway creation function.
func WithGatewayCreateFunc(gatewayCreateFunc gateway.CreateFunc) ConfigOpt
WithGatewayConfigOpts
Applies configuration options to all created gateways.
func WithGatewayConfigOpts(opts ...gateway.ConfigOpt) ConfigOpt
Gateway configuration options applied to all shards
WithIdentifyRateLimiter
Sets a custom rate limiter for IDENTIFY requests.
func WithIdentifyRateLimiter(rateLimiter gateway.IdentifyRateLimiter) ConfigOpt
WithIdentifyRateLimiterConfigOpt
Configures the default identify rate limiter.
func WithIdentifyRateLimiterConfigOpt(opts ...gateway.IdentifyRateLimiterConfigOpt) ConfigOpt
WithCloseHandler
Sets a handler called when a shard closes.
func WithCloseHandler(closeHandler gateway.CloseHandlerFunc) ConfigOpt
Function called when a shard closes and cannot reconnect
Note: Not called when auto-scaling triggers a re-shard.
Utility functions
ShardIDByGuild
Calculates which shard manages a specific guild.
func ShardIDByGuild(guildID snowflake.ID, shardCount int) int
The total number of shards
Returns: The shard ID that should manage this guild.
Formula: (guildID >> 22) % shardCount
Usage examples
Basic sharding
package main
import (
"context"
"github.com/disgoorg/disgo/sharding"
"github.com/disgoorg/disgo/gateway"
)
func main() {
// Create shard manager
manager := sharding.New(
token,
func(event gateway.EventData) {
// Handle events from all shards
},
sharding.WithShardCount(4), // 4 shards
sharding.WithShardIDs(0, 1, 2, 3), // Manage all 4
)
// Open all shards
manager.Open(context.Background())
defer manager.Close(context.Background())
// Get shard for a specific guild
shard := manager.ShardByGuildID(guildID)
latency := shard.Latency()
}
Auto-scaling
manager := sharding.New(
token,
eventHandler,
sharding.WithAutoScaling(true),
sharding.WithShardSplitCount(2), // Split into 2 when needed
sharding.WithShardCount(2), // Start with 2 shards
)
// When Discord requires more shards:
// - Shard 0 will automatically split into shards 0 and 2
// - Shard 1 will automatically split into shards 1 and 3
// - New total: 4 shards
Resuming shards
// Save shard states when shutting down
states := make(map[int]sharding.ShardState)
for shard := range manager.Shards() {
states[shard.ShardID()] = sharding.ShardState{
SessionID: shard.SessionID(),
Sequence: shard.Sequence(),
ResumeURL: shard.ResumeURL(),
}
}
// Save states to database/file
// Resume with saved states
manager := sharding.New(
token,
eventHandler,
sharding.WithShardIDsWithStates(states),
sharding.WithShardCount(len(states)),
)
manager.Open(context.Background())
Stateless sharding
For distributed setups where different processes handle different shards:
// Process 1: Handles shards 0-1
manager1 := sharding.New(
token,
eventHandler,
sharding.WithShardIDs(0, 1),
sharding.WithShardCount(4), // Total across all processes
)
// Process 2: Handles shards 2-3
manager2 := sharding.New(
token,
eventHandler,
sharding.WithShardIDs(2, 3),
sharding.WithShardCount(4),
)
Custom gateway configuration
manager := sharding.New(
token,
eventHandler,
sharding.WithGatewayConfigOpts(
gateway.WithCompress(true),
gateway.WithIntents(
gateway.IntentGuilds,
gateway.IntentGuildMessages,
),
gateway.WithPresenceOpts(
gateway.WithWatchingActivity("the shards"),
gateway.WithOnlineStatus(discord.OnlineStatusDND),
),
),
)
Monitoring shards
// Iterate over all shards
for shard := range manager.Shards() {
fmt.Printf("Shard %d: Latency=%v, Status=%s\n",
shard.ShardID(),
shard.Latency(),
shard.Status(),
)
}
// Get specific shard
if shard := manager.Shard(0); shard != nil {
latency := shard.Latency()
}
// Find shard for a guild
shard := manager.ShardByGuildID(guildID)
Close handler
manager := sharding.New(
token,
eventHandler,
sharding.WithCloseHandler(func(
shard gateway.Gateway,
err error,
reconnect bool,
) {
log.Printf("Shard %d closed: %v (reconnect=%v)",
shard.ShardID(),
err,
reconnect,
)
// Save shard state for resuming
if reconnect {
saveShardState(sharding.ShardState{
SessionID: shard.SessionID(),
Sequence: shard.Sequence(),
ResumeURL: shard.ResumeURL(),
})
}
}),
)
Best practices
- Start with auto-determined shard count: Let Discord determine the optimal shard count initially
- Enable auto-scaling: For large bots that may grow, enable auto-scaling to handle re-sharding automatically
- Save session states: Always save session IDs, sequences, and resume URLs to resume shards after restarts
- Monitor shard health: Track latency and connection status for each shard
- Use stateless sharding for scale: Distribute shards across multiple processes/servers for horizontal scaling
- Configure rate limiting: Adjust identify rate limiter settings based on your bot’s verification status