skip to content

How does a gRPC caller's deadline reach the server, and what does a grpc-timeout value actually look like?

level: middleimportance: must knowfreq 60%

answer

  1. one small field, integer plus letter
  2. no space, no fractions, no suffix word
  3. six units and the case matters
  4. capital M is minutes, small m milliseconds
  5. at most eight ASCII digits

basics

~10 s

It travels as one request metadata field, grpc-timeout, whose value is a positive integer of at most eight ASCII digits followed immediately by a single unit letter: H, M, S, m, u or n.

solid answer

~40 s

The caller holds a **deadline** — an instant. The client library converts it, at the moment the request is written, into the time still remaining, and sends that duration in the `grpc-timeout` request field. The value is a positive integer of **at most eight ASCII digits** with a **one-letter unit** glued directly to it and no space: `H` hours, `M` minutes, `S` seconds, `m` milliseconds, `u` microseconds, `n` nanoseconds. So `900m` is 900 milliseconds and `5M` is five minutes — the case of the letter is the whole difference. There are no fractions: express a finer bound by choosing a finer unit. The eight-digit ceiling is why a long bound has to be sent in a coarse unit rather than in nanoseconds.

code

http · 8 lines
http
:method POST
:scheme https
:path /pharmacy.v1.Eligibility/CheckPrescription
:authority eligibility.svc.internal
te: trailers
content-type: application/grpc+proto
grpc-timeout: 900m
grpc-accept-encoding: identity, gzip

go deeper

for a junior

Recognise grpc-timeout in a request and read its value: digits then one unit letter, no space. Knowing that 900m is 900 milliseconds is enough at this stage.

for a middle

Explain the grammar and the conversion: a caller's deadline is an instant, the field carries the remaining duration, and the six unit letters include a case-sensitive pair where M is minutes and m is milliseconds.

for a senior

Point at the ways this bites in production: a unit-case mistake that silently removes a bound, and the fact that the server's view of the deadline is slightly shorter than the caller's because transit time is already spent.

for a principal

Consider where bounds are chosen at all. Per-call deadlines set ad hoc by each caller produce an unowned distribution of numbers; deciding how they are derived and reviewed is a standards question, not a coding one.

## From an instant to a duration The caller thinks in **deadlines**: 'this answer is worthless after 10:04:31.200'. The wire carries **durations**: 'you have 900 milliseconds'. The client library does the conversion at the moment the request headers are written, computing the time still remaining and emitting it as the `grpc-timeout` request metadata field. That is not a cosmetic difference. A duration written into the request is only correct for the instant it was written, which is why the field is recomputed per request rather than stored once and repeated. A **timeout**, by contrast, is a duration each piece of code invents for itself — start a stopwatch, give up after N seconds. The two behave identically for exactly one call and diverge everywhere else. ## The grammar The value is deliberately tiny, because it has to be parsed on every single request: - a **positive integer**, written in ASCII digits, **at most eight of them**; - immediately followed by **one unit letter**, with no space and no punctuation between them. So `100m`, `1S`, `30S`, `2H` are all well-formed. `1.5S` is not — there are no fractions. `1500 m` is not — the space breaks it. `900000000000n` is not — twelve digits exceeds the eight-digit ceiling. ## The six unit letters | letter | unit | example | meaning | |---|---|---|---| | `H` | hours | `2H` | two hours | | `M` | minutes | `5M` | five minutes | | `S` | seconds | `30S` | thirty seconds | | `m` | milliseconds | `900m` | 900 milliseconds | | `u` | microseconds | `500u` | 500 microseconds | | `n` | nanoseconds | `250n` | 250 nanoseconds | The trap is in the middle of the table. **`M` is minutes and `m` is milliseconds**, and the two differ by a factor of 60,000. A field read as `5M` when `5m` was intended turns a five-millisecond bound into a five-minute one — an unbounded call in all but name, and it is a single keystroke away. ## The eight-digit ceiling and what it forces Eight digits caps the *number*, not the duration, so you buy range by moving to a coarser unit: 1. Decide the bound in whatever unit is natural — say two hours. 2. Check whether the value fits in eight digits in that unit. Two hours in nanoseconds is 7,200,000,000,000 — thirteen digits, far too many. 3. Step up until it fits: `7200S`, `120M` and `2H` all express the same bound and all fit comfortably. The practical effect is that fine units are for short bounds and coarse units for long ones, which is what you would have written anyway. Some client libraries pick the unit for you from the duration you supplied; the field on the wire is the same either way. ## Reading it in a capture At a pharmacy counter the whole interaction is worthless after about a second, so the eligibility call goes out with `grpc-timeout: 900m` sitting alongside `:path`, `te: trailers` and `content-type: application/grpc+proto` in the request metadata. Two things are worth noticing when you look at a real request: - The field is **ordinary request metadata**, not a special frame or a negotiated setting. It is present on requests that have a bound and absent on requests that do not — there is no 'unbounded' sentinel value to look for. - It is **per call**. On a long-lived connection carrying many concurrent calls, each one carries its own value, and they routinely differ. ## What the server does with it The server parses the value, adds it to its own clock and treats the result as the point past which the answer is not wanted. Two honest caveats follow. First, the two clocks are not the same clock, and the duration spent in transit is already gone by the time the server reads the field — so the server's view of the bound is slightly shorter than the caller's, which is the safe direction. Second, the field bounds **this** call at **this** server; it is not a budget the protocol automatically threads through anything the handler goes on to do.

  • Why does a gRPC client recompute grpc-timeout for every request instead of sending the same value?
    Because the caller holds a deadline — an instant — while the field carries a duration. The remaining time shrinks as the instant approaches, so the correct value is computed when the request headers are written. A fixed value re-sent unchanged is a timeout, not a deadline.
  • How would you express a two-hour bound in a gRPC grpc-timeout field, and why not in nanoseconds?
    As `2H`, `120M` or `7200S`. Nanoseconds would need thirteen digits and the value is limited to at most eight ASCII digits, so long bounds must be expressed in a coarser unit. All three spellings mean the same duration to the server.

saying these in an interview costs you the question

  • Reads grpc-timeout: 5M as five milliseconds
  • Thinks the field carries an absolute timestamp
  • Assumes a fractional value such as 1.5S is legal
  • Writes the unit as a word, for example 900ms
  • Treats a deadline and a per-call timeout as identical
  • Assumes any duration fits because digits are unlimited