โหมดมืด
บทที่ 4 — Reliability และ Resilience
ก่อนอ่าน — ต้องรู้อะไรมาก่อน?
บทนี้ต่อจาก บทที่ 1-3 — ควรเข้าใจ:
- Sync vs Async (บทที่ 3)
- Replication, Multi-region (บทที่ 1-2)
- Queue, Pub-Sub (บทที่ 3)
- คำว่า "fail", "downtime", "recovery"
หลังจบบท คุณจะ:
- ออกแบบ retry / timeout / circuit breaker ที่ถูก
- เข้าใจ idempotency + at-least-once delivery
- กำหนด SLI (Service Level Indicator — ตัวชี้วัด เช่น % availability), SLO (Service Level Objective — เป้าภายในของทีม), SLA (Service Level Agreement — สัญญากับลูกค้า มีบทลงโทษถ้าไม่ถึง) + error budget (งบความผิดพลาดที่ยอมให้เกิดได้)
- จัดการ disaster + chaos engineering
1. ปรัชญา — "Things will fail. Design for failure."
คำแปล: "ทุกอย่างพัง — ออกแบบให้พร้อมพัง" (reliability = รี-ไล-อะ-บิ-ลิ-ตี้ คือความน่าเชื่อถือของระบบ)
ในระบบเล็ก ๆ — failure เป็น "exception"
ในระบบใหญ่ — failure เป็น "norm"
1.1 ความจริงที่ engineer มักลืม
- Hard disk fail rate: ประมาณไม่กี่ % ต่อปี ขึ้นกับรุ่น/อายุ
(Backblaze = บริษัท backup ที่เปิดเผยสถิติ disk failure รายปีให้สาธารณะ. Backblaze Drive Stats Q1 2024 รายงาน annualized failure rate (AFR) ~1.4% สำหรับ HDD แบบ mix และ ~0.65% สำหรับ SSD — ตัวเลขล่าสุดที่ blog ของ Backblaze)
*(SSD/NVMe มี failure mode ต่างจาก HDD: SSD "ค่อย ๆ สึก" จาก P/E cycles ตามจำนวน write ไม่ใช่ mechanical fail แบบสุ่ม — NVMe เปลี่ยน profile อีกชั้น ไม่ควรเหมารวม)*
- Network partition เกิดขึ้นเป็นประจำใน data center ใหญ่ (ความถี่ที่แท้จริงต่างกันตามผู้ให้บริการ — Google Jupiter, Microsoft, AWS ต่างเคยรายงาน partition หลายระดับ)
- Bug ใน code: หลีกเลี่ยงไม่ได้
- Configuration mistake: เกิดบ่อยกว่าที่คิด
- Cloud provider outage: AWS us-east-1 ดาวน์ปีละหลายครั้ง
- DDoS attack: ทุกระบบเสี่ยงGoal: เมื่อ failure เกิด — degrade gracefully + recover automatically, ไม่ใช่ "crash + alert at 3 AM"
1.2 Fallacies of Distributed Computing — 8 ข้อ
ทีม Sun Microsystems รวบรวมไว้แบบค่อย ๆ ต่อยอด — Bill Joy + Tom Lyon (~1991, สี่ข้อแรก), Peter Deutsch (~1994, อีกสามข้อ), James Gosling (~1997, ข้อ 8) — engineer ใหม่ assume สิ่งเหล่านี้ ผิดทั้งหมด (ดู บทที่ 0 distributed-systems สำหรับประวัติเต็ม):
1. The network is reliable ← FALSE — packet drop เกิดเสมอ
2. Latency is zero ← FALSE — มี latency เสมอ
3. Bandwidth is infinite ← FALSE — bandwidth จำกัด
4. The network is secure ← FALSE — ทุก hop เสี่ยง
5. Topology doesn't change ← FALSE — server มา/ไป ตลอด
6. There is one administrator ← FALSE — หลายคน, หลายทีม
7. Transport cost is zero ← FALSE — egress แพง
8. The network is homogeneous ← FALSE — มี firewall, NAT, proxy→ Plan for all to be FALSE
2. Failure Modes — รู้จักศัตรู
2.1 Crash Failure
Service หยุดตอบ — process ตาย
✅ ดี: ตรวจจับง่าย (health check fail)
✅ Recovery: restart2.2 Performance Failure ⭐ ร้ายแรงสุด
Service ช้า แต่ยังตอบ
❌ ยากตรวจจับ — "ช้าหรือเปล่า? หรือแค่ busy?"
❌ บ่อยครั้งร้ายแรงกว่า crash
เพราะ thread block + resource exhausted + cascadeInsight: "Slow is the new down" — สำนวนอเมริกัน "X is the new Y" หมายถึง "X = ของใหม่ที่มาแทน Y" → แปลว่า "ช้า = ตายแบบใหม่" คือ service ช้ามีผลร้ายแรงพอ ๆ กับ service ตาย เพราะระบบไม่รู้จะ failover (health check บอกว่ายัง up อยู่)
2.3 Byzantine Failure
Byzantine (อ่าน "ไบแซนไทน์") = ความล้มเหลวแบบ "โกหก" — service ไม่ได้ตายเฉย ๆ แต่ยังตอบอยู่ ทว่าตอบ "ค่าผิด" ออกมา (ตรวจจับยากที่สุด เพราะดูเหมือนทำงานปกติ) ชื่อมาจากปัญหาคลาสสิก "Byzantine Generals" (ปัญหาทหารไบแซนไทน์ — โจทย์ CS คลาสสิกปี 1982 ของ Lamport/Shostak/Pease ดู บทที่ 0 distributed-systems สำหรับคำอธิบายเต็ม)
Service return ค่า "ผิด" / "corrupted"
❌ Rare ใน well-designed system
❌ Hard to handle — ต้อง checksum, multi-source verification2.4 Network Partition
Server ติดต่อกันไม่ได้
"Split brain" — แต่ละฝั่งคิดว่าอีกฝั่งตาย
CAP theorem in action — เลือก C หรือ A3. Timeout — Always Set Timeout!
กฎข้อแรกของ reliability — ทุก network call ต้องมี timeout เสมอ เพราะถ้าไม่ตั้ง dependency ที่ช้าเพียงตัวเดียวจะทำให้ thread ค้างรอจนหมด แล้วทั้งแอปล่ม (cascade) ส่วนนี้บอกค่า timeout ที่เหมาะกับงานแต่ละแบบและวิธี implement ในหลายภาษา:
3.1 ปัญหา — Default Infinite Timeout
❌ ไม่ set timeout:
- 1 slow dependency → cascade failure
- ทุก thread stuck waiting
- App ตายในที่สุด (resource exhausted)3.2 ทางแก้ — Explicit Timeout
Recommendation (rough):
- DB query: 1-5 seconds
- HTTP call to internal service: 3-10 seconds
- HTTP call to external service: 5-15 seconds (default ของ Stripe/AWS SDK ยุคใหม่ — ใช้คู่กับ retry+backoff); 10-30 seconds เฉพาะ batch/slow API ที่รู้ว่าช้าจริง
- User-facing endpoint: < 30 seconds total3.3 Implementation
typescript
// Axios with timeout
const response = await axios.get('/api/data', { timeout: 5000 });
// Fetch with AbortController
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const res = await fetch('/api/data', { signal: controller.signal });
clearTimeout(timeoutId);
} catch (err) {
if (err.name === 'AbortError') {
// timeout
}
}java
// Spring WebClient
WebClient client = WebClient.builder()
.baseUrl("https://api.example.com")
.build();
Mono<Data> result = client.get()
.uri("/data")
.retrieve()
.bodyToMono(Data.class)
.timeout(Duration.ofSeconds(5));กฎทอง #1: NEVER call external service without explicit timeout
4. Retry with Exponential Backoff
ความล้มเหลวชั่วคราว (timeout, 503) มักหายเองถ้าลองใหม่ แต่ retry แบบ naive (ทุก client ยิงพร้อมกันทันที) จะถล่ม service ที่กำลังจะฟื้น — exponential backoff + jitter แก้ด้วยการเว้นนานขึ้นเรื่อย ๆ บวกสุ่มเล็กน้อยเพื่อกระจายเวลา retry ของ client ออกจากกัน:
4.1 ปัญหา — Naive Retry
Service ตาย → retry ทันที → ทุก client retry พร้อมกัน
→ "Thundering herd" → overload service มากกว่าเดิม4.2 Solution — Exponential Backoff + Jitter
Pattern:
- Wait ระหว่าง retries
- Increase wait แบบ exponential
- Add jitter (random delay) — กระจาย client
- Limit max attempts4.3 Implementation
typescript
interface RetryConfig {
maxAttempts?: number;
baseDelay?: number;
maxDelay?: number;
jitter?: boolean;
}
async function retry<T>(
fn: () => Promise<T>,
config: RetryConfig = {}
): Promise<T> {
const {
maxAttempts = 3,
baseDelay = 100,
maxDelay = 30000,
jitter = true
} = config;
let lastErr: any;
for (let i = 0; i < maxAttempts; i++) {
try {
return await fn(); // ลองเรียก fn() — ถ้าสำเร็จ return ทันที
} catch (err) {
lastErr = err;
// ถ้าเป็น client error (4xx) — ไม่ retry เพราะ retry ไปก็ fail เหมือนเดิม
if (isClientError(err)) throw err;
// Exponential backoff:
// Math.pow(2, i) = 2^i (i=0→1, i=1→2, i=2→4, ...) ทำให้รอนานขึ้นเรื่อย ๆ
// Math.min(..., maxDelay) = clamp ไม่ให้รอเกิน maxDelay (เช่น 30s)
let delay = Math.min(baseDelay * Math.pow(2, i), maxDelay);
// เพิ่ม jitter (สุ่มเวลาเล็กน้อย 0-50%) เพื่อกระจาย retry ของ client ออกจากกัน
// หมายเหตุ: นี่คือ "equal jitter" (delay × 0-50%) — AWS Architecture Blog
// (Marc Brooker 2015) เสนอ "full jitter" คือสุ่ม [0, delay] เต็มช่วง
// พบว่าให้ผลดีกว่าในการกระจาย load จริง — ดูอ้างอิงท้ายส่วน
if (jitter) {
delay += Math.random() * delay * 0.5;
}
await sleep(delay); // sleep() = helper รอ delay ms ก่อน loop รอบถัดไป
}
}
throw lastErr;
}📌 Jitter types: "equal jitter" (โค้ดข้างบน) vs "full jitter" (
delay = Math.random() * cap) — สำหรับ production แนะนำ full jitter ตามที่ AWS แนะนำ (Exponential Backoff And Jitter — AWS Architecture Blog, 2015)
4.4 เมื่อไหร่ NOT to Retry
4xx Client error (400, 401, 403, 404):
- Won't succeed on retry (it's YOUR fault)
- Don't retry
5xx Server error:
- May recover, retry OK
429 Too Many Requests:
- Server says "back off"
- Retry with longer delay (use Retry-After header)
408 Request Timeout, 503 Service Unavailable:
- Retry-friendly
- Server temporarily busy4.5 Idempotency Required
Retry safe only ถ้า operation idempotent
GET: safe ✅
PUT: safe ✅ (set X to Y)
DELETE: safe ✅ (delete X)
POST: UNSAFE ❌ (creates new each time)
For POST: ใช้ idempotency key (ดู section ถัดไป)5. Idempotency — Safe to Retry
5.1 นิยาม
Idempotent = doing same thing N times = doing 1 time
Examples:
- "Set balance to $100" ✅ idempotent
- "Add $100 to balance" ❌ not (4 retries = $400 added!)
- "Delete user 42" ✅ idempotent
- "Append to log" ❌ not5.2 Idempotency Key Pattern (Stripe)
Client generates UUID per request:
POST /charges
Idempotency-Key: 7e8f9-abc-def-123
{ amount: 100 }
Server:
1. Check if key exists in cache/DB
2. If exists → return cached response (don't process)
3. If new → process + cache result (TTL 24h)5.3 Implementation
java
@PostMapping("/charges")
public Charge create(
@RequestHeader("Idempotency-Key") String key,
@RequestBody ChargeRequest req
) {
// Check cache
Optional<Charge> cached = idempotencyService.find(key);
if (cached.isPresent()) {
return cached.get();
}
// Process
Charge charge = paymentService.charge(req);
// Cache (TTL 24h)
idempotencyService.store(key, charge, Duration.ofHours(24));
return charge;
}5.4 Use Cases ที่ใช้กันจริง
- Stripe payment API
- GitHub API
- Square API
- AWS APIs (using ClientRequestToken)
- ทุก payment providerรายชื่อข้างบนเป็น payment / critical API ที่ "ห้าม double charge เด็ดขาด" — idempotency จึงเป็นข้อบังคับ ไม่ใช่ทางเลือก
Insight: Idempotency = ป้องกัน "double charge" ที่ payment provider กลัวที่สุด
6. Circuit Breaker — กัน Cascade Failure
เมื่อ service ปลายทางช้าหรือล่ม การพยายามเรียกต่อไปทำให้ thread/resource ของฝั่งเราหมด แล้วล่มตามเป็นลูกโซ่ — circuit breaker ทำงานเหมือนเบรกเกอร์ไฟ: เมื่อ fail บ่อยก็ "ตัดวงจร" หยุดเรียกชั่วคราวคืน error ทันที พอครบเวลาค่อยลองทีละครั้ง (half-open) ก่อนกลับสู่ปกติ:
6.1 ปัญหา
Service A calls Service B
B ช้า/ตาย
→ A's threads ติด wait
→ A's resources exhausted
→ A ตาย → C ตาย → cascade6.2 Solution — Circuit Breaker
Track failure rate ของ B
→ ถ้า failure rate สูง → "OPEN" — หยุดเรียก B
→ Return error ทันที (no actual call)
→ หลัง timeout → "HALF-OPEN" → ลอง 1 call
→ Success → "CLOSED" (resume normal)6.3 States
6.4 Implementation
typescript
class CircuitBreaker {
private state: 'closed' | 'open' | 'half-open' = 'closed';
private failures = 0;
private nextAttempt = 0;
constructor(
private threshold = 5,
private timeoutMs = 30_000
) {}
async call<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === 'open') {
if (Date.now() < this.nextAttempt) {
throw new Error('Circuit open');
}
this.state = 'half-open';
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (err) {
this.onFailure();
throw err;
}
}
private onSuccess() {
this.failures = 0;
this.state = 'closed';
}
private onFailure() {
this.failures++;
if (this.failures >= this.threshold) {
this.state = 'open';
this.nextAttempt = Date.now() + this.timeoutMs;
}
}
}6.5 Libraries
| Language | Library |
|---|---|
| Java | Resilience4j ⭐, Hystrix (deprecated) |
| .NET | Polly |
| Node.js | opossum |
| Python | pybreaker |
| Go | gobreaker |
6.6 Spring Boot — Resilience4j config
yaml
resilience4j:
circuitbreaker:
instances:
paymentService:
slidingWindowSize: 10
failureRateThreshold: 50 # > 50% → open
waitDurationInOpenState: 30s # wait 30s before half-open
permittedNumberOfCallsInHalfOpenState: 3
minimumNumberOfCalls: 10java
@CircuitBreaker(name = "paymentService", fallbackMethod = "fallback")
public Payment charge(ChargeRequest req) {
return stripeClient.charge(req);
}
public Payment fallback(ChargeRequest req, Exception ex) {
// Queue for retry later
queue.enqueue(req);
return Payment.pending();
}7. Bulkhead — Isolate Resources
คำอ่าน: bulkhead = "บัลค์-เฮด" (ผนังกั้นในเรือ) · hedged request = "เฮดจ์ด-รีเควสต์" (ยิงซ้ำเผื่อ — ดูส่วนถัดไป)
ชื่อ bulkhead มาจากผนังกั้นห้องในเรือเดินสมุทร — ถ้ารูรั่วถูกจำกัดอยู่ห้องเดียว เรือก็ไม่จม หลักเดียวกันใช้กับซอฟต์แวร์: แยก resource pool ต่อ dependency เพื่อให้ปัญหาของ service หนึ่งไม่ลามไปกินทรัพยากรทั้งหมดจนระบบล่มทั้งก้อน:
7.1 Ship Metaphor
ใน เรือเดินสมุทร:
- 1 รูในตัวเรือ
- ไม่มี bulkhead → ทั้งเรือจม
- มี bulkhead (ผนังกั้น) → น้ำติดในห้องเดียว เรือยังลอย7.2 Code Analogy
App มี 1 thread pool (100 threads)
1 slow downstream กิน 100 threads
→ ทุก request fail
Solution: separate thread pool per dependency
- 30 threads for Service A
- 30 threads for Service B
- 40 threads for Service C
A ตาย → only 30 threads ติด
B + C ยังทำงาน7.3 Implementation
typescript
class Bulkhead {
private queue: Array<() => void> = [];
private active = 0;
constructor(private maxConcurrent = 10) {}
async run<T>(fn: () => Promise<T>): Promise<T> {
await this.acquire();
try {
return await fn();
} finally {
this.release();
}
}
private acquire(): Promise<void> {
return new Promise(resolve => {
if (this.active < this.maxConcurrent) {
this.active++;
resolve();
} else {
this.queue.push(() => {
this.active++;
resolve();
});
}
});
}
private release() {
this.active--;
const next = this.queue.shift();
if (next) next();
}
}
// Use
const paymentBulkhead = new Bulkhead(30);
await paymentBulkhead.run(() => paymentService.charge());8. Combined Pattern — Timeout + Retry + Circuit Breaker + Bulkhead
ในจริง — รวมทุก pattern เข้าด้วยกัน:
typescript
async function safeCall<T>(
fn: () => Promise<T>,
config: {
timeoutMs: number;
retries: number;
circuit: CircuitBreaker;
bulkhead: Bulkhead;
}
): Promise<T> {
return config.bulkhead.run(async () => {
return config.circuit.call(async () => {
return retry(
() => withTimeout(fn(), config.timeoutMs),
{ maxAttempts: config.retries }
);
});
});
}→ Defense in depth — หลายชั้นกัน
9. Failover — Switch to Backup
failover คือการสลับไปใช้ตัวสำรองเมื่อตัวหลักล่ม — มีสองรูปแบบ: active-passive (สำรองรอ standby แล้ว promote เมื่อล่ม) กับ active-active (ทุกตัวรับ traffic อยู่แล้ว ตัวหนึ่งล่มก็ไม่ต้องสลับ) ส่วนนี้เทียบทั้งสองและยกตัวอย่าง DB failover ด้วย Patroni:
9.1 Active-Passive
Primary serves traffic (100%)
Backup standby, syncing data
On failure: promote backup → manual หรือ auto9.2 Active-Active
Both serve traffic (load balanced)
On 1 failure: other handles all traffic
No "failover" — already serving9.3 Database Failover Example — Patroni
PostgreSQL with Patroni:
[Primary] ←──streaming replication──→ [Replica 1] ──→ [Replica 2]
Patroni monitors via DCS (Distributed Config Store เช่น etcd/Consul/ZooKeeper):
- Primary down → promote replica ที่ LSN สูงสุด
(LSN = Log Sequence Number = ตำแหน่งใน WAL ที่บอกว่า replica apply ไปถึงไหนแล้ว)
- Update connection string ผ่าน DCS / HAProxy / DNS
- Failover time ขึ้นกับ ttl, loop_wait, retry_timeout (default รวมแล้วประมาณหลัก 10s)⚠️ Senior caveats ที่ตัวอย่างย่อข้างบนตัดทิ้ง:
- Patroni default ใช้ async streaming replication ไม่ใช่ sync — ถ้าอยาก sync จริง ๆ ต้องตั้ง
synchronous_standby_namesใน PostgreSQL และยอมแลกกับ write latency ที่เพิ่มขึ้น (write ต้องรอ replica acknowledge)- Split-brain protection ต้องอาศัย DCS + watchdog (เช่น softdog) + fencing — ถ้า primary ยังมี client เขียนอยู่แต่ DCS คิดว่า dead จะเกิด split-brain ได้
- Auto failback ไม่ทำให้ทันที — primary เก่าที่กลับมาต้อง re-init จาก new primary (pg_rewind หรือ basebackup) ขั้นตอนนี้มัก manual
- เวลา 10-30 วินาที เป็น "best case" ของ default config — ขึ้นกับ DCS health, network และ application-level connection pool ด้วย
(อ่านต่อ: Patroni docs — Configuration, DCS, Watchdog sections)
9.4 Multi-Region Failover
Region A (primary) ←─async──→ Region B (standby)
100% traffic 0% traffic
Region A down:
- DNS update → Region B
- Re-route traffic
- Promote B as primary
RPO: data since last replication (usually 1s lag)
RTO: time to failover (5-15 min typical)10. Health Check — รู้ว่าใครพร้อม
10.1 3 Types of Health Check
1. Liveness — "Am I running?"
- Process alive
- Basic — just respond 200
- If fail → restart container
2. Readiness — "Can I serve traffic?"
- DB connected
- Cache reachable
- Dependencies OK
- If fail → LB ลบจาก rotation (ไม่ restart)
3. Startup — "Done starting up?"
- สำหรับ slow-start apps (load model, warm cache)
- K8s won't kill ระหว่าง startup10.2 Spring Boot — Actuator
http
GET /actuator/health
{
"status": "UP",
"components": {
"db": { "status": "UP" },
"redis": { "status": "UP" },
"diskSpace": { "status": "UP", "details": { "free": "100GB" } }
}
}10.3 Kubernetes Probe Config
yaml
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
failureThreshold: 1กฎ: liveness ตรวจแค่ "process alive" — readiness ตรวจ "ready to serve"
ผิดบ่อย: ใช้ liveness check DB → DB ช้า → K8s restart pod เปล่า ๆ
11. Graceful Shutdown — Clean Exit
ทุกครั้งที่ deploy หรือ scale down แอปจะได้รับสัญญาณ SIGTERM — ถ้าตายทันทีจะทำ request ที่ค้างอยู่หาย graceful shutdown แก้ด้วยการหยุดรับ request ใหม่ → รอ request ที่ค้างเสร็จ → ปิด connection → ค่อย exit ทำให้ deploy โดยไม่มี request ตกหล่น:
11.1 Pattern
App receives SIGTERM (deploy, K8s scaling):
1. Stop accepting new requests
2. Drain in-flight requests (wait up to N seconds)
3. Close connections (DB, cache, queue)
4. Exit cleanly
→ No request lost
→ K8s default grace period: 30 seconds11.2 Implementation
typescript
// Node.js
import express from 'express';
const app = express();
const server = app.listen(3000);
process.on('SIGTERM', async () => {
console.log('SIGTERM received, shutting down gracefully');
// 1. Stop new connections
server.close(() => {
console.log('HTTP server closed');
});
// 2. Close dependencies
await db.close();
await redis.quit();
await queue.disconnect();
// 3. Exit
process.exit(0);
});yaml
# Spring Boot — application.yml
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30syaml
# K8s — pod spec
spec:
terminationGracePeriodSeconds: 60 # ให้เวลา drain12. Backpressure — Slow Down Producer
ถ้า producer ผลิตเร็วกว่า consumer ประมวลผล queue จะโตไม่หยุดจน OOM — backpressure คือกลไกส่งสัญญาณ "ช้าลงหน่อย" กลับไปหา producer ทำได้หลายวิธี: bounded queue (บล็อกเมื่อเต็ม), drop strategy หรือ throttle ส่วนนี้อธิบายแต่ละแบบพร้อมตัวอย่าง:
12.1 ปัญหา
Producer faster than consumer
→ Queue grows unbounded
→ Eventually: OOM, dropped messages, system crash12.2 Backpressure = Signal "Slow Down" to Producer
12.3 Methods
1. Bounded queue
- Block producer when queue full
- Simple, effective
2. Drop strategy
- Drop oldest (LIFO)
- Drop newest (FIFO)
- Drop random
- Sample (every Nth — for metrics)
3. Throttle producer
- Token bucket
- Adaptive rate (TCP-like)12.4 Bounded Queue Implementation
⚠️ ตัวอย่าง backpressure แบบ busy-wait ที่เคยมีตรงนี้รอ rewrite — โค้ดเดิมเป็น
while (queue.length >= maxSize) await sleep(10)ซึ่ง ไม่ใช่ backpressure จริง: มันเป็นแค่ poll-loop ที่กิน CPU/event loop และไม่ส่งสัญญาณกลับไปให้ producer ช้าลง อาจทำให้ระบบ production พังในรูปแบบที่หาไม่เจอBackpressure จริง ๆ ทำงานยังไง (เชิงคุณภาพ):
- Reactive Streams
request(n)— consumer บอก upstream ว่ารับได้กี่ item ถ้าไม่ขอ upstream ก็ไม่ส่ง (pull-based)- Promise-based wait queue —
push()คืน Promise ที่ resolve ตอนpop()ทำให้มีที่ว่าง (ไม่ poll)- Semaphore / counting lock — ใช้ primitive ของ concurrency lib (เช่น
async-semaใน Node,Semaphoreใน Java)ใน production ให้ใช้ library ที่ทำ backpressure ถูกต้องอยู่แล้ว:
- Node/TS: RxJS (
onBackpressureBuffer,onBackpressureDrop), หรือ Nodestreamที่มีhighWaterMark- Java: Project Reactor / RxJava (Flux/Flowable มี
onBackpressureBuffer,onBackpressureLatest)- JVM general: Akka Streams
- Kafka consumer: ใช้
max.poll.records+ manual commit เพื่อ control rateอย่า roll your own queue ใน production — backpressure ที่ถูกต้องต้องคิดทั้งฝั่ง producer/consumer และ error path ครบ
13. SLI / SLO / SLA — Quantify Reliability
"ระบบเชื่อถือได้แค่ไหน" ต้องวัดเป็นตัวเลข ไม่ใช่ความรู้สึก — SLI คือสิ่งที่วัด (เช่น % availability), SLO คือเป้าภายใน (เช่น 99.9%), SLA คือสัญญากับลูกค้า (มีบทลงโทษถ้าไม่ถึง) ส่วนนี้แยกสามคำที่สับสนกันบ่อยและให้ตัวเลข SLO ที่เหมาะกับแต่ละประเภทแอป:
13.1 Definitions
SLI (Service Level Indicator):
- "What we measure"
- e.g., availability = success / total × 100
SLO (Service Level Objective):
- "Internal target"
- e.g., 99.9% availability over 30 days
SLA (Service Level Agreement):
- "Customer contract" (legal)
- e.g., 99.5% — refund if not met13.2 Rule of Thumb
SLA < SLO (buffer)
- SLO = "team's target"
- SLA = "what we promise customer" (less aggressive)13.3 Common SLO Targets
| App Type | SLO | Downtime/month |
|---|---|---|
| Public web app | 99.9% | 43 min |
| Internal API | 99.5% | 3.6 hours |
| Background job | 95% | 1.5 days |
| Banking core | 99.99% | 4 min |
| Status page (ironic) | 99% | 7 hours |
14. Error Budget — Reliability ≠ "Always Up"
Error budget = 100% - SLO
SLO 99.9% → budget 0.1% = 43 min/month downtime "allowed"
Use budget for:
- Deploy (controlled risk)
- Maintenance
- Real incidents
If budget exhausted (downtime > 43 min):
- Freeze feature deploys
- Focus on reliability investment
- Postmortem + remediationInsight: 100% reliability ไม่มีในโลก — มี budget = engineer ตัดสินใจ risk ได้
ดู Observability บทที่ 6 สำหรับลึก
15. Chaos Engineering — Find Weakness Intentionally
แทนที่จะรอให้ระบบพังเองตอน production แล้วค่อยรู้จุดอ่อน chaos engineering จงใจ "ฉีดความล้มเหลว" ในสภาพควบคุม (kill server, ตัด network) เพื่อพิสูจน์ว่าระบบทนได้จริงตามที่ออกแบบ — Netflix บุกเบิกด้วย Chaos Monkey ส่วนนี้สอนหลักการและวิธีทำอย่างปลอดภัย:
15.1 หลักการ
Netflix Chaos Monkey:
- Randomly kill production servers
- Force teams to design for failure
- "หา weakness ก่อนมัน หามาเอง"15.2 Game Day
Planned exercise:
- Inject failure (kill DB, partition network)
- Test response process
- Find unknown unknowns
- Practice runbook execution
Frequency: quarterly ขึ้นไป15.3 Tools
| Tool | จุดเด่น |
|---|---|
| Chaos Monkey (Netflix) | Kill EC2 instances |
| Chaos Mesh | Kubernetes-native |
| LitmusChaos | K8s, CNCF project |
| Gremlin | Commercial, broad scenarios |
| AWS Fault Injection Simulator | AWS-native |
| Pumba | Docker chaos |
15.4 Scenarios
- Kill random pod / instance
- Drop network packets (10-50%)
- Increase CPU/memory pressure
- Simulate disk full
- Delay responses (add 500ms latency)
- Region failover drill
- DNS resolution fail
- Certificate expiration16. Graceful Degradation — Fail Partially, Not Totally
แทนที่ระบบจะ "ล่มทั้งหมด" เมื่อบางส่วนพัง graceful degradation ทำให้มัน "ใช้งานได้บางส่วน" — เช่น search ล่มก็ปิดปุ่ม search แต่ feed ยังดูได้, ML รวนก็ใช้ feed เรียงตามเวลาแทน เป้าหมายคือผู้ใช้รู้สึกว่า "ช้า/ฟีเจอร์น้อยลง" ไม่ใช่ "ใช้ไม่ได้เลย":
16.1 หลักการ
เมื่อบางส่วนของระบบ fail — user ยังใช้งานอะไรได้?16.2 ตัวอย่าง — Twitter Timeline
Normal:
- Database → tweets
- Search → results
- ML → recommendations
- CDN → images
Degraded scenarios:
- Database slow → show cached old tweets (5 min stale)
- Search down → disable search button + message
- Image service down → show text-only post
- ML recommendation down → show chronological feed
→ User experience degrades, ไม่ใช่ catastrophic failure16.3 Patterns
Feature flags:
- Disable heavy feature เมื่อ load สูง
- "Black Friday mode" — disable wishlist, ratings
Fallback:
- ML rec fail → use rule-based recommendation
- Cache fail → query DB (slower but works)
- External API fail → use stale data + warn
Progressive enhancement:
- Page works without JS (basic features)
- JS adds interactivity (enhanced)
- App usable even when API slow17. Disaster Recovery (DR)
disaster recovery คือแผนรับมือเมื่อเกิดหายนะใหญ่ (region ล่ม, ข้อมูลเสียหาย) วัดด้วยสองตัวเลข: RPO (ยอมเสียข้อมูลได้กี่ชั่วโมง) และ RTO (ยอม down ได้กี่ชั่วโมง) — ยิ่ง RPO/RTO ต่ำยิ่งแพง ส่วนนี้เทียบ strategy 4 ระดับ (backup, pilot light, warm standby, active-active) และย้ำว่าต้อง "ทดสอบ restore" จริง:
17.1 Recovery Objectives — RPO + RTO
RPO (Recovery Point Objective):
- Max acceptable data loss
- "เรา accept loss 1 hour of data" → RPO = 1h
RTO (Recovery Time Objective):
- Max acceptable downtime
- "เรา accept down 4 hours" → RTO = 4h17.2 Strategy Tiers — Cost vs Recovery
| Strategy | RPO | RTO | Cost |
|---|---|---|---|
| Backup + Restore | 24h | 1-4h | Lowest |
| Pilot Light | 1h | 30 min | Low-medium |
| Warm Standby | minutes | 5-15 min | Medium |
| Active-Active | 0-seconds | 0 (auto) | Highest |
17.3 Tier Details
Backup + Restore:
- Daily backup to S3
- Restore on disaster
- Cheap แต่ช้า
Pilot Light:
- Critical infra always running in DR region
- Data replicating
- Scale up other components on DR event
Warm Standby:
- Full env in DR region (smaller scale)
- Data sync
- Promote on DR
Active-Active (Multi-Region):
- Both regions serve traffic
- No "failover" — already serving
- RPO: 0 (sync) หรือ seconds (async)
- RTO: 0 (auto-failover)17.4 DR Practice
✅ Regular DR drill (quarterly)
✅ Test restoration (ไม่ใช่แค่ backup!)
✅ Document runbook
✅ Multiple offsite backups
✅ Encrypt backups
✅ Off-cloud copy (ransomware protection)
✅ Test failover automationGitLab incident 2017: มี backup 5 ระบบ — ไม่มี backup ไหน restore ได้! → lesson: test restoration
18. Multi-Region Considerations
การกระจายระบบหลาย region ให้ทั้ง latency ต่ำสำหรับผู้ใช้ทั่วโลก, DR, compliance และ availability สูง — แต่แลกมากับความซับซ้อน (data sync, consistency ข้าม region, ต้นทุน) ส่วนนี้ชั่งน้ำหนักว่าเมื่อไหร่คุ้มที่จะทำและ challenge ที่ต้องเตรียมรับมือ:
18.1 ทำไม Multi-Region
✅ Lower latency for global users
✅ Disaster recovery
✅ Compliance (data residency — EU data in EU)
✅ Higher availability (99.99% เป็นไปได้)18.2 Challenges
❌ Data sync (CAP theorem)
❌ Higher cost (2-3x infrastructure)
❌ Complex operations
❌ Network cost (egress ระหว่าง region)
❌ Latency ระหว่าง region (US-EU ~100ms)18.3 Data Strategy
1. Single primary (other regions read-only)
- All writes → 1 region
- Reads served local
- Simple, but writes ช้าสำหรับ user ไกล
2. Multi-master (active-active)
- Writes anywhere
- Sync ระหว่าง region (async)
- Conflict resolution needed
- Complex แต่ low latency
3. Sharded by region
- User from Asia → Asia DB
- User from EU → EU DB
- No cross-region for user data
- GDPR friendly19. Monitoring + Alerting
19.1 ต้อง monitor อะไรบ้าง
✅ Health metric — uptime, error rate, latency
✅ Resource — CPU, memory, disk, network
✅ Business — orders/sec, signup/min, revenue
✅ Custom — queue depth, cache hit rate19.2 Alert เมื่อไหร่
- Error rate > threshold
- Latency p99 > SLO target
- Resource > 80%
- Queue growing unbounded
- Anomaly (sudden change)19.3 Alert ที่ดี
✅ Actionable — engineer รู้ทำอะไร
✅ Urgent — ต้องตอบเลย (ไม่ใช่ "informational")
✅ Specific — รู้ว่า component ไหนพัง
✅ มี runbook link
❌ Alert spam — ignore ในที่สุด
❌ False positive บ่อย — alert fatigueดู Observability book สำหรับลึก
19.4 Observability ลึก — OpenTelemetry + 3 pillars
text
Observability ≠ Monitoring
- Monitoring: "ระบบยังทำงานไหม" (รู้สิ่งที่ตั้งเป็น metric ไว้ล่วงหน้า)
- Observability: "ทำไมระบบทำงานแบบนี้" (ตอบ unknown unknowns ได้)
3 Pillars (จำง่าย: LMT):
1. Logs — event records (structured > unstructured)
2. Metrics — time-series numbers (low cardinality)
3. Traces — request flow ข้าม services
OpenTelemetry (CNCF, Graduated 2024):
- Vendor-neutral observability framework
- Replaces OpenTracing + OpenCensus
- Native support: AWS X-Ray, Datadog, Honeycomb, Jaeger, Tempo, Grafana
- Auto-instrumentation libraries: Java, Python, Go, Node.js, Ruby, .NET
Structured Logging (best practice):
- JSON format → queryable in Elasticsearch / Loki / DataDog
- Include: timestamp, level, service, trace_id, span_id, message, context
- Never: PII in logs (PCI/GDPR compliance)
Trace Sampling:
- Head sampling — decide at request start (cheap, biased)
- Tail sampling — buffer all, decide at end (expensive, better)
- Production: 1% baseline + 100% errors + 100% slow
Cardinality Explosion (Prometheus pitfall):
- ห้าม use high-cardinality labels (user_id, session_id) ใน metrics
- 1M users × 10 metrics = 10M time series → Prometheus OOM
- Use logs/traces for high-cardinality data
Prometheus histograms — update 2026:
- ปัญหา histogram แบบเดิม (classic): ต้อง pre-define bucket boundaries ซึ่งเลือกผิดทำให้ p99 ไม่แม่น และ label cardinality สูง
- ตั้งแต่ Prometheus 2.40 (พ.ย. 2022) มี "native histograms" (sparse, exponential buckets) ที่ปรับเองตามข้อมูล แม่นกว่าและประหยัด memory — GA ใน Prometheus 3.x
- ของใหม่: ใช้ native histograms กับ workload ที่ละเอียดต่อ latency (เช่น p99.9) แทน classic ที่จำกัด bucket📚 เรียนต่อ: Honeycomb's "Observability Engineering" (Charity Majors, free chapters), OpenTelemetry docs
20. Reliability Patterns Summary
เรียน pattern เรื่อง reliability มาหลายตัว ตารางนี้สรุปทั้งหมดในที่เดียว — แต่ละ pattern แก้ปัญหาคนละมุม (timeout กันรอค้าง, circuit breaker กัน cascade, bulkhead แยกความเสียหาย ฯลฯ) ในระบบจริงมักใช้หลาย pattern ซ้อนกัน ใช้เป็น checklist ตอนออกแบบ:
| Pattern | Purpose |
|---|---|
| Timeout | Prevent indefinite waiting |
| Retry + Backoff | Recover from transient failure |
| Idempotency | Safe to retry |
| Circuit Breaker | Stop calling failing service |
| Bulkhead | Isolate failure to subset |
| Fallback | Use cached/default when fail |
| Throttle / Rate Limit | Protect from overload |
| Health Check | Detect + remove unhealthy |
| Graceful Shutdown | Clean exit on deploy |
| Backpressure | Slow producer when consumer behind |
| Replication | Survive single-node failure |
| Failover | Switch to backup |
| DR | Multi-region disaster recovery |
| Chaos Engineering | Find weakness intentionally |
21. Real Example — Resilient Payment Flow
มาดูว่า pattern ทั้งหมดประกอบกันในโค้ดจริงยังไง — payment flow ที่ทนทานซ้อนหลาย pattern เข้าด้วยกัน: idempotency (กัน double charge), bulkhead, circuit breaker, retry และ timeout ตัวอย่างนี้แสดงว่าระบบที่ critical จริงต้องใช้ defense หลายชั้น:
⚠️ โค้ดด้านล่างเป็น pseudocode สไตล์ TypeScript เพื่อสื่อ pattern เท่านั้น ไม่ใช่โค้ดที่ copy ไปรันได้จริง — โค้ดเดิมเคยใส่
@Serviceซึ่งเป็น Java/Spring annotation (ไม่ใช่ TS) ปนเข้ามา ถ้าจะใช้ใน NestJS ให้เปลี่ยนเป็น@Injectable()จาก@nestjs/common; ถ้าจะใช้ Spring Boot จริง ดูตัวอย่าง Resilience4j ที่ section ก่อนหน้า (Circuit Breaker / Bulkhead)
typescript
// NestJS style: ใช้ @Injectable() จาก @nestjs/common
// (Spring/Java style: @Service จาก org.springframework.stereotype)
@Injectable()
export class PaymentService {
private paymentCircuit = new CircuitBreaker({
threshold: 5,
timeoutMs: 30_000
});
private bulkhead = new Bulkhead(20); // max 20 concurrent calls
async charge(req: ChargeRequest): Promise<Payment> {
// 1. Idempotency check
const cached = await this.idempotencyStore.find(req.idempotencyKey);
if (cached) return cached;
// 2. Process with reliability patterns
const payment = await this.bulkhead.run(() =>
this.paymentCircuit.call(() =>
retry(
() => this.callStripeWithTimeout(req),
{ maxAttempts: 3, baseDelay: 200 }
)
)
);
// 3. Cache result (24h)
await this.idempotencyStore.store(req.idempotencyKey, payment, '24h');
// 4. Audit log
await this.audit.record(payment);
return payment;
}
private async callStripeWithTimeout(req: ChargeRequest): Promise<Payment> {
return withTimeout(
this.stripeClient.create(req),
10_000 // 10 sec timeout
);
}
}Stack of patterns:
- Idempotency key (prevent double charge)
- Bulkhead (isolate Stripe calls)
- Circuit breaker (stop on cascading fail)
- Retry with backoff (handle transient)
- Timeout (prevent infinite wait)
- Audit log (compliance + forensic)
22. Capacity Planning for Failure — N+M Redundancy
ระบบที่ทนการล่มต้องมี capacity เผื่อไว้ — N+M redundancy หมายถึง "ต้องการ N เครื่อง แต่เตรียม N+M" เพื่อให้ M เครื่องล่มได้โดยยังให้บริการได้ ส่วนนี้สอนวิธีคำนวณ capacity ที่รวม buffer สำหรับ failure (ไม่ใช่แค่ peak load):
N+1 redundancy:
- Need 1 → have 2
- 1 fails → still works
N+2:
- Need 3 → have 5
- 2 can fail → still works
Calculate:
- Peak load → required capacity
- Add redundancy buffer
- Plan for region failure (spare in another region)
Example:
- App needs 10 servers at peak
- Plan: 15 (N+5)
- 5 can fail → still servingCost vs reliability trade-off — เลือกตาม business value
23. Cost of Outage — Why Reliability Matters $$$
ตัวเลขจริงในอุตสาหกรรม:
1 minute downtime cost:
- Amazon retail: ~$220K/min ($1B/year หาย)
- Stripe: ~$10K/min
- Average B2B SaaS: $5-50K/min
- Trading firms: $millions/min
Calculate:
- Revenue per minute
- × downtime minutes
- + customer churn (10% leave permanently)
- + reputation damage
- + legal (if SLA breached)
- = full costInsight: ใช้ตัวเลขนี้ justify reliability investment ให้ business
24. Reliability Anti-Patterns
รวมความผิดพลาดเรื่อง reliability ที่เจอบ่อยและทำให้ระบบเปราะ — ไม่ตั้ง timeout, retry ไม่จำกัด, sync ทุกอย่าง, shared fate (ทุกอย่างล่มพร้อมกัน) แต่ละข้อมาคู่กับวิธีแก้ ดูไว้เพื่อหลีกเลี่ยงตั้งแต่ออกแบบ:
24.1 ❌ No Timeout
Service A รอ B forever → cascade failureFix: Always set timeout
24.2 ❌ Unbounded Retry
Fail → retry forever → amplify load on broken serviceFix: Max attempts + backoff + circuit breaker
24.3 ❌ Synchronous Everything
User รอ 10 service calls ติดต่อกัน
1 ช้า → ทั้งหมดช้าFix: Async where possible, parallel where applicable
24.4 ❌ Shared Fate
Many features on same server
1 feature crash → kill server → all features downFix: Bulkhead / separate services
24.5 ❌ No Graceful Degradation
1 service down → app unusableFix: Fallback / progressive enhancement
24.6 ❌ No Monitoring
"Find out from customer ticket"Fix: Health check + alerting + on-call rotation
24.7 ❌ Manual DR
Disaster → engineer runs runbook manuallyFix: Automate (PagerDuty + scripts)
24.8 ❌ No Game Day
Never test failure → unknown unknownsFix: Quarterly chaos exercise
24.9 ❌ Single Region
1 region down → 100% outFix: Multi-region (if critical)
24.10 ❌ No Idempotency
Retry causes duplicateFix: Idempotent operations + idempotency key
25. Real Outage Cases — Learn From Disasters
25.1 AWS S3 — Feb 2017
Cause: Typo ใน command — engineer accidentally removed servers
Result: S3 down ~4 hours in us-east-1
Impact: ทุกเว็บใหญ่ที่ใช้ S3 ดาวน์
Lesson:
- Limit blast radius (จำกัด "รัศมีความเสียหาย" — ออกแบบให้พลาด 1 จุดกระทบวงแคบ ไม่ลามทั้งระบบ เช่น ไม่ allow mass removal = สั่งลบทีเดียวเยอะ ๆ ไม่ได้)
- Test changes in staging
- Better tooling (preview before execute)25.2 Cloudflare — June 2022 BGP
Cause: BGP misconfiguration ใน Cloudflare's network
Result: Many sites down briefly
Lesson:
- Test BGP changes carefully
- Multiple control planes
- Quick rollback capability25.3 Facebook (Meta) — Oct 2021
Cause: BGP misconfiguration → entire FB infrastructure unreachable
Result: 6+ hours down (FB, IG, WhatsApp)
Impact: ~$60M revenue loss
+ access to internal tools (engineers ไม่สามารถ badge เข้า office!)
Lesson:
- Multiple control plane regions
- Out-of-band access for emergency
- BGP change validation25.4 GitLab — Jan 2017
Cause: Engineer typed "rm -rf" on wrong server (primary แทน secondary)
Result: ~6 hours of data loss for ~700 projects
Lesson:
- Backups (5 different methods — NONE worked!)
- Test restoration regularly
- Separate roles (no human access to production storage)25.5 CrowdStrike — July 2024
Cause: Bad update pushed to Windows kernel driver
Result: 8.5M+ Windows machines BSOD globally
Impact: Airlines, hospitals, banks down — estimated $5B+
Lesson:
- Staged rollout (canary first)
- Kernel-level changes need extra caution
- Customer rollback capabilityเรียนจากความล้มเหลวของคนอื่น — ราคาถูกกว่าเรียนเอง
26. Checkpoint — ฝึกทำเอง
ลงมือทำเพื่อให้ pattern เรื่อง reliability ติดมือ — แบบฝึกหัดนี้ให้คุณสร้าง resilient HTTP client (timeout + retry + circuit breaker), idempotency key, health check และทดสอบกับเซิร์ฟเวอร์ที่ไม่เสถียร ทำครบจะออกแบบระบบที่ทนต่อความล้มเหลวได้จริง:
🛠️ Checkpoint 4.1 — Resilient HTTP Client
Build wrapper around fetch:
- Timeout (5s default)
- Retry 3x with exponential backoff
- Jitter
- Circuit breaker
- Test กับ unstable server
🛠️ Checkpoint 4.2 — Idempotency Key
Implement:
- API accept
Idempotency-Keyheader - Cache result by key (24h TTL)
- Return cached on retry
- Test ด้วย duplicate request
🛠️ Checkpoint 4.3 — Health Check
Spring Boot หรือ Node.js:
- /health/live (process alive)
- /health/ready (DB + cache OK)
- K8s probe config
🛠️ Checkpoint 4.4 — Graceful Shutdown
- Setup SIGTERM handler
- Stop accepting new requests
- Drain in-flight
- Close connections
- Verify ด้วย
docker stop
🛠️ Checkpoint 4.5 — Chaos Test
- Run app in staging
- Kill random pod
- Verify auto-restart
- Verify no errors during chaos
27. สรุปบท
ทบทวนแก่นของบทนี้ — reliability คือการ "ออกแบบโดยสมมติว่าทุกอย่างจะพัง" แล้วใส่ pattern ป้องกันไว้ทุกชั้น: timeout, retry, idempotency, circuit breaker, bulkhead, graceful degradation, SLO/error budget และ DR เช็กลิสต์นี้คือสิ่งที่ทำให้ระบบอยู่รอดในโลกจริง:
✅ Plan for failure — distributed systems จะพังเสมอ
✅ Timeout every external call — never indefinite
✅ Retry with exponential backoff + jitter (idempotent only)
✅ Idempotency-Key สำหรับ safe POST retry
✅ Circuit breaker — stop calling failing service
✅ Bulkhead — isolate failure to subset
✅ Fallback / graceful degradation — degrade not crash
✅ Health check: liveness + readiness + startup
✅ Graceful shutdown — drain on SIGTERM
✅ SLI/SLO/SLA + error budget — quantify reliability
✅ Chaos engineering — find weakness intentionally
✅ Multi-region DR — RPO/RTO based on cost-benefit
✅ Cost of outage = $$$/min — justify reliability investment
✅ Learn from disasters — AWS, Facebook, GitLab, CrowdStrike
28. คำศัพท์เพิ่ม
| คำศัพท์ | ความหมาย |
|---|---|
| Reliability | ระบบทำงานถูกต้อง / ใช้ได้ |
| Availability | % time ระบบใช้ได้ |
| Fault tolerance | ทนต่อ failure ได้ |
| Resilience | recover จาก failure |
| Failover | switch ไป backup |
| High Availability (HA) | 99.9%+ availability |
| SPOF | Single Point of Failure |
| Cascade failure | 1 fail → ทำให้อื่น fail |
| Thundering herd | ทุก client retry พร้อมกัน |
| Timeout | จำกัดเวลา wait |
| Retry | ลองใหม่ |
| Exponential backoff | wait เพิ่มขึ้นแบบ exponential |
| Jitter | random delay กระจาย |
| Idempotency | ทำซ้ำได้ผลเดิม |
| Circuit breaker | stop calling fail service |
| Bulkhead | isolate resources |
| Fallback | use default when fail |
| Backpressure | signal slow down |
| Liveness probe | "process alive?" |
| Readiness probe | "ready to serve?" |
| Graceful shutdown | clean exit |
| SLI | Service Level Indicator |
| SLO | Service Level Objective |
| SLA | Service Level Agreement |
| Error budget | 100% - SLO |
| RPO | Recovery Point Objective (data loss) |
| RTO | Recovery Time Objective (downtime) |
| DR | Disaster Recovery |
| Chaos engineering | intentional failure injection |
| Game day | planned failure exercise |
| Postmortem | analysis after incident |
| Runbook | document procedure for incident |
← บทที่ 3 | บทที่ 5 → Worked Examples
🔤 Glossary · 📋 Style guide · 📅 last_verified: 2026-06-03