Observability in distributed systems requires correlation of events across processes: distributed tracing (assigning request IDs that span processes) and the happened-before relation allow reconstruction of causality for debugging. Tools like Jaeger or Zipkin implement tracing; understanding causality is essential for interpreting traces.
Trace a multi-step request (HTTP → service A → service B → database) by hand, assigning trace IDs and span IDs. Then examine how the tracing tool reconstructs the critical path and identifies where time was spent.
From the Chandy-Lamport algorithm, you know that capturing consistent global state in a distributed system is fundamentally hard — no single node can see everything at once. Observability is the practical discipline of making a distributed system's internal behavior visible enough to diagnose problems, even when you cannot pause the whole system and inspect it. It rests on three pillars: logs, metrics, and traces. Logs record discrete events. Metrics track numerical measurements over time. Traces follow individual requests as they flow across service boundaries.
The most important concept for distributed debugging is distributed tracing. When a user clicks "submit order" and that request touches an API gateway, an authentication service, an inventory service, and a payment processor, each service produces its own logs independently. Without correlation, you have four separate log streams with no way to connect them. Distributed tracing solves this by assigning a unique trace ID to the initial request and propagating it through every downstream call. Each service creates a span — a record of the work it performed, including start time, duration, and the trace ID. When you collect all spans sharing a trace ID, you can reconstruct the full request path as a tree: the API gateway span at the root, with child spans for each service call, nested to show which calls triggered which.
Tools like Jaeger, Zipkin, and OpenTelemetry implement this pattern by injecting trace context into HTTP headers, message queue metadata, or RPC frameworks. The key design decisions are sampling (tracing every request is expensive, so most systems trace a fraction — say 1% — of requests in production) and instrumentation (each service must be configured to create spans and propagate context). A well-instrumented system lets you answer questions like "why was this request slow?" by examining its trace and identifying which span consumed the most of the time. It also reveals dependency patterns, error propagation paths, and bottlenecks that are invisible in any single service's metrics or logs.
The deeper lesson connects back to causality. A trace is essentially a practical application of the happened-before relation: span A started before it called service B, which completed before A continued. The trace reconstructs a partial ordering of events across processes. But unlike Lamport timestamps, traces are designed for human consumption — they produce visual timelines and call graphs that engineers can inspect. The limitation is that traces only capture instrumented paths. If a background job or async message queue is not instrumented, the trace has a gap. Building effective observability means systematically closing those gaps so that the system's behavior can be understood from outside, without needing to reproduce the exact conditions that caused a failure.
No topics depend on this one yet.