A lockstep pairing of scan events and weight readings loses one weight reading; what do all the later pairs look like?
answer
- pairs are defined by position, not identity
- one loss shifts every later pair
- well-formed results, wrong contents
- no signal, because a loss is not one
- carry a key and verify it before emitting
basics
~20 sEvery later pair is shifted by one: each scan is combined with the next item's weight. Pairing matches by position in arrival order, not by identity, so the results stay well formed and no failure is signalled — the terminal validates each item against its neighbour's weight forever.
solid answer
~50 sLockstep pairing takes the front value of each queue, so the pairs are defined by **arrival position**, not by anything in the values themselves. Lose one weight reading and the two queues are permanently offset by one: scan `n` is paired with weight `n+1` for the rest of the run, and the last scan waits for a partner that never comes. Nothing detects this. Every pair is structurally valid, both values are well formed, no failure is signalled, and the backlog does not grow — it just sits one value deep. The terminal now compares each item against the following item's weight, so it rejects correct items and accepts wrong ones with complete confidence. The fix is to stop pairing by position: carry a shared correlation key in both values and verify it in the combining step, failing loudly on a mismatch.
code
pseudocode · 12 lines// positional pairing: emits a plausible but wrong pair after a loss
on both queues non-empty:
emit combine( take_front(scans), take_front(weights) )
// key-checked pairing: drift becomes a failure at the first bad pair
on both queues non-empty:
scan = take_front(scans)
weight = take_front(weights)
if scan.line_id != weight.line_id:
emit failure("pairing drifted at line " + scan.line_id)
else:
emit combine(scan, weight)go deeper
Remember that pairing matches the first waiting value from each side. Nothing in the values decides the match, so losing one value on one side shifts every later pair by one.
Explain why nothing is reported: a lost value is not a signal, and every emitted pair is structurally valid. Then name the fix — match on a key carried in both values rather than on arrival order.
Show how you would find this in production: per-source in and out counts, a check that the two fronts agree on a shared identifier, and a distinction between a constant one-value offset and a growing backlog from a rate mismatch.
Decide when positional pairing is allowed at all. Across independent sources it is an unchecked invariant that fails silently and corrupts data, so the standard worth setting is that any cross-source join carries a correlation key and fails loudly on mismatch.
## Positional pairing has no notion of identity Lockstep pairing is defined entirely on order: keep a queue per source, and when every queue is non-empty take the front of each and emit the combination. Nothing in that rule inspects the values. It cannot, because it is generic over whatever the sources carry. The correctness of "this scan goes with this weight" is therefore not a property the join enforces — it is an **assumption** you made about the two sources when you chose the rule, namely that they will produce values one-for-one, in the same order, forever. That assumption is fine when both sources are derived from the same upstream event inside one process. It is fragile the moment the two feeds are independent: a sensor that misses a reading, a delivery that is dropped by a lossy buffer, a device that restarts, a filter upstream that silently rejects one value. ## The drop shifts everything after it With scans `S1..S4` and weights `W1..W4`, where `W2` is lost: | pair emitted | scan | weight | is it right? | |---|---|---|---| | 1 | `S1` | `W1` | correct | | 2 | `S2` | `W3` | wrong — the third item's weight | | 3 | `S3` | `W4` | wrong — the fourth item's weight | | — | `S4` | (waiting) | never emitted | Three things are true at once, and each of them is what makes this a production incident rather than a bug caught in review: - **The results are well formed.** Every pair carries a real scan and a real weight. Schema validation passes, a type check passes, and a downstream consumer has no way to tell pair 2 from pair 1. - **Nothing is signalled.** A lost value is not a signal. Only a completion or a failure is, and neither happened. The join is healthy and the log is clean. - **The output is one short and then stalls.** The final scan sits in its queue with no partner, so the last item of the transaction simply never appears. On a self-service terminal this is the worst possible failure shape: the machine confidently compares each item against the next item's weight, so it flags correct items as suspicious and lets mismatched ones through, and the operator's screen shows no fault at all. ## Two different faults that look alike Do not conflate a **one-off offset** with a **rate mismatch**: - A single lost value leaves a **constant** backlog — one value permanently waiting in one queue. Memory does not grow; the results are merely wrong. - A source that persistently produces fewer values than its partner leaves a **growing** backlog: the surplus queue expands for as long as the pipeline runs, and the symptom is climbing memory rather than wrong pairs. They demand different responses. The first is a correctness defect in how you join; the second is a flow-control problem about a producer outrunning its partner. ## Making the join verifiable 1. **Carry a shared key in both values.** If each scan and each weight reading carry the same transaction-line identifier, the combining step can compare them and turn silent drift into an explicit failure at the first bad pair. 2. **Join on the key rather than on position.** Hold values in a small map by key and emit when the key is complete, with a bounded wait so an unmatched value is reported instead of retained forever. 3. **Make the invariant checkable.** Track a monotonically increasing sequence in each source and assert that both fronts agree; the assertion costs almost nothing and converts an invisible fault into a loud one. 4. **Detect the stall.** A join whose output rate falls to zero while inputs still arrive is a signal in itself, and it is observable from outside the pipeline without any operator that understands the values. ## When positional pairing is genuinely safe - Both sources are derived from **one** upstream event, so their counts cannot diverge by construction. - Exactly one value per source per unit of work is guaranteed, with no filtering, no sampling and no dropping buffer anywhere between the source and the join. - Loss is impossible on the path — in-process, no network, no lossy overflow strategy. - The values are interchangeable, so a shift would be harmless: aggregating totals, not matching identities. Outside those conditions, positional pairing is an unchecked assumption dressed as a data-flow rule, and the day it breaks it breaks quietly.
- Does the lost value make the queues grow without bound?No. A single loss leaves a constant offset of one value in one queue, so memory is unaffected — only the contents of the pairs are wrong. Unbounded growth is a different fault: one source persistently producing more values than its partner, which expands the surplus queue for as long as the pipeline runs.
- Would switching to latest-value combination fix the drift?No. It removes the stall, because it never waits for a partner, but it still matches by recency rather than identity: each result carries whichever weight happened to arrive most recently, which after a loss is just as likely to be the wrong one. Only a key-based match fixes correctness.
- How would you detect the drift from outside the pipeline?Count values in and results out per source. Positional pairing guarantees one result per value per source, so a persistent gap between the scan count and the result count, or an output rate that falls to zero while inputs still arrive, is visible without understanding the values at all.
It is the missed buttonhole: the shirt still buttons all the way down, every button sits in a hole, and only the collar at the end reveals that everything was one off.
saying these in an interview costs you the question
- Says the join will signal an error when the counts diverge
- Thinks the pairing resynchronises once the queues drain
- Believes a lost value makes the backlog grow without bound
- Claims latest-value combination fixes the mismatch
- Assumes malformed output would be caught by schema validation
- Treats positional pairing as safe across independent sources