Dissecting a GitHub Outage: Blast Radius and Localizing Failures in Microservices

Arpit Bhayani

Arpit Bhayani

Jul 06, 2022 • 7 min read

Play

Dissecting a GitHub Outage: Blast Radius and Localizing Failures in Microservices

Outages in distributed systems are inevitable. When operating at scale, the primary engineering goal is not to eliminate all hardware or software faults—an impossible feat—but to contain them. A partial outage where a non-critical subsystem degrades gracefully is acceptable; a catastrophic cascading outage that brings down an entire platform is not.

By examining a post-incident review from GitHub concerning an outage in GitHub Actions, we can extract vital system design principles regarding blast radius, shared database dependencies, zero-trust service authentication, and the limits of automated failover orchestration.


1. Incident Breakdown: What Happened at GitHub?

The Incident Timeline

At approximately 04:00 UTC on January 28, GitHub’s service monitors detected abnormal error rates impacting the Actions service.

Key observations from the post-mortem included:

  • Impact: Failure or delayed execution of queued GitHub Actions workflow jobs.
  • Recovery & Resilience: Once the underlying issue was resolved, jobs that were queued during the outage executed successfully.
  • Root Cause: An infrastructure error within GitHub’s SQL database layer impacted a core microservice that facilitates authentication and communication between internal Actions microservices.
  • Monitoring Anomaly: Telemetry failed to signal that the database was degraded, which prevented automated failover orchestrators from stepping in and significantly elongated Mean Time to Detect (MTTD) and Mean Time to Resolve (MTTR).
flowchart TD
    A[Git Push / Webhook] --> B[Message Broker / Queue]
    B --> C[GitHub Actions Core Runner]
    C --> D[Auth & Inter-Service Comm Microservice]
    D --> E[(SQL Database Layer)]
    E -.->|Silent Infrastructure Fault| F[Automated Failover Orchestrator]
    F -.->|Telemetry reported OK: No Failover Triggered| E
    D -->|Failure Cascades Upstream| C

2. Deep-Dive: Architectural Weaknesses & Key Insights

A. Decoupling via Asynchronous Buffers

A notable bright spot in this outage was that jobs queued during the downtime were not permanently dropped; they executed once the system recovered.

This behavior demonstrates the power of asynchronous messaging patterns:

  • Synchronous Paradigm: If external triggers (e.g., webhook pushes) synchronously wrote directly to the execution engine and its underlying database, the API would have returned HTTP 500s, discarding user jobs permanently unless clients retried.
  • Asynchronous Paradigm: By using an upstream persistent message broker (e.g., Kafka, RabbitMQ, SQS) as an ingestion buffer, GitHub decoupled job submission from job execution. The ingestion queue absorbed backpressure while internal workers were down, providing out-of-the-box fault tolerance.

B. The Single Point of Failure (SPOF) in Distributed Microservices

Externally, users perceive “GitHub Actions” as a unified service. Internally, it is an ecosystem composed of multiple microservices: runner orchestrators, premium runners, log aggregators, billing monitors, and dispatchers.

However, GitHub revealed that an infrastructure error in a SQL database layer took down an internal service powering authentication and communication between Actions services, halting everything.

Why Microservices Share Databases (and Why It’s Dangerous)

Microservices best practices dictate the Database-per-Service pattern. In practice, organizations frequently compromise on this due to:

  1. Financial Cost: Provisioning dedicated, highly available database clusters per microservice explodes infrastructure bills.
  2. Network Overhead & Latency: Cross-service joins require network roundtrips or complex event-driven data replication patterns.

When multiple services share a database cluster, or depend synchronously on a single core microservice connected to that cluster, that database becomes a single point of failure (SPOF). When it degrades, the blast radius encompasses all dependent services.

C. Zero-Trust Inter-Service Communication

Why would an internal service need an “authentication and communication” microservice?

Modern architectures enforce Zero-Trust Networks. Even within private subnets, services do not blindly trust network traffic:

  • If service AA wants to trigger an action on service BB, it must present an identity token (e.g., mTLS, signed JWTs).
  • This prevents rogue services or compromised worker nodes from performing unauthorized operations (e.g., an compromised worker spamming an internal notifications service).

However, placing an authentication checkpoint on the critical path of every inter-service RPC introduces a hard synchronous dependency. If the service issuing or verifying auth tokens fails due to database degradation, all downstream inter-service calls fail.


