system-design · intermediate

Availability — Nines, Error Budgets, and Redundancy

The Central Question

Consider an API endpoint running on the CoreLab platform (corelab.com):


System availability is not a subjective feeling. It is a precise operational ratio measuring how often a service is reachable and capable of executing its intended function.

This lesson answers one central question: How do engineers calculate system availability using MTBF and MTTR, model serial vs parallel availability decay, enforce SRE error budgets, and design multi-AZ architectures that achieve Four Nines (99.99%) availability?


Defining Availability: Time-Based vs Request-Based

Availability is measured across two distinct operational paradigms:

flowchart TD
  subgraph Time-Based Uptime Measurement
    T1[Total Monthly Window: 720 Hours] --> T2[Total Outage Time: 43.2 Minutes]
    T2 --> T3["Availability = 99.9% Uptime"]
  end

subgraph Request-Based Success Measurement
R1[Total Valid Requests: 5,000,000] --> R2[Failed 5xx Requests: 5,000]
R2 --> R3["Availability = 99.9% Success Rate"]
end

Figure 1: Comparing time-based uptime against request-based success metrics.

1. Time-Based Availability (Uptime Ratio)

Time-based availability measures operational uptime relative to Mean Time Between Failures (MTBF) and Mean Time To Repair (MTTR):

$$\text{Availability}_{\text{time}} = \frac{\text{MTBF}}{\text{MTBF} + \text{MTTR}} \times 100\%$$

Where:


If a system runs cleanly for 720 hours ($\text{MTBF} = 720\text{ hours}$) and takes 43.2 minutes ($\text{MTTR} = 0.72\text{ hours}$) to recover from a crash:

$$\text{Availability}_{\text{time}} = \frac{720}{720 + 0.72} = \frac{720}{720.72} \approx 99.9\%$$

2. Request-Based Availability (API Success Rate)

Time-based availability treats a 1-minute outage at 3:00 AM (5 users online) identically to a 1-minute outage at 2:00 PM (50,000 users online). Request-based availability measures the ratio of successful requests to total valid requests:

$$\text{Availability}_{\text{request}} = \frac{\text{Successful Requests}}{\text{Total Valid Requests}} \times 100\%$$

$$\text{Availability}_{\text{request}} = \frac{4,995,000}{5,000,000} \times 100\% = 99.9\%$$

Request-based metrics reflect true user experience because they scale directly with user volume.


The Language of "Nines" and Downtime Budgets

Engineers state availability targets in "nines" — standard target percentages ranging from two nines ($99\%$) to five nines ($99.999\%$).

gantt
    title Allowed Monthly Downtime Windows Across Nines Targets
    dateFormat  mm:ss
    axisFormat %M:%S
    section 99% (Two Nines)
    7 Hours 12 Mins Downtime :crit, n2, 00:00, 43:12
    section 99.9% (Three Nines)
    43 Minutes 12 Seconds :active, n3, 00:00, 04:19
    section 99.99% (Four Nines)
    4 Minutes 19 Seconds :done, n4, 00:00, 00:25

Figure 2: Monthly allowed downtime windows corresponding to target Nines.

Target NameAvailability %Allowed Downtime per Month (30 Days)Allowed Downtime per Year (365 Days)Allowed Failed Requests (per 1M Requests)
Two Nines99%7 hours, 12 minutes3.65 days10,000
Three Nines99.9%43 minutes, 12 seconds8.76 hours1,000
Four Nines99.99%4 minutes, 19 seconds52.56 minutes100
Five Nines99.999%25.9 seconds5.26 minutes10

How Serial Dependencies Multiply Unavailability

When a client request must traverse a series of dependent microservice components, total availability is not the average of component availabilities. Serial availability is the product of each component's individual availability.

flowchart LR
  Client[Client Browser] -->|Req| GW[API Gateway: 99.9%]
  GW -->|Req| App[App Server: 99.9%]
  App -->|Req| DB[(Database: 99.9%)]
  
  style GW fill:#fff3cd,stroke:#ffebaa
  style App fill:#fff3cd,stroke:#ffebaa
  style DB fill:#fff3cd,stroke:#ffebaa

Figure 3: A serial request chain where overall availability degrades with every added dependency.

The Serial Availability Formula

For a system requiring $N$ independent serial components to all succeed:

$$A_{\text{total}} = \prod_{i=1}^{N} A_i = A_1 \times A_2 \times A_3 \times \dots \times A_N$$

