skip to content

With three gRPC server-side interceptors composed into a chain, which one sees the request first and the status last?

level: middleimportance: must knowfreq 52%

answer

  1. nesting, not a queue
  2. outside in, then inside out
  3. outermost sees the status last
  4. a rejecter hides everything inside it
  5. registration-to-nesting is library convention

basics

~20 s

The outermost one. A chain of gRPC interceptors nests around the service method, so pre-work runs outside in and post-work unwinds inside out: the outermost wrapper sees the request first and the call's status last.

solid answer

~50 s

The chain composes as nested wrappers, with the service method innermost. On the way in, each wrapper does its pre-work and then invokes the next one, so the **outermost** sees the request first. On the way out the stack unwinds, so the **innermost** sees the outcome first and the outermost sees the call's `grpc-status` last. Two consequences matter in practice. A wrapper that rejects a call never invokes what it wraps, so anything nested inside it never runs — an audit wrapper placed inside an authentication wrapper will have no record of the calls that were denied. And a wrapper only measures what is nested inside it, so a latency wrapper has to be outermost to see what the caller experienced. Which end of your registration list becomes the outermost layer is a library convention, not a protocol rule: check it rather than assuming.

code

pseudocode · 11 lines
pseudocode
chain = outermost(middle(innermost(serviceMethod)))

call arrives
  outermost   enter          <- request observed first
    middle    enter
      innermost enter
        serviceMethod runs -> PERMISSION_DENIED (7)
      innermost exit         <- status observed first
    middle    exit
  outermost   exit           <- status observed last
status written to the caller

go deeper

for a junior

Know that wrappers nest around the service method rather than running as a flat list, and that the one on the outside is the first to see the request.

for a middle

Explain both directions: pre-work outside in, post-work inside out, so the outermost wrapper sees the request first and the call's status last. Note that registration-to-nesting mapping varies by library.

for a senior

Reason about placement from what each wrapper must observe — a rejecting wrapper hides everything inside it, a timing wrapper measures only what it wraps, and a mapping wrapper changes what outer counters record.

for a principal

The ordering is a shared contract across every service in the estate. Standardising it buys comparable metrics and a complete audit trail; the cost is that changing the order later is a coordinated migration, not a local edit.

## Composition, not a queue It is tempting to picture interceptors as a queue of steps the server runs one after another. They are not. A chain composes by **nesting**: each wrapper is handed the next stage and decides when — or whether — to invoke it. Written out, three wrappers around a method are `outermost(middle(innermost(serviceMethod)))`. That single fact settles the ordering question in both directions: - **Inbound, outside in.** The outermost wrapper's pre-work runs first, then the middle's, then the innermost's, then the service method. - **Outbound, inside out.** The innermost wrapper's post-work runs first, then the middle's, then the outermost's, and only then is the call's status written to the caller. So the wrapper that sees the request first is the same one that sees the status last. A wrapper's two halves sit at opposite ends of the call. ## Which end of the registration list is outermost This is the part candidates most often state too confidently. The nesting is a property of the pattern; the mapping from *the order you registered things* to *which one ends up outermost* is a convention of the library you are using, and implementations differ — some make the first registered the outermost layer, others the last. A protocol answer cannot settle it, and neither can a memory of one implementation. The honest interview answer is: describe the nesting, then say that the registration-to-nesting mapping is verified, not assumed — and that the cheapest way to verify it is to install two wrappers that each log on entry and on exit and read the order once. ## What the order actually changes ### A rejecting wrapper hides everything inside it A wrapper that decides a call is unauthenticated returns without invoking the next stage. Everything nested inside it — every other wrapper and the service method — simply does not run for that call. In a court records e-filing service that is a live defect, not a curiosity. If the audit wrapper is nested inside the authentication wrapper, the audit trail contains every accepted filing and **no** rejected attempt, which is precisely the half an auditor asks for. Put the recording wrapper outside the rejecting one, and it observes both. ### A measuring wrapper only measures what it wraps A timing wrapper measures the span between its own pre-work and its own post-work. Nested innermost, it measures the service method and nothing else. Nested outermost, it measures the method plus every wrapper between — which is what the caller actually waited for, and therefore the number worth alerting on. ### A rewriting wrapper changes what the outer ones observe If an inner wrapper maps a handler's failure onto a different status, wrappers outside it never see the original. An outer metrics wrapper counts the mapped value. That is fine when it is deliberate and invisible when it is not — one reason a mapping wrapper and a counting wrapper are usually the same wrapper, or adjacent ones. ## A practical ordering | position | wrapper | why there | |---|---|---| | outermost | timing and outcome counting | spans everything the caller waited for, and sees the status actually returned | | next | attribution and audit | records rejected calls as well as accepted ones | | next | authentication / coarse rejection | stops unauthenticated work before anything expensive runs | | innermost | correlation context for the handler | closest to the method that consumes it | | — | the service method | | This is one defensible ordering, not the ordering. Interviewers are looking for the reasoning — *what does this wrapper need to be able to see?* — rather than a memorised stack. ## Client-side chains behave the same way A chain of client-side wrappers nests identically around the outgoing call: the outermost stamps its metadata first and observes the terminal status last. The one asymmetry worth naming is that a client-side wrapper that short-circuits does so before anything goes on the wire, so the server has no idea the call was ever attempted — there is no rejected-call record on the far side to reconcile against. ## What to say when asked 1. The chain nests; the service method is innermost. 2. Pre-work runs outside in; post-work unwinds inside out. 3. A wrapper only sees, measures and can reject what is nested inside it. 4. The registration-order-to-nesting mapping is a library convention worth verifying once.

  • Where do you put a wrapper that must measure the latency the caller experienced?
    Outermost, so its span covers every other wrapper as well as the service method. Nested inside an authentication wrapper it would exclude the time spent checking credentials, which is time the caller waited for all the same.
  • An audit wrapper records every accepted filing and no rejected one. What is wrong?
    It is nested inside the wrapper that rejects unauthenticated calls, and a rejecting wrapper returns without invoking what it wraps. Move the audit wrapper outside the rejecting one so it observes both outcomes, and have it record the rejection status rather than only successes.
  • If an inner wrapper maps a failure onto a different status, what do the outer ones count?
    The mapped status. Post-work unwinds inside out, so by the time an outer wrapper observes the outcome the mapping has already happened. Outer counters therefore reflect what the caller will receive, not what the handler originally produced.

A chain of wrappers is a set of nested envelopes. The outer envelope is opened first on the way in, and it is the last one sealed on the way out.

saying these in an interview costs you the question

  • Describes the chain as a flat queue of sequential steps
  • Says the outermost wrapper sees the status first as well
  • States a registration order as if the protocol defined it
  • Assumes every wrapper runs even when an outer one rejects
  • Thinks a wrapper can measure work nested outside itself