3. The Failure of Automated Database Failover

In standard database architectures featuring a primary-replica (master-replica) topology, a database node crash should not take an entire service down for hours.

sequenceDiagram
    autonumber
    participant Master as Master DB (Failing)
    participant Replica as Replica DB
    participant Orch as Health Orchestrator
    participant Svc as Microservice

    Master--xMaster: Partial failure / Latency spike / Table Lock
    Orch->>Master: Heartbeat check (Ping)
    Master-->>Orch: HTTP 200 / Pong (Telemetry appears normal)
    Note over Orch: Failover is NOT triggered
    Svc->>Master: Read/Write Transactions
    Master--xSvc: Connection Timeout / Locked Queries

How Automated Failovers Function

  1. An orchestrator (e.g., GitHub’s Orchestrator, Raft/Consensus-backed monitors) periodically probes the primary node via heartbeats, replication lag metrics, and query execution checks.
  2. When the primary is deemed unreachable or unhealthy, the orchestrator initiates failover: demoting the primary, electing a promotion-eligible replica, syncing WAL (Write-Ahead Logs), and updating discovery endpoints (DNS or service mesh routes).

Why the Automation Failed

In this incident, automated processes did not detect that the database was unhealthy, and monitoring telemetry showed no red flags:

  • Partial Failures / Gray Failures: The database daemon might still accept simple ping requests, while worker pools, internal query engines, or specific critical tables are completely locked or stalled.
  • Metric Blind Spots: Generic telemetry (CPU, disk I/O, simple ping) often reports “green” even when transaction throughput has collapsed or locks have cascaded.
  • The MTTD Impact: Engineers rely heavily on monitoring dashboards during an outage. When the telemetry dashboard erroneously indicates that the database is healthy, engineers look everywhere else first (network routes, application deployments, configuration changes), significantly delaying recovery.

Remediation Actions

When automated systems fail to trigger, operators are left with two primary levers:

  1. Manual Failover: Executing an operations runbook script to forcefully promote a replica and route traffic away from the problematic master node.
  2. Targeted Reboot: Rebooting the problematic primary instance to drop stale lock states and force client connections to drop and re-establish.

4. Architectural Takeaway: Blast Radius and Failure Localization

The fundamental lesson of this post-mortem is the necessity of localizing failures to constrain their blast radius.

Blast RadiusDegree of Synchronous Coupling\text{Blast Radius} \propto \text{Degree of Synchronous Coupling}

PropertySynchronous Architecture (Tight Coupling)Asynchronous Architecture (Loose Coupling)
Communication MediumREST, gRPC, direct TCP socketsEvent Brokers (Kafka, RabbitMQ, SQS)
Temporal DependencyCallee must be healthy at the instant the caller invokes itCallee processes events whenever it is healthy
Failure BehaviorUpstream caller blocks, exhausts thread pool, and crashesMessages buffer safely in the queue broker
Blast RadiusLarge: Outage cascades across the entire call chainSmall: Outage remains localized to the failing worker service

How to Minimize Blast Radius in Practice

  1. Prefer Event-Driven Over Direct RPC: Avoid multi-hop synchronous RPC chains (ABCDA \rightarrow B \rightarrow C \rightarrow D). Use message queues to convert operations into durable events whenever immediate consistency is not strictly required.

  2. Implement Circuit Breakers and Fallbacks: When calling an external service (like inter-service auth), apply the Circuit Breaker pattern. If the auth service is down, fall back to cached tokens with temporary TTL extensions or enter a read-only degraded mode instead of failing every operation.

  3. Deconstruct Monolithic Shared Databases: If multiple microservices must use the same physical database engine, isolate them using separate schemas, resource pools, and connection limits. A runaway query from service AA should never exhaust the database connection pool required by service BB.

  4. Audit Telemetry for “Gray Failures”: Ensure failover detection algorithms validate end-to-end transactional behavior (e.g., executing representative read/write synthetic transactions) rather than shallow OS-level pings.

Arpit Bhayani

Principal Engineer II at Razorpay - building Agent Studio, Ex-staff engg at GCP Memorystore & Dataproc, Creator of DiceDB, ex-Amazon Fast Data, ex-Director of Engg. SRE and Data Engineering at Unacademy. I spark engineering curiosity through my no-fluff engineering videos on YouTube and my courses