Timeouts and deadline propagation

Distributed systems Mid-level Python 3.13+ PostgreSQL 17+ Reviewed By Best Latest Apps

Predict the output

The handler has a 0.1 s budget and calls a dependency that takes 1 s. What prints?

Python 3.13
import asyncio


async def slow_dependency() -> str:
    await asyncio.sleep(1)
    return "data"


async def handler() -> str:
    try:
        async with asyncio.timeout(0.1):
            return await slow_dependency()
    except TimeoutError:
        return "fallback"


print(asyncio.run(handler()))

Options
Hint 1 of 1

What does asyncio.timeout raise when it expires?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A fallback

Correct. asyncio.timeout cancels the call at 0.1 s and raises TimeoutError, which the handler catches.

asyncio.timeout sets a deadline on the block. When it passes, the inner await is cancelled and the context manager turns that into TimeoutError, which the handler catches to return the fallback.

Why not the others?
  • B data. The dependency needs 1 s; the budget is 0.1 s.
  • C It raises CancelledError. asyncio.timeout converts the cancellation into TimeoutError.
  • D It prints nothing and hangs. The timeout cancels the sleep.

30-second answer

Every network call needs a timeout, or one slow dependency ties up threads until the caller falls over. Set connect and request timeouts from the dependency's measured latency (for example a little above its p99), not a round default. Across a chain, pass a deadline rather than fixed timeouts: each service uses the time left, so a downstream call never keeps working after the user's request has already timed out.

Practise saying it · 35 s. No recording, just a timer

How do you set timeouts across services?

Say your answer out loud, then check which of these points you covered:

  • Every call has one
  • Based on measured latency
  • Propagate a deadline
  • Inner shorter than outer
  • Timeout is an unknown outcome

Deep dive

gRPC carries deadlines natively; for HTTP, pass the remaining budget in a header.

Inner timeouts should be shorter than outer ones, or the outer layer gives up while the inner call keeps running.

Cancel work when the deadline passes; in Python asyncio, asyncio.timeout() cancels the awaited task.

A timeout doesn't mean the operation failed on the server; it may have succeeded. Retries need idempotency.

Common mistakes

  • Library defaults with no timeout (Python requests waits forever unless you pass timeout=).
  • Outer timeout 5 s, inner 30 s.
  • Treating a timeout as a definite failure and charging twice on retry.

Senior take & follow-ups

Say every call needs one, base it on measurements, propagate deadlines, and remember a timeout is an unknown outcome.

Follow-up questions

Think of your answer first, then open the hint.

  1. What does a timeout tell you about the operation?

    Show hint

    Nothing certain: it may have completed.

  2. How do you pick the number?

    Show hint

    From measured latency percentiles plus headroom, and the caller's own budget.

Suggest a fix if something here is wrong or out of date.