Skip to content

Performance Guide

Overview

discord.go is designed for low-latency gateway dispatch and efficient REST operations. This guide covers allocation reduction, connection reuse, and throughput tuning.

Allocation Reduction

Cached Voice Encryption

The voice Client caches the cipher.AEAD when the secret key is received, avoiding per-packet AES cipher and GCM allocation. This eliminates 40+ allocations per second per voice connection at 20 packets/sec.

JSON Handling

The REST client marshals request bodies once and reuses the bytes for retries. Response bodies are read fully into memory before unmarshaling to support error parsing and 429 retry logic.

Byte Buffer Reuse

The bytes.NewReader used for request bodies is lightweight. For high-throughput applications, consider passing a custom http.Client with a transport that pools buffers.

Connection Pooling

The default HTTP client clones http.DefaultTransport and sets MaxIdleConnsPerHost = 32 (default is 2). Since Discord REST requests go to a single host (discord.com), the higher value prevents connection churn under concurrent REST calls.

To tune further, provide a custom http.Client:

go
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.MaxIdleConnsPerHost = 64
transport.MaxConnsPerHost = 100
httpClient := &http.Client{Transport: transport}
restClient := rest.New(token, nil, httpClient)

Gateway Throughput

Event Dispatch

The bot dispatches events to handlers in goroutines, bounded by bot.WithMaxHandlerConcurrency. Set this to limit goroutine creation:

go
client := bot.New(token,
    bot.WithMaxHandlerConcurrency(100),
)

Heartbeat Latency

Monitor gateway latency via bot.GatewayLatency():

go
latency := client.GatewayLatency()
if latency > 5*time.Second {
    log.Printf("high gateway latency: %v", latency)
}

REST Latency

Measure REST latency with bot.APILatency:

go
latency, err := client.APILatency(ctx)

Cache Tuning

The default cache.Memory cache has no TTL or size limit. For long-running bots, consider:

  • Implementing a cache with TTL to bound memory usage.
  • Implementing an LRU cache with a max-size eviction policy.
  • Using the cache interfaces (GuildCache, ChannelCache, etc.) to cache only what your bot needs.

Rate Limit Throughput

The rate limiter enforces:

  • 50 requests/second global limit using golang.org/x/time/rate.Limiter (token bucket). This avoids global mutex contention under high concurrency — concurrent goroutines reserve tokens in parallel without serializing through a single lock.
  • Per-bucket limits based on Discord's X-RateLimit-Bucket headers.
  • Optimistic remaining count decrement to maximize throughput.

For bulk operations, use batch endpoints (e.g., BulkOverwriteGlobalCommands) instead of individual requests.

Compression

Enable gateway compression to reduce bandwidth:

go
client := bot.New(token,
    bot.WithGatewayCompression(true),
)

This enables zlib-stream encoding on the gateway WebSocket.

Common Patterns

  • Set MaxHandlerConcurrency to bound goroutine creation.
  • Monitor gateway and REST latency.
  • Use batch REST endpoints for bulk operations.
  • Enable compression for bandwidth-sensitive deployments.

Best Practices

  • Profile with go test -bench before optimizing.
  • Use context.Context with deadlines on all REST calls.
  • Avoid tight loops that make many REST requests without delays.
  • Reuse rest.Client and bot.Bot instances; do not create new clients per request.

Common Mistakes

  • Not setting MaxHandlerConcurrency, allowing unbounded goroutine creation.
  • Creating a new rest.Client per request instead of reusing one.
  • Not monitoring gateway latency for connection health.
  • Using global command sync in tight loops during development.

Released under the Apache License 2.0.