asobi_matchmaker (asobi v0.75.1)
View SourceThe matchmaking queue. Players submit tickets with a mode and property
constraints (add/2); a pluggable strategy groups compatible tickets,
spawns a match, and pushes match.matched to each player. A single
gen_server owns the queue and ticks it on an interval.
Summary
Functions
The queue as an operator sees it: how many tickets are waiting, split by mode, and how long they have been waiting.
Types
-type mode_queue() :: #{mode := binary(), waiting := pos_integer(), oldest_wait_ms := non_neg_integer(), average_wait_ms := non_neg_integer()}.
-type snapshot() :: #{sampled_at := integer() | null, age_ms := non_neg_integer() | null, waiting := non_neg_integer(), modes := [mode_queue()]}.
Functions
-spec get_queue_stats() -> {ok, map()}.
-spec handle_call(term(), gen_server:from(), map()) -> {reply, term(), map()}.
-spec init([]) -> {ok, map()}.
-spec snapshot() -> snapshot().
The queue as an operator sees it: how many tickets are waiting, split by mode, and how long they have been waiting.
Read from an ETS table this process publishes to on each tick, not by
calling this process. The matchmaker is a single gen_server that runs every
strategy and spawns every match inside its own tick, so a gen_server:call
from an HTTP handler queues behind that work: the read would be slowest
exactly when the queue is deepest and an operator most needs to look at it.
The other direction is worse - HTTP concurrency is unbounded, so a console
polling once a second, times however many operators, lands in the mailbox of
the hottest process in the system. A read plane must not be able to slow the
game path down, at any request rate.
Publishing costs one ets:insert/2 per tick over a fold the tick has already
paid for. A reader pays one ets:lookup/2 and sends no message at all.
Waits are derived at read time from the sampled submitted_at values, so
they keep ageing correctly between ticks; only the counts are as old as
age_ms, at most one tick_interval (1s by default). Before the first tick,
or with no matchmaker running, the result is an empty queue rather than an
error - null timestamps say which.
-spec start_link() -> gen_server:start_ret().