Table of Contents

Gateway Dispatch Ordering

DisCatSharp uses an ordered dispatch queue to process gateway events received from Discord. This page explains how the dispatch pipeline works and how to configure it.

How It Works

When the Discord gateway sends a dispatch event (e.g., MESSAGE_CREATE, GUILD_MEMBER_UPDATE), DisCatSharp routes it through an internal Channel<GatewayPayload> queue before processing. This queue ensures events are dequeued in the exact order they arrive from the WebSocket connection.

A single consumer loop per client (per shard) reads from this queue and processes each event sequentially. This guarantees that internal cache and state mutations always happen in FIFO order, regardless of the configured dispatch mode.

After the internal processing (cache updates, entity backfills, etc.) completes for an event, the user-facing event handlers (Client.MessageCreated += …) are invoked. The configured dispatch mode controls whether those handlers are awaited inline or fired concurrently.

Dispatch Modes

The dispatch mode is configured via GatewayAdvancedConfiguration.DispatchMode:

ConcurrentHandlers (Default)

var config = new DiscordConfiguration
{
    Token = "your-token",
    Gateway =
    {
        Advanced =
        {
            DispatchMode = GatewayDispatchMode.ConcurrentHandlers
        }
    }
};

Internal cache mutations are processed sequentially (ordered). After each event's cache work completes, user event handlers are fired concurrently (fire-and-forget). Multiple events may have their user handlers running at the same time, but cache state is always consistent.

Best for: Most bots. Provides the highest throughput while maintaining cache integrity.

SequentialHandlers

var config = new DiscordConfiguration
{
    Token = "your-token",
    Gateway =
    {
        Advanced =
        {
            DispatchMode = GatewayDispatchMode.SequentialHandlers
        }
    }
};

Each event is fully awaited — including all cache mutations and user event handler invocations — before the next event is dequeued. This provides total serialization per shard.

Best for: Bots where handler logic depends on strict event ordering and cannot tolerate overlapping execution (e.g., state machines that track member joins/leaves in sequence).

Trade-off: Lower throughput. If a handler takes a long time, subsequent events will queue up.

Queue Capacity

The dispatch queue has a bounded capacity. If events arrive faster than they are consumed, overflow events are dropped and a warning is logged. This design ensures the WebSocket reader is never blocked — heartbeats, reconnects, and other control opcodes are always processed immediately.

var config = new DiscordConfiguration
{
    Token = "your-token",
    Gateway =
    {
        Advanced =
        {
            // Default: 10,000 events
            DispatchQueueCapacity = 20_000,

            // Or unbounded (use with caution):
            // DispatchQueueCapacity = 0
        }
    }
};
Warning

If the queue is full, incoming dispatch events will be dropped and a warning will be logged. Increase the capacity if you see drop warnings in your logs.

Note

Setting the capacity to 0 creates an unbounded queue. This eliminates drops entirely but may consume significant memory under sustained high load.

Sharding

Each shard (each DiscordClient instance) has its own independent dispatch queue and consumer loop. The dispatch mode and queue capacity settings apply per shard.

When using DiscordShardedClient, the configuration is cloned for each shard, so the dispatch settings are consistent across all shards.

Summary

Feature ConcurrentHandlers SequentialHandlers
Dequeue order FIFO ✓ FIFO ✓
Cache mutation order Sequential ✓ Sequential ✓
User handler execution Concurrent (fire-and-forget) Sequential (awaited)
User handler overlap Possible Never
Throughput High Lower
Default