51 lines
2.2 KiB
Markdown
51 lines
2.2 KiB
Markdown
|
|
# ADR-002: Request ID Propagation via X-Request-ID Header
|
||
|
|
|
||
|
|
**Status:** Accepted
|
||
|
|
**Date:** 2026-03-18
|
||
|
|
|
||
|
|
## Context
|
||
|
|
|
||
|
|
In a distributed system, a single inbound request may fan out to multiple downstream service
|
||
|
|
calls. Without a shared correlation identifier, tracing a request across service logs requires
|
||
|
|
matching timestamps or other heuristics. A request ID, propagated as an HTTP header
|
||
|
|
(`X-Request-ID`), lets logs across services be correlated by a single value.
|
||
|
|
|
||
|
|
The `logz` module owns the request ID context key (ADR-003 global: context helpers live with
|
||
|
|
data owners). `httpclient` depends on `logz` and should use its helpers rather than define its
|
||
|
|
own context key.
|
||
|
|
|
||
|
|
## Decision
|
||
|
|
|
||
|
|
Inside the retry function in `Do`, before executing each request attempt, the client reads
|
||
|
|
the request ID from the context using `logz.GetRequestID(req.Context())`. If a non-empty
|
||
|
|
value is present, it is set as the `X-Request-ID` header on the outgoing request:
|
||
|
|
|
||
|
|
```go
|
||
|
|
if id := logz.GetRequestID(req.Context()); id != "" {
|
||
|
|
req.Header.Set("X-Request-ID", id)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
The header is set on every retry attempt, not just the first, because the same `*http.Request`
|
||
|
|
object is reused across retries.
|
||
|
|
|
||
|
|
If no request ID is present in the context (the ID is the zero string), the header is not
|
||
|
|
set. This is verified by `TestClient_Do_NoRequestID`.
|
||
|
|
|
||
|
|
## Consequences
|
||
|
|
|
||
|
|
**Positive:**
|
||
|
|
- Request IDs flow automatically to downstream services without any caller boilerplate.
|
||
|
|
- Correlation across service boundaries works with no additional middleware.
|
||
|
|
- The integration is testable: `TestClient_Do_InjectsRequestID` verifies end-to-end
|
||
|
|
propagation using `logz.WithRequestID` and an `httptest.Server`.
|
||
|
|
|
||
|
|
**Negative:**
|
||
|
|
- `httpclient` takes a direct import dependency on `logz`. This is accepted per ADR-001
|
||
|
|
(global) which permits direct imports between framework modules.
|
||
|
|
- The header name `X-Request-ID` is hardcoded. Projects that use a different header name
|
||
|
|
(e.g. `X-Correlation-ID`) cannot configure this without forking the client.
|
||
|
|
- Header propagation only works when the caller places the request ID in the context via
|
||
|
|
`logz.WithRequestID`. Requests built without a context carrying a request ID will not
|
||
|
|
have the header set.
|