$$A_{\text{total}} = 0.999 \times 0.999 \times 0.999 = 0.997003 \approx 99.7\%$$

Connecting three "Three Nines" services in series results in a system that delivers only 99.7% availability (~2.1 hours of downtime per month). Every mandatory serial dependency pulls total availability down.

Raising Availability via Parallel Redundancy

To counteract serial degradation, systems deploy redundant instances in **parallel**. When components operate in parallel (where only one component must succeed for the system to function), parallel availability is calculated as:

$$A_{\text{parallel}} = 1 - \prod_{i=1}^{N} (1 - A_i) = 1 - (1 - A_1)(1 - A_2)$$

$$A_{\text{parallel}} = 1 - (1 - 0.99)(1 - 0.99) = 1 - (0.01 \times 0.01) = 0.9999 = 99.99\%$$

Running two 99% available instances in parallel produces a 99.99% available tier.


Operational SRE Framework: SLI, SLO, and Error Budgets

High-availability engineering relies on three core Site Reliability Engineering (SRE) constructs: SLI, SLO, and Error Budget.

flowchart TB
  subgraph Measurement & Targets
    SLI[SLI: Measured Signal - % HTTP 200s < 300ms]
    SLO[SLO: Targeted Goal - 99.9% over 30 Days]
  end
  subgraph Budget Management
    EB[Error Budget: 0.1% Allowed Failures]
    EB -->|Budget > 0%| Ship[Feature Velocity: Fast Deploys]
    EB -->|Budget Exhausted| Freeze[Deploy Freeze: Focus on Stability]
  end
  
  SLI -->|Evaluated against| SLO
  SLO -->|Derives| EB

Figure 4: Operational control loop connecting SLIs, SLOs, and feature deployment speed.

1. Service Level Indicator (SLI)

An **SLI** is a quantifiable metric measuring real-time service performance.

2. Service Level Objective (SLO)

An **SLO** is the target percentage for an SLI agreed upon by product managers and engineering teams over a specific time window (e.g., 30 rolling days).

3. Error Budget

An **Error Budget** is the exact mathematical allocation of unreliability permitted before product deployments are halted to focus on stability:

$$\text{Error Budget} = 100\% - \text{SLO}$$


Complete Worked Example: Go Multi-AZ Active Health Check Checker

Let's inspect a production Go health monitor for the CoreLab platform (corelab.com) that continuously issues active health probes across multi-AZ instance pools to automate fast failover.

package main

import (
"context"
"fmt"
"net/http"
"sync"
"time"
)

type InstanceHealth struct {
URL string
IsHealthy bool
ConsecFails int
LastCheck time.Time
}

type MultiAZHealthMonitor struct {
mu sync.Mutex
instances map[string]*InstanceHealth
maxFailures int
checkInterval time.Duration
}

func NewHealthMonitor(urls []string, maxFails int, interval time.Duration) MultiAZHealthMonitor {
m := &MultiAZHealthMonitor{
instances: make(map[string]
InstanceHealth),
maxFailures: maxFails,
checkInterval: interval,
}
for _, url := range urls {
m.instances[url] = &InstanceHealth{URL: url, IsHealthy: true}
}
return m
}

func (m *MultiAZHealthMonitor) Start(ctx context.Context) {
ticker := time.NewTicker(m.checkInterval)
go func() {
for {
select {
case <-ticker.C:
m.probeAll(ctx)
case <-ctx.Done():
ticker.Stop()
return
}
}
}()
}

func (m *MultiAZHealthMonitor) probeAll(ctx context.Context) {
m.mu.Lock()
urls := make([]string, 0, len(m.instances))
for u := range m.instances {
urls = append(urls, u)
}
m.mu.Unlock()

client := http.Client{Timeout: 2 * time.Second}

for _, url := range urls {
req, _ := http.NewRequestWithContext(ctx, "GET", url+"/healthz", nil)
resp, err := client.Do(req)

m.mu.Lock()
inst := m.instances[url]
inst.LastCheck = time.Now()

if err != nil || resp.StatusCode != http.StatusOK {
inst.ConsecFails++
if inst.ConsecFails >= m.maxFailures && inst.IsHealthy {
inst.IsHealthy = false
fmt.Printf("[FAILOVER ALERT] Node %s declared UNHEALTHY (Fails: %d). Removed from LB!\n", url, inst.ConsecFails)
}
} else {
resp.Body.Close()
if !inst.IsHealthy {
fmt.Printf("[RECOVERY ALERT] Node %s recovered HEALTHY. Restored to LB!\n", url)
}
inst.IsHealthy = true
inst.ConsecFails = 0
}
m.mu.Unlock()
}
}


