system-design · intermediate
Availability — Nines, Error Budgets, and Redundancy
The Central Question
Consider an API endpoint running on the CoreLab platform (corelab.com):
- When a customer clicks "Submit Order", they expect the service to process their request immediately. If the checkout API returns an HTTP
503 Service Unavailableerror or hangs indefinitely, the application is unavailable for that customer — regardless of how well it performed yesterday.
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:
- 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.
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\%$$
- If an API receives 5,000,000 valid HTTP requests over a month and 5,000 requests fail with HTTP
5xxserver errors:
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 Name | Availability % | Allowed Downtime per Month (30 Days) | Allowed Downtime per Year (365 Days) | Allowed Failed Requests (per 1M Requests) |
|---|---|---|---|---|
| Two Nines | 99% | 7 hours, 12 minutes | 3.65 days | 10,000 |
| Three Nines | 99.9% | 43 minutes, 12 seconds | 8.76 hours | 1,000 |
| Four Nines | 99.99% | 4 minutes, 19 seconds | 52.56 minutes | 100 |
| Five Nines | 99.999% | 25.9 seconds | 5.26 minutes | 10 |
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$$
- If an API Gateway ($99.9\%$), an Application Server ($99.9\%$), and a Database ($99.9\%$) are linked in series:
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)$$
- If an application server has an availability of $99\%$ ($0.99$), running two independent instances in parallel yields:
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.- Example: The proportion of
POST /checkoutrequests that return an HTTP200 OKstatus code in under 300 milliseconds, measured at the load balancer.
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).- Example:
POST /checkoutSLI will be $\ge 99.9\%$ over any 30-day rolling window.
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}$$
- For a $99.9\%$ SLO over 5,000,000 monthly requests, the Error Budget is 5,000 failed requests ($0.1\%$).
- If a bad configuration update causes 4,000 failed requests in a single afternoon, 80% of the monthly error budget is consumed. The engineering team must pause non-essential feature deployments and prioritize infrastructure reliability until the budget recovers.
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 Scenario | Root Cause | System Symptom | Detection Signal | Mitigation Strategy |
|---|---|---|---|---|
| 1. Serial Dependency Chain Collapse | Linking 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 Blindness | Health 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 Outage | Compute 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 Spike | Deploying 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
- 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.
- Every Nine gets exponentially harder: Four Nines ($99.99\%$) permits only 4.3 minutes of downtime per month. Achieving it requires full automation.
- Serial dependencies multiply unavailability: $A_{\text{total}} = A_1 \times A_2 \times A_3$. Every mandatory serial hop reduces total system availability.
- Parallel redundancy restores availability: $A_{\text{parallel}} = 1 - (1 - A_1)(1 - A_2)$. Running independent replicas in parallel masks single-instance crashes.
- 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
| Term | Definition |
|---|---|
| Availability | The 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 Budget | The 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:- Authentication Service (Availability: $99.9\%$)
- Account Ledger API (Availability: $99.95\%$)
- Fraud Detection Engine (Availability: $99.0\%$)
- If all three services are executed in a strict serial chain, calculate the maximum possible overall availability for a checkout request.
- 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
- Reliability — Correct Results Under Stress: Learn how to ensure data correctness and invariant preservation.
- Scalability — Vertical, Horizontal, and Elastic Growth: Explore how to scale instance capacity to maintain availability.
- Load Balancing — Algorithms and Layers: Master traffic distribution and active health probe routing.
Track: Reliability and Operations
Next: Checksums & Data Integrity
Series: Reliability & Failover
- Availability — Nines, Error Budgets, and Redundancy (this guide)
- Reliability — Correct Results Under Stress
- Fault Tolerance — Keep Working When Parts Fail
- Failover — Switching to a Healthy Spare
- Multi-Region Failover — Surviving a Region Outage
- Disaster Recovery — RPO, RTO, and Backups That Work
By Shubham Jain