Bounded Request

Table of Contents

+#+filetags: :pattern:nats:request: +#+created: 2026-10-14 +#+updated: 2026-10-14 +#+pattern_group: request

This page defines the nats pattern Bounded Request in the NATS service patterns language. It specialises Timeout for request/reply on this bus.

1. Also known as

  • NATS: request timeout.
  • The pattern language: Timeout.

2. Context

A caller sends a request to a subject and waits for the reply on a private inbox. The responder may be absent, slow, or dead, and the bus reports none of those as an error by itself.

3. Problem

How long does a caller wait for an answer that may never come?

4. Forces

  • A responder that is up but slow and a responder that does not exist look the same to a caller with no deadline: both wait forever.
  • A deadline shorter than the responder's real latency turns healthy answers into failures.
  • A request holds a thread, a connection and often a user's attention.
  • The default must be one value, or the same call behaves differently depending on which overload it bound to.

5. Solution

Every request passes a deadline, directly or by the single shared default. The client throws when it expires, and the caller reports it:

// The default lives in one place, so no overload is quietly different.
inline constexpr std::chrono::milliseconds default_request_timeout =
    std::chrono::seconds(30);

// A caller that must wait longer, or fail faster, says so.
const auto reply = session.authenticated_request(
    subject, payload, provision_request_timeout);   // 120s

Three rules:

  • One default for the whole platform. `default_request_timeout` is the single source, so a caller cannot get a different deadline by accident.
  • A stated exception is deliberate. A call that passes its own timeout says why in the code beside it: a long provisioning step, a snapshot query.
  • The timeout is set against the responder's real latency, not a round number, so a slow-but-healthy answer is not reported as a fault.

6. Failure handling

Concern What the pattern does
Errors An expired deadline throws; the caller must report it, not swallow it.
Timeouts This pattern is the timeout. Distinguish it from no-responders; see Classify Transport Failure.
Retries A timeout may be retried, but only where the work is idempotent.
Idempotency A retry re-sends; the responder must absorb the duplicate.
Observability The subject and the timeout value are logged, so a deadline can be matched to a responder's latency.

7. Pitfalls

  • An unbounded wait. One absent responder hangs the caller forever.
  • A deadline below the responder's p99. Healthy answers are reported as failures and retried, adding load to an already busy service.
  • A per-call magic number. The same logical call behaves differently in two places, and neither is wrong on its own.
  • Retrying a timed-out write. The first attempt may have succeeded; the retry must be safe. See Idempotent Message Identity.

8. Resulting context

Benefits
A caller always terminates, and its failure names which deadline it hit.
Liabilities
A deadline is a guess about the responder; a wrong one either hangs or gives up early.

9. In ORE Studio

`default_request_timeout` is 30 seconds and is the default of every client and helper overload. Exception sites pass their own: 120 seconds for a provisioning request, shorter for a step-result fetch.

10. Related patterns

11. Sources

12. See also

  • NATS service patterns — the structure note that orders this pattern language, and where to read this page in it.

Emacs 29.3 (Org mode 9.6.15)