How would you guarantee that every request is accounted for and its resources released, whichever stage of the dispatch pipeline it exits at?
answer
- position can be skipped, completion cannot
- two layers: arrivals and dispatches
- the net must not cause the outage
- acquire and release in one element
- test one request per exit path
basics
~20 sAnchor the guarantee at the pipeline's completion boundary, not at a position in the chain that an earlier exit can skip. Cover what the pipeline never sees at the layer in front, and never let the accounting path fail the request.
solid answer
~50 sStart from the failure mode: any element placed at a position in the chain is skipped whenever something in front of it exits first, so "we log in a hook" is not a guarantee. The reliable anchors are the framework's **completion boundary** — the callback or outermost wrapper that runs however dispatch ended, including the error stage — and, for release, the framework's own teardown. Cover what the pipeline cannot see: requests rejected at the adapter boundary for oversized headers or bodies, and connections dropped before a response. Then harden it: the accounting code must not depend on anything that plausibly just failed, must swallow its own errors rather than turning a served request into a 500, and must be tested with one request per exit path. Ownership is the last call: a platform-owned wrapper covers everyone uniformly but sees less route detail than a per-service hook.
go deeper
Take away the core fact: a step placed late in the chain does not run for requests that ended earlier, so it cannot be the place a must-always guarantee lives.
Explain which anchors actually run on every exit — completion callbacks, an outermost wrapper, framework teardown — and why the acquiring element is the right place to release.
Design and prove it: enumerate the exit paths, test one request per exit, contain failures inside the accounting path, and reconcile the pipeline's counts against the layer in front.
Own the tradeoff between a uniform platform-mandated outer layer and per-service hooks, decide how the floor is enforced across many teams, and publish the residual gaps rather than asserting completeness.
## Why this is a design question, not a configuration one A dispatch pipeline has many exits: no route matched, a gate answered early, binding rejected the input, the handler raised, rendering blew up, the client vanished mid-response. A guarantee that "every request is logged, metered, and leaves nothing behind" has to hold on **all** of them, and the naive implementation — a hook near the end of the chain — holds on the fewest. The exits that matter most operationally are exactly the ones that skip it. ## Step 1: anchor on completion, not on position The first move is to stop thinking in terms of "where in the chain" and start thinking in terms of **when dispatch ends**. Most frameworks offer at least one of: - a **completion callback** attached to the request, invoked once dispatch finishes however it finished; - an **outermost wrapper position** that every request passes through on the way in and the way out; - a framework-owned **teardown** that releases the request's resources unconditionally. These are the only positions whose execution does not depend on how far forward the request got. Everything else is best-effort. ## Step 2: know what the pipeline never sees Accounting anchored inside the pipeline still misses traffic that never enters it: 1. Requests refused at the adapter boundary — header or body limits exceeded, a malformed message that could not be parsed into a request at all. 2. Connections opened and abandoned, or dropped by the client while the response was streaming. 3. Traffic the fronting layer answered itself, if one exists. So the honest design is two-layer: the pipeline accounts for what it dispatched, and the layer in front accounts for arrivals. When the two counts disagree, the difference is itself a signal worth alerting on. ## Step 3: make the guarantee unable to break what it protects Accounting code is code; it fails too. Three rules keep a safety net from becoming the outage: - **No dependency on what likely just failed.** If the guarantee logs through the same client, pool or serializer that the failing request was using, it fails on precisely the requests you needed it for. - **Errors are contained.** A failure inside the accounting path must be counted and dropped, never propagated into the response. A request that was served successfully must not become a 500 because its access log line could not be written. - **Bounded work.** Buffering, retrying, or blocking on a downstream sink inside a per-request path converts a slow sink into a latency incident. Hand off to a bounded queue and drop with a counter when it is full. ## Step 4: separate the two guarantees "Accounted for" and "resources released" look alike but have different owners. | | Accounting | Release | |---|---|---| | Owner | Platform, usually one wrapper | Whoever acquired the resource | | Failure mode | Silent under-reporting | Leak, then exhaustion under load | | Detection | Cross-layer count comparison | Pool saturation, slow degradation | | Rule of thumb | Anchor at completion | Release in the same element that acquired | Release is safest expressed as **acquire and release in the same element**, so the outbound half of the element that opened the resource closes it. A central cleanup step that releases things it did not acquire has to know everything anyone might have opened, which is a coupling that decays. ## Step 5: verify by exercising the exits A guarantee nobody has tested on the interesting path is an assumption. Build a test suite that deliberately produces one request per exit — unmatched path, early rejection by a gate, binding failure, handler exception, failure raised inside the error stage, client disconnect mid-body — and assert that each produced exactly one accounting record and left the resource pool at its starting size. This is cheap and it is the only evidence that distinguishes a guarantee from an intention. ## Step 6: decide ownership and accept the tradeoff The remaining decision is organisational. A **platform-owned outermost wrapper** gives uniform coverage that no team can forget, but it sits outside route resolution and therefore knows less about each request on the way in — it typically learns the matched route only on the way out, if at all. A **per-service hook** sees everything but is one refactor away from being mounted in the wrong position, and drift across dozens of services is the normal outcome. The usual answer is a thin mandatory outer layer that guarantees the floor, plus optional per-service enrichment on top, with the floor's correctness enforced by a shared test rather than by a document. State the residual gaps explicitly: what a hard process kill loses, what an overflowing queue drops, and what the fronting layer counts that the pipeline cannot. A guarantee with known, named holes is far more useful than one asserted without them.
- Why is comparing the fronting layer's count with the pipeline's count worth doing?Because the gap is exactly the traffic the pipeline could not see: refusals at the adapter boundary, abandoned connections, and anything the front answered itself. A sudden widening of that gap is often the first visible sign of an attack, a limit misconfiguration, or a crash loop that never reaches dispatch.
- What does a platform-owned outermost wrapper give up compared with a route-scoped hook?Detail on the way in. Sitting outside route resolution, it cannot label a request by its matched route or by route-level configuration before dispatch, and it may only learn the route on the way out. It buys uniformity and unskippability at the price of context.
- How should the accounting path behave when its downstream sink is slow?Hand off to a bounded in-memory queue and drop with an explicit counter when it is full, never block the request. A per-request path that waits on a sink converts that sink's latency into the service's latency, and its outage into the service's outage.
- Where should a resource opened by a hook be released?In the outbound half of the same element that opened it, so it is released on every path that entered that element and on no path that did not. Centralised cleanup for resources someone else acquired requires global knowledge of every acquisition and drifts out of date.
saying these in an interview costs you the question
- Proposes a hook at the end of the chain and calls it a guarantee.
- Ignores requests rejected before the pipeline ever dispatched them.
- Lets the accounting path fail the request it was meant to record.
- Blocks the request on a slow logging or metrics sink.
- Centralises release of resources it never acquired.
- Claims full coverage without testing any early-exit path.