skip to content

A dashboard panel that subscribes to a live shared feed after it starts shows nothing until the next update - why?

level: middleimportance: should knowfreq 54%

answer

  1. the feed did not wait for you
  2. you joined a sequence in progress
  3. nothing is retained by default
  4. attaching decides what you see
  5. retain a latest value for newcomers

basics

~20 s

A live source runs independently of its subscribers and hands each one only what arrives while it is attached. Everything emitted before the panel subscribed was delivered to whoever was attached then and kept nowhere, so the panel waits for the next value.

solid answer

~40 s

A live, already-running source is one sequence shared by everyone attached to it, and attachment is what decides what you see: values emitted before the panel subscribed went to the subscribers of that moment and were not retained. So the panel has nothing to draw until the source produces its next value, and how long that takes is set by the upstream rhythm, not by the panel. Resubscribing does not help, because there is no earlier run to repeat. The fixes are to give the shared stage a small **replay buffer** so a newcomer receives the most recent values, to keep a **current-value cell** that always holds the latest reading, or to have the panel fetch a snapshot on open and then join the live feed for deltas.

code

pseudocode · 9 lines
pseudocode
// live source: produces regardless of who is attached
every 60 seconds:
    reading = measure()
    for each s in subscribers:      // empty list means the reading is dropped
        s.emit(reading)

panel_a.subscribe()                 // t = 0s   -> first value at t = 60s
// ... reading emitted at t = 60s goes to panel_a only
panel_b.subscribe()                 // t = 70s  -> blank until t = 120s

go deeper

for a junior

Hold on to the basic asymmetry: joining a feed that is already running gets you what comes next, not what already happened.

for a middle

Explain that the source produces on its own schedule and retains nothing by default, and name the retention options - a replay buffer, a latest-value cell, or a snapshot fetched on open.

for a senior

Diagnose rather than patch. Show that the blank interval equals the upstream cadence, and decide retention once at the shared boundary rather than letting each panel invent a workaround.

for a principal

Treat what a newcomer is entitled to as part of the published contract for the feed, because consumers build rendering and alerting on that assumption and cannot discover it from the type they are handed.

## What live means here A **live (hot) source** is already producing. Its work was started by something other than this panel - a timer, an upstream connection, a process that pushes readings - and it keeps going regardless of how many subscribers are attached, including none. Subscribing to it is not starting work; it is **joining a sequence in progress**. That single difference explains the blank panel. The values the panel wanted were produced before it joined. They were handed to whoever was attached at that moment, and then, unless something deliberately retained them, they were gone. The panel is not broken and the feed is not broken; the panel is simply seeing the part of the sequence that overlaps its own attachment. ## Why waiting is the only thing the panel can do by itself - There is **no earlier run to repeat**. A cold source would give a newcomer its own execution from the beginning; a live source has one execution and it is already past that point. - **Detaching and attaching again changes nothing**, because it re-joins the same in-progress sequence rather than restarting anything. - The delay before the first value is the **upstream cadence**, not panel latency: a feed that emits once a minute leaves a newcomer blank for up to a minute. - With **nobody attached at all**, values still get produced and still get dropped - the source does not pause to wait for an audience. This is why a live feed alone is a poor fit for a panel that must render immediately on open. The feed carries change; the panel needs state. ## Three ways to give a late panel something to draw 1. **A replay buffer on the shared stage.** The stage keeps the last few values, or the values from the last interval, and delivers them to a newcomer before live ones. The panel paints instantly. The cost is memory and the fact that a replayed reading arrives with no visible age. 2. **A current-value cell.** The shared stage keeps exactly one value, the most recent, and every newcomer receives it immediately on attaching. This suits a panel whose job is to show a present state - a gauge, a status, a count - rather than a history. 3. **Snapshot then join.** The panel asks for a fresh reading through an ordinary request when it opens, renders that, and attaches to the live feed for subsequent changes. This gives the freshest possible start and keeps the feed purely about deltas, at the cost of two paths and a small window where a change can arrive twice or be missed unless the snapshot and the feed are sequenced carefully. | Approach | What the newcomer sees first | Main cost | |---|---|---| | Nothing retained | the next value produced, whenever that is | a blank panel for one upstream interval | | Replay of recent values | the last few values, immediately | memory, plus values whose age is invisible | | Current-value cell | one value, the latest, immediately | no history, and the value may be stale | | Snapshot then join | a freshly fetched reading | two code paths and an overlap to reconcile | ## How this differs from the cold case The same dashboard code behaves in opposite ways depending on which kind of source is behind it. On a cold source, a panel that opens late gets **everything**, because it starts its own run. On a live source, a panel that opens late gets **only what follows**, because it joined one run already in progress. Neither is a bug; they are the two ends of the same axis, and the panel's rendering logic has to be written for the one it is actually attached to. ## Designing the contract instead of patching the panel The durable fix is to state, at the boundary where the feed is published, what a newcomer is entitled to: - **Nothing until the next change** - acceptable for a log tail or an alert ticker, where old items genuinely do not matter. - **The latest value, always** - the right contract for state-shaped data, and the one most dashboard panels actually want. - **The last n values, or the last interval** - right for a small chart that needs a shape, not just a point. Once that is written down, the panel stops carrying defensive code for a case it cannot see, and the shared stage carries the retention policy that its consumers were quietly assuming. A common error is to let each panel invent its own workaround - one keeps a local cache, another polls on open, a third renders a spinner forever - when a single retention decision on the shared source would have answered all three.

  • Would resubscribing the panel to the live feed recover the values it missed?
    No. Attaching again joins the same in-progress sequence, and the earlier values were not stored anywhere to be re-delivered. Recovery has to come from retention on the shared side - a replay buffer or a latest-value cell - or from fetching a snapshot through a separate request when the panel opens.
  • When is it right to leave a live feed with no retention at all?
    When old items have no value to a newcomer: an alert ticker, a log tail, a keystroke or pointer stream. Retention would show a viewer events that already happened and may already have been handled, so blank-until-next-change is the honest contract and it costs nothing to keep.

saying these in an interview costs you the question

  • Blames the panel and adds a retry loop instead of retention
  • Thinks resubscribing to a live source replays what was missed
  • Assumes a live source pauses when nobody is attached
  • Believes every shared source keeps recent values automatically
  • Confuses a slow first value with a network or rendering delay