tezvyn:

Propagating a correlation ID without parameter passing

AI-drafted, machine-checkedSource: interviewintermediate
WHAT IT TESTS

Ambient request-scoped context in async code.

OUTLINE

middleware reads or generates the header, stores it in a contextvars.ContextVar, service code reads it anywhere, and logging filters inject it.

WHAT THIS TESTS Whether you understand request-scoped ambient state in an asynchronous application, and specifically that the correct tool is contextvars, not globals or thread-locals. It also probes observability instincts.

A GOOD ANSWER COVERS Define a module-level ContextVar, for example correlation_id: ContextVar[str]. In middleware, read the X-Correlation-ID header from the request; if missing, generate a uuid4. Call correlation_id.set(value) before calling the downstream handler, capturing the returned token. Now any service function, repository, or logger called within that request can call correlation_id.get() and receive the right value without it being threaded through every signature. Add a logging.Filter that pulls the value from the ContextVar and attaches it to each LogRecord so all logs are automatically tagged. Optionally echo the ID back in the response header. The key property is that contextvars are copied per asyncio task, so concurrent requests do not see each other's values.

COMMON WRONG ANSWERS Storing the ID in a plain module global, which is shared across all in-flight requests and corrupts under concurrency. Using threading.local, which does not align with asyncio's single-thread-many-tasks model. Passing the ID as an explicit argument everywhere, which the question explicitly rules out.

LIKELY FOLLOW-UPS How do contextvars interact with run_in_executor or thread pools? How do you reset the var to avoid leakage? How would you propagate the ID to downstream HTTP calls?

ONE CONCRETE EXAMPLE Middleware: token = correlation_id.set(request.headers.get('x-correlation-id') or str(uuid4())); response = await call_next(request); response.headers['x-correlation-id'] = correlation_id.get(). A deeply nested service then logs logger.info('charging card') and the filter automatically stamps that line with the same ID, letting you trace one request across many functions.

Read the original → starlette.io

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.