← Back to Insights

Production investigation

Investigating Failures in Distributed Systems

By Team · Sun Mar 08 2026 · 5 min read

Investigating Failures in Distributed Systems

Investigating failures in distributed systems requires comprehensive visibility across multiple services. Failures often manifest as partial degradation or increased latency, not complete outages. Pinpointing the root cause involves correlating metrics, logs, and traces from various system components. Focus on critical request paths and their deviations from expected behavior. System state changes can propagate unexpectedly, so analyze temporal relationships between events.

Why This Happens

Distributed systems inherently introduce complexity. Components rely on network communication, which is unreliable. Asynchronous operations mean states diverge across services before convergence. Individual service failures can cascade, affecting upstream and downstream dependencies. Latency spikes in one service can exhaust connection pools in another. Retries can amplify load on an already struggling dependency. Frequent deployments also introduce new codepaths into this environment. Failure modes are often non-obvious and emergent properties of the system's interactions.

Investigation Process

  1. Identify the scope: Determine affected services and users. Use dashboards to confirm recent changes or anomalous metrics. Check for recent deployments or configuration updates.
  2. Examine critical request paths: Trace a problematic request from entry point to failure. Identify components involved in this specific flow.
  3. Collect high-level metrics: Review dashboards for latency, error rates, and resource utilization (CPU, memory, network, disk I/O) across all services on the critical path. Look for synchronized deviations.
  4. Deep dive into service logs: Filter logs for errors and unusual patterns within affected services. Correlate timestamps across logs to observe event order. Use request IDs for correlating traces.
  5. Analyze distributed traces: Utilize tracing tools to visualize request flow across services. Identify the exact service and span where latency increased or an error originated. Look for unexpected calls or missing spans.
  6. Bypass suspected components: If possible, temporarily route around or disable a suspected component. Observe if the problem resolves or shifts. This helps isolate the fault domain.
  7. Isolate resource contention: Check for shared resource saturation. Examples include database connection pools, message queue throughput, or external API rate limits.
  8. Review recent code changes: Consult version control for recent commits to affected services. Look for changes affecting data structures, network calls, or concurrency.
  9. Hypothesize and test: Formulate a specific hypothesis about the root cause. Devise a small, targeted test to validate or invalidate it. This could involve feature flags or rollback.

Practical Example

A global e-commerce platform received reports of intermittent checkout failures. Customers saw a generic 'payment failed' message. During Q4, transaction volume increased substantially. Initial checks showed payment service metrics were healthy. Database connections were not saturated. However, an increase in error rates appeared shortly after a new promotional coupon service deployment.

Investigation began by reviewing checkout service logs. Logs showed increasing 'timeout' errors when calling the coupon service. Distributed traces revealed that calls to the new coupon service consistently took longer than 500ms. The checkout service had a 300ms timeout configured for this dependency. The coupon service itself showed no resource contention. Its internal logic involved calling a legacy eligibility service. This legacy service was under-provisioned and unable to handle Q4 traffic from the new coupon service. The checkout service was timing out before the coupon service could return any response. This cascaded into payment failures. The team temporarily disabled the new coupon service via a feature flag. This restored full checkout functionality. They then scaled the legacy eligibility service and reintroduced the coupon service.

Preventing Recurrence

Preventing recurrence involves architectural, testing, and operational improvements.

  • Dependency timeouts: Implement strict, service-level timeouts for all external calls. Configure these to fail fast.
  • Circuit breakers: Deploy circuit breaker patterns around external dependencies. This prevents cascading failures and allows services to recover.
  • Load testing dependencies: Include dependency load tests in pre-production environments. Simulate high traffic scenarios against critical path components.
  • Enhanced observability: Improve distributed tracing granularity. Ensure all critical paths have full visibility into component calls. Instrument legacy services for better insights.
  • Service boundary contracts: Define clear API contracts and performance expectations between services. Enforce these through automated testing.
  • Chaos engineering: Regularly inject failures into non-production environments. Understand how the system reacts to component degradation.
  • Rollback strategies: Develop fast, reliable rollback mechanisms for all service deployments.

What Teams Usually Do Instead

Many teams immediately focus on the service reporting the error. They restart services without isolating the root cause. This often provides temporary relief but does not solve the underlying issue. Teams might also add more compute resources to the 'failing' service. This often masks the actual problem of an upstream or downstream dependency. Without systemic tracing, engineers waste time examining irrelevant logs. They operate on assumptions about service interactions rather than empirical data. This leads to recurring incidents. They also sometimes implement complex retry logic without backoff. This can exacerbate problems by increasing load on already struggling services.

Key Takeaways

  • Distributed failures are often symptom, not cause.
  • Trace critical paths end-to-end for visibility.
  • Correlate metrics, logs, and traces temporally.
  • Isolate faulty components using hypotheses and tests.
  • Implement timeouts and circuit breakers for resilience.

Related: how some teams handle this operationally