Failure Modes and Engineering Mitigations

Failure ScenarioRoot CauseSystem SymptomDetection SignalMitigation Strategy
1. Serial Dependency Chain CollapseLinking 5 mandatory microservices in a single blocking HTTP call path.Total availability drops to 99.0% despite all services having 99.8% individual targets.Latency traces show cascading serial blocking calls.Decouple non-essential calls using async message queues and local caches; reduce serial depth.
2. Shallow Health Check BlindnessHealth endpoint returns HTTP 200 if process is up, even when DB pool is dead.Load balancer sends traffic to instances returning HTTP 500 on business routes.High 5xx rate on user APIs while LB reports 100% backend health.Implement readiness checks that verify database connectivity and critical resource pools.
3. Single-Zone Data Center OutageCompute and database instances deployed exclusively in one Availability Zone.Cloud data center power loss causes 100% total application downtime.Total TCP connection drop across all public endpoints.Deploy multi-AZ infrastructure with automated cross-zone DNS and load balancing.
4. Error Budget Burn SpikeDeploying un-tested schema migration that locks the primary database table.Database locks for 20 minutes, burning 50% of monthly error budget.High database lock wait metrics and API gateway 504 Gateway Timeout spikes.Enforce blue/green deployments, online schema migrations, and automated canary rollbacks.

What You Should Remember

  1. Availability is a ratio, not a feeling: Measure availability using request-based success ratios ($\frac{\text{Successful Requests}}{\text{Total Requests}}$) rather than simple server pings.
  2. Every Nine gets exponentially harder: Four Nines ($99.99\%$) permits only 4.3 minutes of downtime per month. Achieving it requires full automation.
  3. Serial dependencies multiply unavailability: $A_{\text{total}} = A_1 \times A_2 \times A_3$. Every mandatory serial hop reduces total system availability.
  4. Parallel redundancy restores availability: $A_{\text{parallel}} = 1 - (1 - A_1)(1 - A_2)$. Running independent replicas in parallel masks single-instance crashes.
  5. Manage stability with Error Budgets: Your Error Budget ($100\% - \text{SLO}$) dictates how much operational risk you can afford before freezing feature releases.

Glossary of Terms

TermDefinition
AvailabilityThe percentage of time or valid requests that a system successfully processes without error.
MTBF (Mean Time Between Failures)The average operational duration a system runs cleanly between crashes.
MTTR (Mean Time To Repair)The average duration required to detect a failure, reboot a process, and restore traffic routing.
SLI (Service Level Indicator)A specific quantitative metric measuring service performance (e.g. HTTP success rate).
SLO (Service Level Objective)A target percentage set for an SLI over a designated rolling time window.
Error BudgetThe total allowable margin for service failure ($100\% - \text{SLO}$) over a measurement period.
Availability Zone (AZ)One or more isolated data centers equipped with independent power, networking, and cooling.

Practice Scenario and Self-Assessment

Architecture Scenario

You are designing an API Gateway for a mobile banking application (`corelab.com`). The checkout service depends on:
  1. Authentication Service (Availability: $99.9\%$)
  2. Account Ledger API (Availability: $99.95\%$)
  3. Fraud Detection Engine (Availability: $99.0\%$)
**Questions**:
  1. If all three services are executed in a strict serial chain, calculate the maximum possible overall availability for a checkout request.
  2. How can you redesign the call to the Fraud Detection Engine so that its 99.0% availability does not degrade the core checkout path?

Interactive Self-Assessment

5,000 failed requests.

500 failed requests.

50,000 failed requests.

50 failed requests.

Overall serial availability is the product of individual component availabilities.

Load balancers automatically average serial availabilities.

Serial dependencies increase total system redundancy.

HTTP status codes reset failure probabilities at each hop.


What to Learn Next

Track: Reliability and Operations

Next: Checksums & Data Integrity

Series: Reliability & Failover

  1. Availability — Nines, Error Budgets, and Redundancy (this guide)
  2. Reliability — Correct Results Under Stress
  3. Fault Tolerance — Keep Working When Parts Fail
  4. Failover — Switching to a Healthy Spare
  5. Multi-Region Failover — Surviving a Region Outage
  6. Disaster Recovery — RPO, RTO, and Backups That Work

All series

By Shubham Jain

All articles · Study paths

Shubham Jain · Learning Lab