skip to content

Producers That Cannot Slow

Some producers cannot be asked to wait, so flow control moves into the adapter that wraps them. Interviewers use it to check that you treat demand as a contract, not a law of physics.

on this pageshow

questions

4

A fixed-interval clock source emits a tick whether or not the consumer asked for one — where must flow control live instead?

level: middleimportance: must knowfreq 60%

answer

  1. demand is a contract, not physics
  2. the tick does not wait
  3. flow control moves outward
  4. the adapter is where the choice lives
  5. discard, overwrite latest, bounded hold

basics

~20 s

Flow control moves into the adapter wrapping the source. A clock cannot wait for demand, so the adapter decides each tick's fate: discard it, overwrite a stored latest value, or hold it in a bounded buffer.

solid answer

~50 s

Demand signalling assumes the producer has somewhere to put the pause — a loop it can stop running, a read it can defer. A fixed-interval clock has nowhere to put it: the next tick is defined by wall-clock time, not by who is listening. So the flow-control decision moves outward, into the adapter that presents the clock as a stream. That adapter is the first component in the process that speaks the demand protocol, so it tracks outstanding demand itself, and when a tick arrives with none, it applies a policy chosen in advance: discard the tick, overwrite a single stored latest value, hold it in a bounded buffer and fail if the bound breaks, or fold arrivals into an aggregate. The point is that the loss becomes a decision made once, at the boundary, instead of something the pipeline discovers later as a growing queue.

code

pseudocode · 21 lines
pseudocode
outstanding = 0        # values the consumer has asked for
hasLatest = false

on request(n):         # travels upstream from the consumer
    outstanding = outstanding + n
    releaseIfPossible()

on tick(value):        # the clock calls this; it will not wait
    if outstanding > 0:
        emit(value)
        outstanding = outstanding - 1
    else:
        latest = value          # declared policy: keep only the newest
        hasLatest = true
        discarded = discarded + 1

function releaseIfPossible():
    if hasLatest and outstanding > 0:
        emit(latest)
        hasLatest = false
        outstanding = outstanding - 1

go deeper

for a junior

Recall that a stream consumer asks for a bounded number of values, and that some sources — a timer above all — have no way to honour that request.

for a middle

Explain that the adapter, not the source, tracks outstanding demand, and name what it can do with a value that arrives when demand is zero.

for a senior

Show that the loss policy is decided before the pipeline is built, and that discards are counted and exported rather than inferred from a growing queue.

for a principal

Weigh what the business actually needs from the feed — every event, the freshest value, or a steady cadence — because that, not the tooling, decides what the boundary may throw away.

