Three steps nearest a job's source are each blocked most of the time handing output on; the fourth is not. Which is the bottleneck?
answer
- the symptom travels backwards
- the loudest step is the idle one
- stop at the first unblocked step
- all blocked means look at the write
- location is not cause
basics
~20 sThe fourth step. Backpressure propagates backwards towards the source, so the place you first notice resistance is not the place that caused it: the cause is the first step, walking away from the source, that is not itself blocked.
solid answer
~50 sBlocking travels the wrong way for intuition. When a step cannot accept records fast enough, the step feeding it fills its bounded in-job buffer and stops; that stopping fills the buffer behind it, and so on back to the read from the source. So resistance is visible at every step *behind* the constraint and not at the constraint itself, which is busy rather than waiting. The reading rule is: walk away from the source and stop at **the first step that is not itself resisting** — here, the fourth. Two edge patterns matter. If *every* step resists, the constraint is the last thing in the chain: the step that writes the job's output, or the destination it writes to. If *no* step resists and the job is still behind on unread input, the constraint is the read side.
go deeper
Remember the direction: blocking spreads backwards, towards the source, so the busiest-looking step is the wrong one to work on. The bottleneck is the first step that is not itself blocked.
Explain the chain of bounded buffers that makes the symptom travel, then give the reading rule and both edge cases — everything blocked points at the write or the destination, nothing blocked points at the read.
Show you can run the walk without a blocked-time figure, using buffer occupancy or per-step rates, and that you know a step whose own work is nearly as slow as the constraint will not report blocking even though it sits behind it.
The angle is where diagnosis stops and ownership begins. Locating the constrained step is cheap and mechanical; deciding whether the answer is more capacity, less work, or a renegotiated promise to consumers is the expensive call and is not the diagnostician's to make alone.
## Why the symptom appears in the wrong place Between each pair of adjacent steps in a job graph there is a **bounded in-job buffer** — a small in-memory queue of records the earlier step has produced and the later one has not yet consumed. When a step slows down, its input buffer fills. Once full, the step feeding it has nowhere to put its next record and stops. That step's own input buffer then fills, and its predecessor stops. This is **backpressure**: a constraint at one point in the graph reproduced as blocking at every point behind it, back to the read from the source. The consequence for diagnosis is the whole content of this subject. **The resistance you notice first is not the resistance that caused anything.** It is a symptom that has travelled. The constrained step is the one that is *not* blocked, because it is busy doing whatever makes it slow. ## The reading rule Order the steps from the source outwards and walk away from the source. Stop at **the first step that is not itself resisting**. Prefer that phrasing over "walk upstream" or "walk downstream", because the two words collide here: upstream in a dataflow means nearer the source, which is where the symptoms are, while people also say "downstream" for the external systems consuming the job's output. Naming the step by its property instead of its direction removes the ambiguity entirely. In the case given, steps one through three are blocked and step four is not, so step four is the constraint. Steps one to three have spare capacity; giving them more of anything changes nothing. ## The patterns and what each means | Pattern across the graph | Where the constraint is | What to ask next | |---|---|---| | Steps 1..k blocked, step k+1 not | Step k+1 | What is step k+1 spending its time on? | | Every step blocked, including the last | The step that writes the job's output, or the destination it writes to | Is the destination rate-limiting, or is the write itself expensive? | | No step blocked, unread input still growing | The read from the source, or the source's own supply rate | How many readers, and can the source serve more? | | Resistance appears and clears on a cycle | An intermittent cause at the first non-blocked step | A recurring heavy piece, a periodic flush, a consumer that throttles in windows? | ## The caveat that separates a middle answer from a rote one The rule is a reading of a pattern, not a law. A step behind the constraint will report blocking only if it can produce faster than the rate it is being held to. A step whose own work is nearly as slow as the constraint spends its time computing rather than waiting, so it shows little or no blocked time even though it sits behind the bottleneck. That is why you look at the *shape* across the whole graph rather than at one number, and why two steps that both look busy are worth measuring against each other before you conclude anything. ## When the figures are not there Not every engine in this family publishes a per-step blocked fraction, and the walk has to be done with whatever is available: - **Buffer occupancy per step**, where it is published: the buffers behind the constraint are full, the one after it is empty. - **Processed throughput per step, in records per second.** In a settled graph every step processes at the same rate, so equal rates tell you nothing by themselves; what distinguishes the constraint is that it is busy at that rate while its predecessors are idle at it. - **Elapsed time per step**, in a model where phases do not overlap. In **the two-phase disk-to-disk model**, each phase writes its whole output to shared storage before the next reads it, so there is no in-flight handover to block and no resistance to propagate at all: the bottleneck is simply the phase with the longest elapsed time. ## Location is not cause Finding the first non-resisting step ends this subject and begins another. That step may be slow because one piece of the input holds far more records than the rest, because its worker process is short of memory, because each record costs a great deal to compute, or because the destination it writes to accepts only so many writes per second. Those have separate owners and separate evidence. The discipline worth demonstrating is refusing to guess among them from the resistance figure alone — the figure named the step, and naming the step is all it can do. ## What this rules out It rules out the reflex of acting on the loudest symptom. The first step, nearest the source, usually looks the worst: it has been blocked the longest and its buffer is fullest. Every instinct says to work on it, and every hour spent there is wasted, because it is idle.
- Every step in the job reports resistance, including the last one. What does that mean?There is no step left to walk to, so the constraint is at the end: either the step that writes the job's output is expensive in itself, or the destination accepts writes more slowly than the job produces them. The way to separate the two is to observe the write step's time split — busy computing the write versus waiting on an acknowledgement from the destination.
- No step reports resistance, yet the job's unread input keeps growing. Where do you look?At the read side. Resistance only appears where a step has a consumer to push back against, so a constraint at the very first step produces none. Look at how many readers the job runs against the source, whether the source can serve more concurrently, and whether the read itself is doing work — decompression, parsing, filtering — that belongs to a later step.
- Why is 'walk upstream' a dangerous way to say this?Because upstream means nearer the source, which is where the symptoms are, not where the cause is; walking that way collects more symptoms. It is also confusable with the other everyday sense of downstream, meaning the external consumers of the job's output. Saying 'the first step that is not itself resisting' names the target by a property nobody can misread.
saying these in an interview costs you the question
- Starts work on the step nearest the source because it looks worst
- Says resistance propagates towards the destination rather than towards the source
- Assumes the bottleneck must be inside the job rather than the destination
- Treats equal per-step throughput as proof there is no bottleneck
- Names a cause from the resistance figure alone without inspecting the constrained step