Game Telemetry Windows: Match Counts, Live Sessions, Late Events
OpenAISoftware Engineer · OnsiteLast updated
Build an in-memory service that counts per-match actions inside a global 10-minute window, tracks each player's live client sessions from heartbeats and logouts, and stays correct when events arrive late. Tests bounded-memory eviction, amortized analysis, lazy deletion and exact window semantics.
You run the live-ops dashboard for a multiplayer game. Game servers stream events to a single-threaded, in-memory service. Every event carries a player: str and a whole-number minute: int. Your service must answer "what is happening right now" questions instantly.
Let T be the largest minute seen so far across all events of every kind and every player. The window is the closed range [T - 9, T] (10 minutes). For example, when T = 12 the window is [3, 12], so an event at minute 3 still counts. Note that one player's event can move T forward and push another player's events out of the window.
Memory must stay proportional to what is inside the window (active keys × window length), never to the full history. State the time cost of every method.
Part 1: per-match activity
record(player, match_id, minute): one gameplay action in matchmatch_id. A match ID is unique per player, so the key is(player, match_id). One key can have many actions in the same minute, and each one counts.recent(player, match_id) -> int: the number of actions for that key inside the window (0 for an unknown key).
In this part, events arrive with non-decreasing minutes.
record("ana", 1, 1); record("ana", 1, 3); record("ana", 1, 3); record("bo", 7, 5)
recent("ana", 1) -> 3 # T=5
record("bo", 7, 12) # T=12, window [3,12]
recent("ana", 1) -> 2
record("bo", 7, 13) # window [4,13]
recent("ana", 1) -> 0
recent("bo", 7) -> 3
Part 2: live sessions
A player can have several client sessions (phone, PC, ...).
ping(player, session_id, minute): a heartbeat.logout(player, session_id, minute): the session ends now.live(player) -> int: the number of the player's sessions whose latest heartbeat is inside the window and that have not logged out since then.
Events are still in order, and pings and logouts advance T too.
ping("ana", "s1", 20); ping("ana", "s2", 22)
live("ana") -> 2
logout("ana", "s1", 25)
live("ana") -> 1
ping("bo", "x", 32) # window [23,32]: s2's last ping (22) has expired
live("ana") -> 0; live("bo") -> 1
Part 3: late events
Events may now arrive out of order. T is still the maximum seen. An event whose minute is already older than T - 9 when it arrives is ignored entirely (return False from that call, True otherwise). Because arrival order means nothing now, a ping and a logout for the same session at the same minute count as logged out. Keep the memory bound.
record("cy", 4, 30); record("cy", 4, 25) # 25 is late but inside [21,30]
recent("cy", 4) -> 2
record("cy", 4, 18) -> False # too old, ignored
record("dee", 1, 35) # window [26,35]
recent("cy", 4) -> 1
ping("cy", "a", 33); logout("cy", "a", 34); ping("cy", "a", 31) # late ping doesn't reopen
ping("cy", "b", 35); logout("cy", "b", 35) # same-minute tie: logged out
live("cy") -> 0
ping("cy", "c", 36)
live("cy") -> 1
Hints
Hint 1
With non-decreasing minutes, which stored item will leave the window next? You only need one global queue to find it.
Hint 2
Group repeated actions in the same minute into one bucket per key, so memory depends on the window length, not the event count.
Hint 3
For sessions, push expiry entries without removing old ones, and skip stale entries when you pop them (lazy deletion).
Hint 4
With late events, a FIFO no longer has the oldest item at its head. What structure always does? And when is it safe to forget a logout?
Follow-up questions
- The window becomes 24 hours at second resolution. How do you keep memory bounded (coarser buckets, approximate counts)?
- How would you add top_k_matches(k): the k busiest matches in the window right now?
- How would you shard this across machines when one process can't keep up, given that T is global?
- What changes if late events up to 2 minutes behind T must be accepted even after the window has moved past them?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.