## Demand assumes the producer has somewhere to put the pause In a demand-driven stream, the consumer tells the producer how many values it is ready for, and a well-behaved producer never emits more than that outstanding amount. The protocol works for most producers because they have a place to absorb the wait: a loop they simply stop running, a file they stop reading from, a paged result whose next page is fetched only when asked. Waiting costs such a producer nothing, and the values are still there when demand returns. Some producers have nowhere to put the pause at all: - **A periodic timer.** The next tick is defined by wall-clock time. Nothing about it exists before the instant it happens, and no request from a consumer moves that instant. - **A feed owned by another party.** The values are produced by someone else's process for their own reasons. There may be no channel on which to say "fewer, please", and where one exists it is a negotiation between systems, not a stream operation. - **A callback interface.** The only thing such an interface knows how to do is call you when it has something. It offers no way to say "call me five more times, then stop". - **A source that samples the physical world** on its own schedule. The world does not subscribe. The shared property is not speed. A slow feed you cannot pause has exactly the same problem as a fast one — it just takes longer to show. The property is that **the producer has no way to honour demand**, so demand is a contract someone else must keep on its behalf. ## The contract does not disappear; it moves outward The adapter that presents such a source as a stream is the first component in your process that speaks the demand protocol. That makes it the last point at which the loss is still a *choice* rather than an accident. Concretely, the adapter does three things: 1. **Track outstanding demand.** Add to a counter when a request arrives from downstream; subtract when a value is emitted. 2. **Compare on every arrival.** When the source delivers a value, check whether any demand is outstanding. 3. **Apply a declared policy when it is not.** Never improvise at the moment of pressure, and never simply emit anyway. Step three is the whole subject. The adapter that skips it has not escaped the decision — it has only pushed it into a stage that was never designed to make it. ## The honest policies at a boundary | Policy | What the consumer gets | What is lost | Fits when | |---|---|---|---| | Discard on arrival | Only values that had demand behind them | Everything that arrived while demand was zero | The consumer samples a continuous quantity | | Keep only the latest | The newest value at the moment demand returns | Every intermediate value | Only the current state matters | | Bounded hold | Every value until the bound is reached, then a failure | Nothing, until the bound breaks | Bursts are bounded and each value counts | | Fold into an aggregate | A summary covering every arrival | The individual values, not the information | A count or total is what the consumer wanted | The last row is the one candidates forget. If the consumer wants a total rather than each event, the adapter can accumulate arrivals and emit the accumulator when demand appears: the emission rate is bounded by demand, and nothing the consumer cares about is lost. ## What happens when the decision is skipped - **Emit anyway.** The adapter accepts demand and ignores it. The excess now lands on whichever downstream stage is slowest, and the symptom surfaces far from the cause. - **Hold without a bound.** The decision is not avoided, only deferred — until memory makes it for you, at the worst moment, with no choice about which values are lost. - **Block the thread the source calls you on.** Sometimes available, rarely wise: the pause lands inside a component you do not own, and the loss usually reappears somewhere you cannot see or measure. - **Leave the loss silent.** Whatever policy is chosen, discards that are not counted make a quiet period and a discarded period indistinguishable, and every downstream total silently assumes it saw everything. ## What an interviewer is listening for The weak answer is "you cannot apply backpressure to a timer", full stop. The strong answer accepts that the source will not slow, and then moves immediately to the boundary: who tracks demand, what the policy is, what it costs, and how the loss is made visible. A candidate who says "I would decide at the adapter what we are willing to lose, and export a counter for it" has understood that demand is a contract the adapter must keep honestly, not a law of physics the source is obliged to obey.

  • What does the adapter owe the consumer if it decides to discard ticks?
    Visibility. A discard made at the boundary should be counted and exported, and where the consumer can act on it, marked in the stream as a gap or a count of skipped intervals. Without that, the consumer cannot tell a quiet period from a period whose values were thrown away, and every downstream calculation silently assumes it saw everything.
  • A colleague says the real fix is to make the clock tick less often. When is that right?
    When the interval is a parameter you own and the consumer genuinely needs less resolution — then it is configuration, not flow control, and the loss decision disappears. It fails when the cadence is fixed by another party or by what the consumer must display, and it fails whenever the mismatch is bursty rather than constant, because a slower fixed interval still outruns a consumer that stalls.

A metronome keeps its beat whether or not the pianist can keep up; waiting is simply not one of the things it does. Anyone who needs fewer beats has to decide which ones to ignore.

saying these in an interview costs you the question

  • Says backpressure is impossible here, so nothing can be done
  • Claims a smaller request lengthens the clock's interval
  • Adds an unbounded hold and calls the source demand-aware
  • Treats any value discarded at the boundary as automatically a bug
  • Assumes downstream operators absorb the excess on their own
open as a page

Turnstile scans arrive faster than the counting consumer can take them — how do you choose the adapter's boundary policy?

level: seniorimportance: must knowfreq 52%

basics

~20 s

Let the consumer's need decide. If every event must be counted, hold a bounded buffer and fail when it fills, or aggregate in the adapter; if only the newest value matters, overwrite; if a steady cadence is enough, release the latest value on a clock.

open as a page

An adapter over a push feed accepts demand but emits every value regardless — what does that break?

level: seniorimportance: should knowfreq 44%

basics

~20 s

A dishonest adapter breaks the guarantee the rest of the pipeline is sized against: downstream stages assume no more than outstanding demand arrives. The excess then lands wherever the pipeline is slowest, so the symptom appears far from its cause.

open as a page

Several teams each wrap a push feed that cannot be slowed — what standard do you set for their adapters?

level: principalimportance: should knowfreq 34%

basics

~20 s

Standardise the invariants, not the policy: no adapter exceeds outstanding demand, every loss policy is declared rather than emergent, discards are counted and exported, and no hold is unbounded. Which policy each feed uses stays with the team that owns its consumer.

open as a page