EDA.Gateway.ReadyTracker (EDA v0.4.1)

Copy Markdown View Source

Tracks guild loading progress after READY to distinguish startup loads from runtime joins.

When Discord sends READY, it includes guild stubs (unavailable: true). Full guild data arrives via subsequent GUILD_CREATE events. This GenServer tracks which guilds are still loading and fires SHARD_READY / ALL_SHARDS_READY events when loading completes.

Uses an ETS table (:eda_pending_guilds) for O(1) lookups in the hot path and :persistent_term for the global ready flag.

Summary

Functions

Blocks until all shards have finished loading their guilds.

Returns a specification to start this module under a supervisor.

Marks a guild as loaded (received its GUILD_CREATE).

Returns true if the guild is still loading (pending GUILD_CREATE).

Returns true if every shard is connected and has finished loading its guilds.

Reports that a shard's gateway connection closed.

Registers pending guilds for a shard after receiving READY.

Reports that a shard resumed its session.

Starts the ReadyTracker.

Returns detailed status information about the loading state.

Functions

await_ready(timeout \\ 60000)

@spec await_ready(timeout()) :: :ok | {:error, :timeout}

Blocks until all shards have finished loading their guilds.

Returns :ok when the bot is fully ready, or {:error, :timeout} if the timeout expires. Uses OTP's native GenServer.call suspension — no scheduler is blocked.

If the bot is already ready, returns :ok immediately.

Examples

EDA.await_ready()
EDA.await_ready(30_000)

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

guild_loaded(guild_id)

@spec guild_loaded(String.t()) :: :ok

Marks a guild as loaded (received its GUILD_CREATE).

Called by EDA.Gateway.Events when a startup GUILD_CREATE arrives.

loading?(guild_id)

@spec loading?(String.t()) :: boolean()

Returns true if the guild is still loading (pending GUILD_CREATE).

Reads directly from ETS — no GenServer call, O(1).

ready?()

@spec ready?() :: boolean()

Returns true if every shard is connected and has finished loading its guilds.

Goes back to false while any shard's gateway connection is down, and returns to true once it resumes or completes a fresh READY — so it is safe to use as a readiness check. REST calls do not depend on the gateway and keep working while this is false; events, voice and member chunking do not.

Non-blocking — reads from :persistent_term (O(1)).

shard_disconnected(shard_id)

@spec shard_disconnected(non_neg_integer()) :: :ok

Reports that a shard's gateway connection closed.

Called by EDA.Gateway.Connection on every disconnect. A shard that had finished loading stops counting as ready until it resumes or completes a fresh READY, so ready?/0 stops answering true for a bot that has lost its gateway.

shard_ready(shard_id, guild_ids)

@spec shard_ready(non_neg_integer(), [String.t()]) :: :ok

Registers pending guilds for a shard after receiving READY.

Called by EDA.Gateway.Connection when a READY payload arrives.

shard_resumed(shard_id)

@spec shard_resumed(non_neg_integer()) :: :ok

Reports that a shard resumed its session.

A resume carries on the same session, so Discord sends RESUMED rather than READY and does not reload the guilds. A shard that was ready before the disconnect is ready again at once. SHARD_READY and ALL_SHARDS_READY are not dispatched a second time, because from the consumer's side nothing restarted.

start_link(opts \\ [])

Starts the ReadyTracker.

status()

@spec status() :: map()

Returns detailed status information about the loading state.

Return value

A map with keys:

  • :globally_ready — whether all shards are ready
  • :ready_shards — set of shard IDs that finished loading
  • :expected_shards — total number of shards expected
  • :pending_counts — map of shard_id => remaining guild count