asobi_leaderboards (asobi v0.75.1)
View SourceBoard-set reads: which boards exist, and one page of a board.
asobi_leaderboard_server answers questions about one board it already
holds in ETS - top N, a player's rank, the window around a player. Neither
question here can be answered that way. A board only becomes a process when
someone writes to it, so the process set is not the board set; and the ETS
reads are deliberately capped at 100 rows so that a caller cannot walk a
board, which is exactly what paging through one means.
Both reads therefore go to Postgres, where the scores are already persisted,
and the process layer contributes only the live flag. That flag is the
interesting half for an operator: a board is live-but-absent for its first 30
seconds (the flush interval), and present-but-not-live once nobody has
written to it since the node started.
Rank is computed by the database over the same total order the ETS table is
keyed on - score descending, then player_id - so a rank read here and a
rank from asobi_leaderboard_server:rank/2 agree on any flushed score.
Summary
Functions
The total order a board is ranked in, shared by the rank window and the default listing order so the two cannot drift apart.
Every board with a persisted score, plus every board that is live without one yet.
One board's entries, ranked, as a query for the shared paginator.
Functions
-spec board_order() -> [{atom(), asc | desc}].
The total order a board is ranked in, shared by the rank window and the default listing order so the two cannot drift apart.
player_id is unique within a board, so this is total, which is also what
makes offset paging safe.
Every board with a persisted score, plus every board that is live without one yet.
Each row is #{board_id, entries, top_score, updated_at, live}. Boards known
only as a running process report entries => 0 and null aggregates: they
exist, they have simply not flushed.
Capped at 1000 boards, ordered by id so the cut is stable between calls.
-spec entries_query(binary()) -> #kura_query{from :: atom() | module() | undefined, select :: [atom() | term()] | {exprs, [term()]}, wheres :: [term()], joins :: [term()], order_bys :: [term()], group_bys :: [atom()], havings :: [term()], limit :: non_neg_integer() | undefined, offset :: non_neg_integer() | undefined, distinct :: boolean() | [atom()], lock :: binary() | undefined, prefix :: binary() | undefined, preloads :: [atom() | {atom(), list()}], ctes :: [{binary(), #kura_query{}}], combinations :: [{union | union_all | intersect | except, #kura_query{}}], include_deleted :: boolean()}.
One board's entries, ranked, as a query for the shared paginator.
Returns a query rather than rows because the count and the page have to run
as one unit against the same filter - asobi_ops_page owns that - and
because the caller supplies the ordering. Rank is not the caller's to order
by: it is a window over board_order/0 computed before LIMIT applies, so
row 501 reports rank 501 however the page itself is sorted. No partition is
needed, the WHERE already narrows the window to one board.