jonnify·redis /redis-patterns/time-delay-priority/leaderboards/top-n-and-around-me

R4.05.2 · Leaderboards — dive 2

Top-N and around-me.

Two reads serve every leaderboard view. The top-N is the first N positions of the order; the around-me window is a slice of positions either side of a player's own. Both read the same order and leave it unchanged.

Grounding: echo/apps/codemojex/lib/codemojex/board.ex · Codemojex.Board.top/2 — the head-read ZREVRANGE … 0 (n-1) WITHSCORES against cm:<game>:board.

One game's board, eight players. Pick a read shape — a top-N from the head, or an around-me band centred on sora — and watch which rows it returns.
0—— 1—— 2—— 3—— 4—— 5—— 6—— 7——
readout · Pick a read shape. A top-N is ZREVRANGE 0 (N-1) WITHSCORES; an around-me read is ZREVRANK then a rank-range read centred on the located rank.

A top-N reads from the head; an around-me read locates sora's rank (ZREVRANK = 4) and reads the band rank−k through rank+k. Neither write the board.

§1

Top-N — a read from the head

leaderboards.md.txt · Core Operations

The top-N is the first N positions of the board read high-to-low. ZREVRANGE reads a rank range from the top, and WITHSCORES returns each member's score alongside it. The arguments 0 N-1 are rank indices, inclusive — positions 0 through N−1, the leader through the Nth player.

ZREVRANGE leaderboard 0 9 WITHSCORES    # the top 10
ZREVRANGE leaderboard 0 4 WITHSCORES    # the top 5

The read walks N members from the head, which is O(log N + N): the log to find the head, then N to read across. The board is unchanged — ZREVRANGE reads the order, never writes it. Redis 6.2 folded the range commands into a unified ZRANGE … REV, which reads the identical top-N; only the spelling differs.

The top-N is one head-read of the order. It returns the leader and the players right behind, with their scores, and touches nothing.

§2

Pagination — the top-N, page by page

leaderboards.md.txt · Pagination

A large board is read a page at a time, each page a rank range further down the order. Page p of size s is the range (p−1)·s through p·s − 1. ZCARD returns the member count, which divided by the page size gives the page count. Each page is the same head-read, offset down the order.

ZREVRANGE leaderboard 0 19 WITHSCORES    # page 1: ranks 1–20
ZREVRANGE leaderboard 20 39 WITHSCORES   # page 2: ranks 21–40
§3

Around-me — locate, then read the band

leaderboards.md.txt · Players Around a Rank

The around-me view shows a player their standing against nearby competitors. It is two reads. First locate the player's rank with ZREVRANK; then read the rank band centred on it. With a window half-width k, the band is rank − k through rank + k. Near the top the band clamps at 0 — a player at rank 2 with k = 5 reads 0 7, since there is nothing above rank 0.

ZREVRANK leaderboard "sora"             # say it returns 50
ZREVRANGE leaderboard 45 55 WITHSCORES   # ranks 45–55: sora with five either side

The band is a slice of the existing order; neither read changes the set. Locate, then read the band around the located rank — the around-me view is the top-N read with bounds offset to the player's own rank.

Around-me is two reads of one order: locate the rank, then read the band centred on it. No stored rank is needed to answer it.

§4

In codemojex — the top-N is Board.top/2

echo/apps/codemojex · board.ex · view.ex

codemojex runs the top-N read directly. Its leaderboard is one sorted set per game, keyed cm:<game>:board, scored by each player's best linear total. The read side is Codemojex.Board.top/2.

echo/apps/codemojex/lib/codemojex/board.ex · top/2, the head-readverbatim
@doc "Top `n`, highest first: {player_id, score}."
def top(game, n \\ 10) do
  case Cmd.zrevrange(k(game, "board"), 0, n - 1) |> Cmd.withscores() |> Wire.run(Bus.conn()) do   ← ZREVRANGE cm:<game>:board 0 (n-1) WITHSCORES
    {:ok, flat} -> {:ok, parse(flat)}
    other -> other
  end
end

Notes on ValkeyZREVRANGE key 0 N-1 WITHSCORES reads the first N members high-to-low with their scores in O(log N + N); the read never writes the set — valkey.io/commands/zrevrange.

Cmd.zrevrange/3 and Cmd.withscores/1 are the real EchoWire.Cmd builders; 0, n - 1 is the rank range. Codemojex.View.leaderboard/2 calls it and returns {player, max_score} rows, withholding everyone's guesses: the leaderboard is the one place a player sees others, and only their scores. The around-me window — ZREVRANK to find a rank, then a rank-range read — is the same order read with different bounds; the board does not need a stored rank to answer it.

/echomq/queue · the queue's ordered work

Reading a band of the queue

How the EchoMQ queue presents its ordered work, and reads a band of a group's jobs, is the Queue pillar. Open the Queue pillar for the queue reads built on the same score order a top-N and around-me read walk.

the pattern

The top-N is a head-read of the score order (ZREVRANGE 0 N-1 WITHSCORES) and around-me is a band centred on a rank (ZREVRANK, then a rank-range read).

its codemojex application

Codemojex.Board.top/2 runs Cmd.zrevrange("cm:<game>:board", 0, n - 1) |> Cmd.withscores() for the top-N, highest first; the same order serves an around-me band by adjusting the bounds.

Two reads cover every view: the head for the top-N, a band for around-me. Both read the order — codemojex's Board.top/2 is the head-read — and leave it untouched.

§5

References

sources · related

Sources

Related in this course