โหมดมืด
บทที่ 0 — Mindset + Fallacies
📖 คำอ่านศัพท์เทคนิคที่จะเจอบ่อยในบทนี้ (เพื่อช่วย Thai reader ที่อ่านอังกฤษช้า)
ศัพท์ คำอ่าน ความหมายสั้น latency เล-เทน-ซี่ ความหน่วง เวลาที่ต้องรอ throughput ทรู-พุท ปริมาณงานต่อหน่วยเวลา idempotent ไอ-เด็ม-โพ-เทนต์ ทำซ้ำกี่ครั้งผลก็เหมือนเดิม quorum คอ-รัม จำนวนเสียงข้างมากที่ต้องการ linearizability ลิ-เนีย-ไรซ์-อะ-บิ-ลิ-ตี้ การเห็น state ทันทีแบบลำดับเดียว consensus คอน-เซน-ซัส การที่หลาย node ตกลงค่าเดียวกัน anti-entropy แอน-ติ-เอน-โทร-ปี้ กระบวนการ sync ข้อมูลให้ตรงกัน partition พา-ทิ-ชั่น เครือข่ายแบ่งเป็นส่วน ๆ ที่คุยกันไม่ได้ replica เร็พ-พลิ-ก้า สำเนาข้อมูล jitter จิต-เตอร์ การสุ่มเวลานิดหน่อยให้ไม่ตรงกัน ศัพท์อื่น ๆ จะอธิบายตอนเจอครั้งแรก หรือดูใน glossary บท 11
1. Distributed System คืออะไร
Distributed System = ระบบที่ประกอบด้วย computer หลายเครื่อง ทำงานร่วมกัน สื่อสารกันผ่าน network และพยายามทำให้ผู้ใช้เห็นว่าเป็น "ระบบเดียวกัน" (ทั้งที่จริงๆ มีหลายเครื่องอยู่ข้างใน)
ตัวอย่างระบบ distributed ที่เราใช้กันอยู่ทุกวัน
- Google Search — server หลายพันเครื่องทำงานร่วมกันเพื่อตอบ query หนึ่งคำ
- Stripe payment — กระจาย service หลายตัวเพื่อรับชำระเงินทั่วโลก
- Netflix CDN — กระจาย video file ไว้บน edge server ใกล้ผู้ใช้
- Kafka cluster — message broker ที่กระจาย partition ไปยัง broker หลายตัว
- Postgres with read replicas — master 1 ตัว + read replica หลายตัวเพื่อ scale read
- แม้แต่ web app + DB คนละ server ก็ถือว่าเป็น distributed แล้ว!
สรุปสั้นๆ: ระบบ modern เกือบทั้งหมดเป็น distributed system — แม้แต่ระบบที่ดูเหมือนเรียบง่ายก็ตาม
2. ทำไม Distributed System ถึงยาก
ที่ distributed system ยากกว่าเขียนโปรแกรมแบบ single-machine มาก เพราะหัวใจหลักคือ network call ซึ่งเป็นคนละโลกกับ local function call ที่เราคุ้นเคย
เปรียบเทียบ Function Call ในเครื่อง vs Network Call
| ด้าน | Function call (ในเครื่อง) | Network call |
|---|---|---|
| ผลลัพธ์ | คืนค่า หรือ throw exception | คืนค่า, error, หรือไม่กลับมาเลย (timeout) |
| Latency | นาโนวินาที | มิลลิวินาที-วินาที (และแปรปรวนมาก) |
| คาดเดาได้? | คาดเดาได้ | คาดเดายาก |
| Partial failure | ไม่มี — สำเร็จหมดหรือล้มเหลวหมด | มี — บางส่วนสำเร็จ บางส่วนล้ม บางส่วนไม่รู้ผล |
สามคำที่ต้องจำ: Network ไม่น่าเชื่อถือ → เพิ่ม node ก็เพิ่มจุดที่พังได้ → distributed ยากกว่าหลายเท่า
Mental Model — Function Call vs Network Call
Function call ในเครื่อง มีผลลัพธ์เพียง 2 แบบ:
text
caller ──call──→ callee
←─return──
มีผลลัพธ์แค่ 2 อย่าง:
✅ คืนค่ากลับมา (success)
❌ Throw exception (error)
ไม่มีทางที่ 3 — function ทำงานจบเสมอNetwork call มีผลลัพธ์ที่ "เพิ่มเข้ามา" และเป็นตัวที่ทำให้ distributed ยาก:
text
caller ──request──→ callee
←─response──
มีผลลัพธ์ถึง 4 อย่าง:
✅ ได้ response กลับมา (success)
❌ ได้ error response (callee ตอบว่าไม่ได้)
❌ Connection error (network มีปัญหา)
❓ TIMEOUT (ไทม์-เอาท์ = ครบเวลาแต่ไม่ได้คำตอบ) — รอนานเกินไป แต่ไม่รู้ผล
↑ ตัวนี้แหละที่เป็นตัวร้าย — เราไม่รู้ว่าเกิดอะไรขึ้นจริงๆ
(สถานะที่เราไม่รู้ว่าฝ่ายโน้นทำสำเร็จหรือไม่)ผลของ "ภาวะไม่รู้ผล" (unknown state)
ลองนึกภาพ scenario นี้:
ผู้ใช้กดปุ่ม "โอน 100 บาท" → ระบบเรียก API → TIMEOUT
คำถาม: เงินถูกโอนไปแล้วหรือยัง?
- อาจจะโอนสำเร็จ (server ทำสำเร็จแต่ response หายระหว่างทาง)
- อาจจะยังไม่โอน (request หายก่อนถึง server)
- เราไม่มีทางรู้จากภายนอก
ทางเลือกแบบ naive ทั้งสองทางมีปัญหาทั้งคู่:
- Retry แบบไม่คิด → อาจหักเงินสองครั้ง
- ยอมแพ้แบบไม่คิด → อาจสูญเสีย transaction
ด้วยเหตุนี้เอง idempotency + retry + reconciliation จึงเป็นรากฐานของระบบ distributed — ต้องออกแบบให้รองรับ "ภาวะไม่รู้ผล" ตั้งแต่แรก
บทเรียนสำคัญ: ทุก operation ข้าม network มี "สถานะที่สาม" คือ unknown — ต้องออกแบบให้รับมือกับมันได้
3. Distributed ≠ Microservices — แยกให้ชัด
มือใหม่หลายคนสับสนระหว่าง "distributed system" กับ "microservices" — คิดว่าเป็นเรื่องเดียวกัน จริงๆ แล้วเป็นคนละ scope กันครับ
นิยามเปรียบเทียบ
Distributed System (ความหมายกว้าง)
- คือ "computer หลายเครื่องสื่อสารกันผ่าน network"
- ไม่กำหนด architecture
- ตัวอย่าง: web app + database (ใช่ — แค่นี้ก็เป็น distributed แล้ว)
- ตัวอย่าง: monolith app + Redis cache (ก็เป็น distributed)
Microservices (architectural style)
- คือการ "แบ่ง app เดียวให้กลายเป็น service เล็กๆ หลายตัว"
- แต่ละ service มี DB เป็นของตัวเอง, deploy แยกกัน, ทีมแยกกัน
- สื่อสารระหว่างกันผ่าน API หรือ event
- ตัวอย่าง: Order service + Payment service + Inventory service
Venn Diagram — ความสัมพันธ์ระหว่างสองคำ
สรุป: Microservices เป็นแค่ subset ของ distributed system — และเป็นแบบที่สุดโต่งที่สุด (แยก service เยอะที่สุด) ส่วน app modern เกือบทุกตัวก็เป็น distributed อยู่แล้ว ไม่ต้องเป็น microservices ก็ได้
เมื่อไหร่ควร "ไป distributed มากขึ้น"?
เหตุผลที่ควรเพิ่ม network hop (ทำให้ distributed มากขึ้น):
- ✅ ต้อง scale แยกกัน — DB scale คนละแบบกับ app
- ✅ ต้องการ high availability — มี replica ไว้ failover
- ✅ ต้องการ geo-distribution — ผู้ใช้กระจายทั่วโลก
- ✅ ต้องการใช้ ภาษา/runtime ต่างกัน ในแต่ละส่วน
- ✅ มี team boundary — แต่ละทีม deploy เอง
เหตุผลที่ "ยังไม่ควร" ไป distributed มากขึ้น:
- ❌ ทำเพราะ "best practice" ทั้งที่ไม่จำเป็น
- ❌ Resume-driven development — อยากเขียน microservices ใน resume
- ❌ Premature optimization — ระบบยังเล็กก็แบ่งซะเยอะ
- ❌ ทีมเดียว product เดียว → monolith ก็พอ
Heuristic ในการตัดสินใจ: เริ่มจาก monolith + remote DB (ซึ่งก็เป็น distributed อยู่แล้ว!) แล้วค่อย split ออกเมื่อเจอ pain จริงๆ — อย่ารีบ split ก่อนเวลา
4. 8 Fallacies of Distributed Computing — 8 ความเชื่อผิดๆ ที่ทุกคนเคยมี
หลักการนี้พัฒนาแบบค่อย ๆ ต่อยอด (iterative — แบบรุ่นแล้วรุ่นเล่า) ที่ Sun Microsystems (บริษัทคอมพิวเตอร์ยุค 80-90 ผู้สร้างภาษา Java และ Solaris) โดยทีมงานหลายคนต่อยอดกัน — เป็น "checklist" ที่ developer ทุกคนต้องรู้ก่อนเขียน distributed system
📜 Attribution (ใครเป็นคนตั้ง — สรุปสั้น): รายการ 8 ข้อนี้ตั้งโดยทีมที่ Sun Microsystems รวมหลายคนข้ามรุ่น (Bill Joy + Tom Lyon เริ่ม 4 ข้อแรก ~1991, Peter Deutsch เพิ่ม 5-7 ~1994, James Gosling เพิ่มข้อ 8 ~1997) — ไม่ใช่ใครคนเดียว
📚 รายละเอียดประวัติเต็ม (สำหรับคนที่สนใจ history เท่านั้น — มือใหม่ข้ามได้)
- Bill Joy + Tom Lyon (~1991) — กำหนด 4 ข้อแรก (network reliable, latency zero, bandwidth infinite, network secure) ในยุค Sun Microsystems ทำ RPC + NFS
- Peter Deutsch (~1994) — เพิ่มข้อ 5, 6, 7 (topology changes, one administrator, transport cost) ในยุคหลัง — มักถูกอ้างผิดว่าเป็นคนตั้งทั้ง 8 ข้อ แต่จริง ๆ มาทีหลังเพื่อต่อยอดของ Joy/Lyon
- James Gosling (~1997) — เพิ่มข้อ 8 (network homogeneous) ตอนพัฒนา Java
(ข้อมูลปีและผู้ตั้งส่วนใหญ่อ้างจากบันทึก Sun Wiki / Arnon Rotem-Gal-Oz "Fallacies of Distributed Computing Explained" ไม่มี primary source แบบ paper ตรง ๆ — ใช้เป็น history ที่ยอมรับกันในวงการ แต่ถ้าต้องอ้างอิงทางการให้ระบุที่มาว่า "ตามบันทึก Sun Microsystems" — flagged 2026-06)
8 ข้อที่ developer มักเข้าใจผิดเกี่ยวกับ network
- เครือข่ายเชื่อถือได้ (The network is reliable) — network เชื่อถือได้
- ส่งข้อมูลไม่ใช้เวลา (Latency is zero) — ส่งข้อมูลไม่ใช้เวลา
- Bandwidth ไม่จำกัด (Bandwidth is infinite) — ส่งข้อมูลได้ไม่จำกัด
- เครือข่ายปลอดภัย (The network is secure) — network ปลอดภัย
- โครงสร้างเครือข่ายไม่เปลี่ยน (Topology doesn't change) — โครงสร้าง network ไม่เปลี่ยน
- มี admin คนเดียวคุม (There is one administrator) — มี admin คนเดียวคุม
- ส่งข้อมูลฟรี (Transport cost is zero) — ส่งข้อมูลฟรี
- เครือข่ายเหมือนกันหมด (The network is homogeneous) — network เหมือนกันหมด
ทั้ง 8 ข้อนี้ผิดทั้งหมด ในระบบ distributed จริง — ถ้าออกแบบโดยเชื่อข้อใดข้อหนึ่ง = ระบบจะพังในวันใดวันหนึ่งแน่นอน
Fallacy 1 — Network เชื่อถือได้ (The Network is Reliable)
ความเป็นจริงที่ต้องยอมรับ: network ล้มเหลวได้หลายแบบและเกิดขึ้นทุกวันใน production
- Packet ตกหล่น — TCP retransmit ได้บ้าง แต่บางครั้งก็เกินขีดจำกัด
- Connection timeout — server รับ connection ไม่ทันเพราะ overload
- Server crash กลางทาง — กำลังประมวลผลอยู่ดีๆ ก็ตาย
- สาย fiber ขาด — มี construction มา dig ใกล้ๆ
- Router config ผิด — network engineer พลาด
- DDoS attack — โดน flood จนใช้งานไม่ได้
ความน่าจะเป็นต่อ request เดียวอาจจะน้อย — แต่เมื่อระบบมี request หลายล้านครั้งต่อวัน ก็แทบจะเจอทุกวัน
ออกแบบยังไงให้รับมือได้:
- ✅ Retry with backoff — ลองใหม่แต่รอเวลาเพิ่มขึ้น (exponential backoff)
- ✅ Timeout ทุก call — อย่ารอแบบ infinite
- ✅ Idempotency — retry แล้วต้องไม่มี side effect ซ้ำ
- ✅ Circuit breaker — ตัด traffic เมื่อ downstream มีปัญหา
- ✅ Replication — ทำสำเนาข้อมูลสำคัญไว้หลายที่
Fallacy 2 — Latency = 0 (Latency is Zero)
ความเป็นจริง: การส่งข้อมูลข้าม network ใช้เวลาเสมอ และ "speed of light" คือ ขีดจำกัดของจักรวาล ที่เลี่ยงไม่ได้
| สถานการณ์ | Latency โดยประมาณ (2026) | หมายเหตุ |
|---|---|---|
| ในเครื่องเดียวกัน (function call) | < 1 μs (microsecond — ไมโครวินาที = 1/1,000,000 วินาที) | local call |
| Same data center (intra-DC RTT) | ~0.5 ms | modern DC backbone |
| Same region (เช่น Singapore → Singapore) | 1-5 ms | LAN-ish |
| US East ↔ Western Europe | 70-100 ms (best, MAREA/Dunant cable) ถึง ~150 ms (suboptimal route) | ขึ้นกับ submarine cable + routing |
| US ↔ Asia | 100-200 ms | จากเอเชียไปฝั่งใด ๆ |
| Cross-region (ทั่วไป) | 100-200 ms | |
| Mobile network 5G (good coverage) | +10-30 ms | เทียบเท่า WiFi |
| Mobile network 4G LTE (typical) | +50-100 ms | |
| Mobile network 3G/poor coverage | +100-500 ms | ค่าเดิม Jeff Dean ยุค 2012 |
เพดานทางฟิสิกส์:
- แสงในสุญญากาศ: 3 × 10⁸ m/s → ~3.3 ms ต่อ 1000 km
- สัญญาณในไฟเบอร์: ~2 × 10⁸ m/s (ช้ากว่าสุญญากาศ ~30% เพราะ refractive index ของแก้ว) → ~5 ms ต่อ 1000 km
- ดังนั้นเลข "5 ms ต่อ 1000 km" ที่ใช้กันในวงการ network คือ "แสงในไฟเบอร์" ไม่ใช่สุญญากาศ
- ไม่ว่าจะเป็นกรณีไหน — เร็วกว่านี้ไม่ได้ตามฟิสิกส์
ออกแบบยังไงให้รับมือได้:
- ✅ CDN — cache content ไว้ใกล้ผู้ใช้ (edge)
- ✅ Read replica ตามภูมิภาค — อ่านจาก replica ที่ใกล้ที่สุด
- ✅ Async — ไม่ต้องรอ ส่งงานไปแล้วทำอย่างอื่นต่อ
- ✅ Batch request — รวบรวมหลาย request ส่งทีเดียว
- ✅ Cache — ของไหนใช้บ่อยให้เก็บไว้ก่อน ไม่ต้องโทรไปถามทุกครั้ง
Fallacy 3 — Bandwidth ไม่จำกัด (Bandwidth is Infinite)
ความเป็นจริง: bandwidth มีจำกัด ใช้ร่วมกันหลายคน และ "แพง" ด้วย
- 1 Gbps link ในเครื่องนึง อาจถูกแชร์โดยหลายร้อย service
- Mobile network มี bandwidth 1-100 Mbps และแปรปรวนตามสภาพอากาศ/ทำเล
- AWS egress คิด $0.09 ต่อ GB — ส่งข้อมูลออกจาก cloud แพงมาก
- Backbone internet ก็ congest ในช่วง peak hour (เช่นช่วงเย็นในไทย)
ออกแบบยังไงให้รับมือได้:
- ✅ Compression — ใช้ gzip, brotli ก่อนส่ง
- ✅ Pagination — แบ่งข้อมูลเป็นหน้าๆ ไม่ส่งทีเดียว
- ✅ Stream — file ใหญ่ส่งแบบ stream ไม่ load หมดก่อน
- ✅ CDN — ลด egress จาก origin server
- ✅ เลือก protocol ให้เหมาะ — binary (Protobuf, gRPC) เล็กกว่า JSON มาก
Fallacy 4 — Network ปลอดภัย (The Network is Secure)
ความเป็นจริง: network ไม่มี "พื้นที่ปลอดภัย" จริง — แม้แต่ภายใน data center ก็โดน attack ได้
ภัยคุกคามที่ต้องรู้:
- MITM (อ่าน เอ็ม-ไอ-ที-เอ็ม = Man-in-the-Middle = คนกลางดักฟัง) — มีคนแอบดักกลางทาง
- DNS spoofing (ดี-เอ็น-เอส สปูฟ-ฟิง = ปลอมแปลง DNS) — มีคนหลอกว่าตัวเองคือ DNS server
- ARP poisoning (อ่าน เอ-อาร์-พี = Address Resolution Protocol = วางยา) — ภายใน LAN มีคนหลอกว่าตัวเองคือ gateway
- Public WiFi sniffing (สนิฟ-ฟิง = ดักฟังจมูก) — WiFi เปิดที่ café = ทุกคนใน WiFi อ่าน packet ได้
- VPN leak (วี-พี-เอ็น ลีค = รั่ว) — VPN configure ผิด → ข้อมูลรั่ว
ออกแบบยังไงให้รับมือได้:
- ✅ TLS ทุกที่ — แม้แต่ service-to-service ภายใน data center
- ✅ Mutual TLS (mTLS) — service ต้องยืนยันตัวตนกันสองทาง
- ✅ Network segmentation — แบ่ง zone จำกัดการเข้าถึง
- ✅ Zero trust architecture — ไม่ trust network ใดๆ ทั้งสิ้น
- ✅ Encrypt at rest + in transit — เข้ารหัสทั้งตอนเก็บและตอนส่ง
ศัพท์ภัยคุกคามที่ต้องรู้ — อธิบายแบบเข้าใจง่าย
MITM (Man-in-the-Middle) — คนกลางดักฟัง
text
Client ──→ [Attacker] ──→ Server
↑ relay + อาจอ่าน/แก้ข้อมูล- ไม่มี TLS: attacker อ่านได้ทุกอย่าง รวมถึง password, session token
- มี TLS: attacker เห็นแต่ข้อมูลที่ encrypt แล้ว = ปลอดภัย
- วิธีป้องกัน: TLS (= HTTPS encryption layer — ชั้นเข้ารหัสของ HTTPS) + certificate pinning (= app จำ certificate ของ server ไว้ ถ้าเปลี่ยน = ไม่ trust)
DNS Spoofing / Cache Poisoning — หลอก DNS
- ผู้โจมตีสร้าง "evil-server" แล้วหลอกระบบ DNS ว่าตัวเองคือ
example.com - Client ค้น DNS → ได้ IP ของผู้โจมตีไปแทน → ส่งข้อมูลให้คนผิด
- วิธีป้องกัน: DNSSEC (= DNS ที่ใส่ลายเซ็นกัน spoof), DNS over HTTPS (DoH = ส่ง DNS query ผ่าน HTTPS encrypted แทน UDP plain text)
ARP Poisoning — หลอกใน LAN
- ผู้โจมตีบอก switch ของ LAN ว่า "ฉันคือ 192.168.1.1 (gateway)"
- ทุก traffic ใน LAN วิ่งผ่านผู้โจมตี
- วิธีป้องกัน: VLAN (= Virtual LAN, แบ่ง LAN เป็นกลุ่มย่อยที่คุยกันไม่ได้) segmentation, port security บน switch
Public WiFi Sniffing — ดักจับใน WiFi เปิด
- WiFi ที่ไม่มี password = ใครก็จับ packet ได้
- ไม่มี TLS: เห็น HTTP request + credentials ใน plain text
- มี TLS: เห็นแค่ encrypted blob = ปลอดภัยพอสมควร
- วิธีป้องกัน: ใช้ TLS ทุกที่, ใช้ VPN ตอนทำธุรกรรมสำคัญ
VPN Leak — VPN รั่ว
- VPN เปิดอยู่ แต่ DNS query ดันไปออก ISP โดยตรง (DNS leak)
- หรือบาง app ข้าม VPN ไปใช้ network ปกติ (split tunnel)
- วิธีป้องกัน: เปิด kill switch (= ถ้า VPN หลุด ให้ block network ทั้งหมดทันที), ทดสอบ DNS leak ก่อนใช้งาน
บทเรียนสำคัญ: ให้คิดว่า network เป็น "เขตอันตราย" เสมอ — แม้แต่ภายใน data center Zero trust (= ไม่เชื่อใจ network ใด ๆ ทั้งสิ้น) = TLS + auth ทุก hop ไม่ว่าจะเป็น traffic ภายในหรือภายนอก
💡 service-to-service ใช้ mTLS (= Mutual TLS, ทั้ง client และ server ต้องยืนยันตัวตนกันสองทาง — ต่างจาก TLS ปกติที่ verify แค่ฝั่ง server)
Fallacy 5 — Topology ไม่เปลี่ยน (Topology Doesn't Change)
ความเป็นจริง: ในยุค cloud + Kubernetes — server มาแล้วก็ไป IP เปลี่ยนได้ตลอดเวลา
- Auto-scaling — server มา/หายตามโหลด
- Pod restart ใน Kubernetes — IP ใหม่ทุกครั้ง
- Cloud instance restart — IP เปลี่ยน
- เพิ่ม data center ใหม่ — routing เปลี่ยน
- Service rename — endpoint URL เปลี่ยน
- Network route เปลี่ยน — backbone provider ปรับ
ออกแบบยังไงให้รับมือได้:
- ✅ Service discovery — ใช้ DNS, Consul, K8s Service ค้นหา service
- ✅ อย่า hardcode IP — เก็บใน config + reload ได้
- ✅ Health check + load balancer skip node ที่ unhealthy
- ✅ Service mesh จัดการเรื่อง dynamic ให้
Fallacy 6 — มี Administrator คนเดียว (There is One Administrator)
ความเป็นจริง: ใน organization ขนาดใหญ่ — มีหลายทีมเป็นเจ้าของ service ต่างๆ และไม่มีคนเดียวที่คุมทุกอย่าง
- หลายทีม own service คนละตัว
- มี dependency ข้าม organization
- แต่ละทีมมี priority ไม่เหมือนกัน
- แต่ละทีมมี SLA ของตัวเอง
- on-call ก็แยกทีม
ออกแบบยังไงให้รับมือได้:
- ✅ API contract ชัดเจน — เปลี่ยนต้องแจ้งล่วงหน้า
- ✅ Versioning — รองรับหลาย version พร้อมกัน
- ✅ SLA เขียนไว้เป็นเอกสาร ไม่ใช่แค่พูดปาก
- ✅ Resilient ต่อ dependency failure — service อื่นล่มก็ยังทำงานต่อได้
- ✅ แต่ละ service มี monitoring เป็นของตัวเอง
Fallacy 7 — ส่งข้อมูลฟรี (Transport Cost is Zero)
ความเป็นจริง: Cloud คิดค่า bandwidth — และคิดเยอะ! โดยเฉพาะการส่งข้อมูลออกจาก cloud (egress)
| ประเภท traffic | ราคาประมาณ (AWS-specific, snapshot 2026-06) |
|---|---|
| Cloud egress (ออก internet) | $0.05-0.15 ต่อ GB |
| Cross-region (ข้ามภูมิภาคใน cloud เดียวกัน) | $0.02 ต่อ GB (varies by region pair) |
| Inter-AZ (ข้าม availability zone) | $0.01 ต่อ GB |
💡 Context สำคัญ (2024-2026):
- AWS / GCP / Azure ประกาศ free egress สำหรับลูกค้าที่ migrate ออก (Data Egress Free initiatives, ปี 2024)
- Cloudflare R2 = $0 egress มาตั้งแต่เปิดตัว
- EU Data Act บังคับใช้ Jan 2027 → กดดันราคา egress ของ cloud ทุกเจ้าลงอีก
- ตัวเลขข้างบนเป็น AWS-specific และเปลี่ยนเร็ว — เช็ค AWS / GCP / Azure pricing page ทางการก่อนคำนวณจริง
ตัวอย่างคำนวณ:
- ย้ายข้อมูล 1 TB ข้าม region = ~$20
- ย้ายข้อมูล 1 PB = ~$20,000
ข้อมูลที่ดูเหมือนเล็ก สะสมแล้วเป็นเงินจำนวนมาก — โดยเฉพาะระบบที่ส่งข้อมูลข้าม region บ่อยๆ
ออกแบบยังไงให้รับมือได้:
- ✅ Process data ในที่ที่เก็บ — ไม่ดึงไป process ที่อื่น
- ✅ Aggregate ก่อน transfer — ส่งสรุปแทนที่จะส่งทุก record
- ✅ Cache ใกล้ผู้ใช้ — ลด round trip
- ✅ ใช้ CDN ลด origin egress
Fallacy 8 — Network เหมือนกันหมด (The Network is Homogeneous)
ความเป็นจริง: network ในระบบใหญ่มีหลายแบบมาก — เปลี่ยนเทคโนโลยี + เปลี่ยน behavior
- Protocol ต่างกัน — HTTP, gRPC, AMQP, MQTT แต่ละตัวมีพฤติกรรมต่างกัน
- Security model ต่างกัน — บาง zone trust กัน บาง zone ไม่
- Latency profile ต่างกัน — บาง path เร็ว บาง path ช้า
- Mobile vs WiFi vs LAN — สภาพแวดล้อมต่างกันสิ้นเชิง
ออกแบบยังไงให้รับมือได้:
- ✅ อย่า assume environment — ทดสอบหลายๆ แบบ
- ✅ ทดสอบใน network ที่เหมือน production — ใช้ chaos engineering ดู
- ✅ ตั้ง timeout ตาม path — บาง path ใช้ timeout สั้น บาง path ยาว
บทเรียนรวมของ 8 Fallacies: เขียนโปรแกรมแบบป้องกัน (defensive programming) เสมอ — assume ทุกอย่างพัง แล้วออกแบบให้ระบบรอด
5. Distributed Systems "Truths" — ความจริงที่ต้องยอมรับ
นอกจาก 8 fallacies (สิ่งที่ developer คิดผิด) — ยังมี "ความจริง" อีก 7 ข้อที่ดูแปลกแต่ต้องยอมรับ ความจริงเหล่านี้ทำให้ distributed system "ผิดธรรมชาติ" ของการเขียนโปรแกรมแบบเดิมโดยพื้นฐาน
หมายเหตุ: หัวข้อนี้นำเสนอเป็นข้อ 9-15 (นับต่อจาก 8 fallacies เพื่อให้อ่าน flow ต่อ ๆ ไปไม่งง) — แต่ ไม่ต้องจำเลข ทั้ง 7 ข้อนี้เป็น "ความจริง" อิสระต่อกัน อ่านในมุมไหนก็ได้
9. นาฬิกาไม่ตรงกัน — Clocks are not synchronized
- ปัญหา: แต่ละ server มีนาฬิกาของตัวเอง — แม้จะ sync ผ่าน NTP (Network Time Protocol — โปรโตคอลตั้งนาฬิกาผ่าน network — ดูบทที่ 4) ก็ยังเหลื่อมได้ ±10-100 ms (สำหรับ wall clock = "นาฬิกาแขวนผนัง" ที่เห็นเป็นเวลาปกติ ต่างจาก monotonic clock ที่เดินหน้าทางเดียวเพื่อวัดระยะเวลา)
- ผลกระทบในทางปฏิบัติ:
- "Created at 10:00:01 vs 10:00:02" → อะไรก่อน? ไม่รู้แน่นอน เพราะนาฬิกาเหลื่อม
- "Last write wins" ตาม timestamp → server ไหนนาฬิกาเร็ว = ชนะตลอด (อาจไม่ใช่ write ล่าสุดจริง)
- Lock TTL อิง wall clock → clock skew → bug ที่หา root cause ยาก
ทางแก้: ใช้ logical clock + HLC ดู บทที่ 4
10. Node สองตัวเห็น state ไม่เหมือนกัน — Two computers can disagree about state
- ปัญหา: ในเวลาเดียวกัน server A และ B อาจเห็น state ต่างกัน
- A:
x = 5(เพิ่งเขียน) - B:
x = 0(ยังไม่ propagate มาถึง)
- A:
- บทเรียน: ไม่มี "global truth" — มีแค่ "view per node" เท่านั้น
ทางแก้: ออกแบบให้ระบบยอมรับ eventual consistency ดู บทที่ 1
11. ลำดับเหตุการณ์ขึ้นกับมุมมอง — Order of events depends on observer
- สถานการณ์:
- Event A เกิดที่ Node 1 ตอน 10:00:00.005
- Event B เกิดที่ Node 2 ตอน 10:00:00.003 (เร็วกว่า A ตามเวลาจริง?)
- มุมมองที่ต่างกัน:
- จาก Node 1 → เห็น A ก่อน (เพราะ A เกิดที่นี่)
- จาก Node 2 → เห็น B ก่อน
- บทเรียน: "ใครเกิดก่อน" — ขึ้นอยู่กับ "ใครเป็นผู้สังเกต"
ทางแก้: ใช้ Lamport clock หรือ Vector clock แทน physical clock ดูบทที่ 4
12. Failure detection ไม่สมบูรณ์ — ตรวจจับ "ตาย" ไม่ได้ 100%
- คำถามหลัก: Heartbeat หาย 5 วินาที = ตายจริง? หรือแค่ช้า?
- Trade-off:
- ถ้าประกาศตายเร็วเกิน → false positive → อาจเกิด split brain
- ถ้าประกาศตายช้าเกิน → false negative → request ไป pile up ที่ node ที่ตายแล้ว, timeout เป็นพันๆ
ทางแก้: ใช้ Phi Accrual failure detector — วิธีตรวจ node ตายแบบ probabilistic (ตอบเป็นค่า "ความน่าจะตาย %" แทน yes/no) ดู Section 15
13. ไม่มี Global State — มีแค่ View ของแต่ละ Node
- Global state ในอุดมคติ: ทุก node เห็นข้อมูลเดียวกันที่เวลาเดียวกัน
- ความเป็นจริง: เป็นไปไม่ได้ — เพราะ relativity ของเวลา + network latency
- เรามีแค่:
- Snapshot per node — มุมมองของตัวเอง
- Eventually agreement — ในที่สุดจะตรงกัน (แต่ไม่ใช่ทันที)
ทางแก้ขั้นสูง (optional): Chandy-Lamport snapshot algorithm — algorithm ปี 1985 ของ K. Mani Chandy + Leslie Lamport สำหรับเก็บ "global snapshot" ของระบบแบบประมาณ (approximate) โดยไม่ต้องหยุดทุก node — ใช้ในงาน research/critical infra, ไม่จำเป็นสำหรับ application dev ทั่วไป
14. ระบบมี "บางอย่างพัง" อยู่เสมอ — The system is always partially broken
ลองคำนวณดู: ใน cluster ที่มี 1000 node
- Server hardware fail rate ประมาณ 5-10% ต่อปี
- = อย่างน้อย 50-100 server ตายต่อปี
- คำนวณ: 365 วัน ÷ 50 ตาย = ~7.3 วัน/ครั้ง (กรณี 5%), 365 ÷ 100 = ~3.6 วัน/ครั้ง (กรณี 10%)
- เฉลี่ย ~1 server เสีย ทุก 3.6-7.3 วัน
สาเหตุที่พังในระดับ production:
- Hardware: disk fail, NIC (Network Interface Card — การ์ดเครือข่าย) fail, CPU thermal (CPU ร้อนเกิน), RAM bit flip (RAM เปลี่ยนค่า 0↔1 เองจาก cosmic ray หรือ electrical noise)
- Network: network blip, switch reboot, cable cut
- ระบบ "ปกติ" คือมี node ตายอยู่เสมอ — ไม่ใช่เรื่องผิดปกติ
Mindset shift:
| มุมมอง | คิดแบบเดิม | คิดแบบ distributed |
|---|---|---|
| ระบบทำงาน 100% | "ปกติ" (ตาย = abnormal) | "ฝันเฟื่อง" |
| ระบบทำงาน 95-99% | "ผิดปกติ" | "ปกติ" (ตายบ้าง = expected) |
15. Performance แกว่งมาก — Performance varies wildly
สถานการณ์จริง: request เดียวกัน, data เดียวกัน, รัน 1000 ครั้ง:
| Percentile | Latency | เทียบกับ p50 |
|---|---|---|
| p50 (median) | 10 ms | baseline |
| p99 | 100 ms | 10x ช้ากว่า |
| p999 | 500 ms | 50x ช้ากว่า! |
| max | 5000 ms | network hiccup |
สาเหตุที่แกว่ง:
- GC pause — GC (Garbage Collection — การเก็บขยะหน่วยความจำ) ของ Java/Go ทำให้ process หยุดทำงานชั่วครู่ (ดู glossary บท 11)
- 2026 update: Go (>=1.5) และ JVM ตัว ZGC/Shenandoah (GA 2018-2021) ปกติ pause ระดับ sub-millisecond
- Generational ZGC GA ใน JDK 21 (2023) และเป็น default ตั้งแต่ JDK 23+ → sub-ms pause พร้อม throughput ดีกว่า original ZGC มาก
- G1 GC (เคย JVM default) ค่า MaxGCPauseMillis default = 200ms (ไม่ใช่ ~100ms)
- pause 5-10s ที่เห็นในตัวอย่างถัด ๆ ไปเป็นกรณี G1 ที่ไม่ tune หรือ JVM รุ่นเก่า (pre-JDK 17) ไม่ใช่ baseline ปกติของ 2026 (last reviewed: 2026-06)
- Disk seek — HDD seek time หรือ SSD garbage collection
- Network jitter (จิต-เตอร์ — การแกว่ง) — packet ตกล่าช้าผิดปกติ
- CPU steal time (สตีล-ไทม์ — เวลาที่ "ถูกขโมย")
- คำอธิบายง่าย ๆ ก่อน: VM ของเราโดน VM อื่นบนเครื่องเดียวกันแย่ง CPU
- คำอธิบายเทคนิค: เวลาที่ VM ของเราถูก hypervisor (= software ที่ run VM หลายตัวบนเครื่อง physical เดียวกัน) scheduler พักไว้ เพราะ physical CPU กำลังถูก VM อื่นบนเครื่องเดียวกันใช้งานอยู่
- เห็นใน
topคอลัมน์stสูง = noisy neighbor (สำนวนอเมริกัน "เพื่อนบ้านเสียงดัง" — มาจากภาพการอยู่อพาร์ตเมนต์ที่เพื่อนบ้านห้องข้าง ๆ ทำเสียงดังจนเรานอนไม่หลับ; ใน cloud หมายถึง VM อื่นที่อยู่บนเครื่อง physical เดียวกันแล้วใช้ CPU หนักจนเรากระทบ) บน cloud - ทางแก้: ใช้ Dedicated Instances / Bare Metal (เช่น AWS Graviton dedicated tenancy) → ~0% steal time แต่แพงกว่า
- TCP retransmit (รี-ทรานส์-มิต — ส่งซ้ำ) — packet ที่ผู้ส่งไม่ได้รับ ACK (= Acknowledgement, สัญญาณตอบรับว่าได้รับ) ภายใน timeout จะถูกส่งซ้ำโดยอัตโนมัติตามโปรโตคอล TCP → app เห็นเป็น latency spike ไม่ใช่ error
- 2026 update: stack ที่ใช้ BBR congestion control (Google 2016, default ใน Linux kernel ใหม่ ๆ) จะมี retransmit-related latency spike น้อยกว่า CUBIC algorithm แบบเดิม
บทเรียน: ดู tail latency (tail = หาง, p99 และ p999 — ความหน่วงที่ส่วนหางของกราฟ คือ 1% และ 0.1% ของ request ที่ช้าสุด) เสมอ — ไม่ใช่แค่ค่าเฉลี่ย เพราะ average ซ่อนปัญหาที่ผู้ใช้บางคนเจอ
📊 (ขั้นต่อยอด · Senior level) Measuring percentiles in production — เลือก algorithm ถูก:
⚠️ มือใหม่: ข้ามไปข้อ 6 (หัวข้อ Failure Models) ได้เลย — section นี้สำหรับคนทำ observability infrastructure
basic ใช้ Prometheus histogram ตามปกติพอ; ตัวเลือกข้างล่างเป็น "ขั้นต่อยอด" เมื่อต้อง aggregate ข้าม services จำนวนมาก
- Naive (sort + index) — เก็บค่าทั้งหมดแล้ว sort หา index ที่ตำแหน่ง p99; O(n log n) memory; ใช้ได้กับ batch แต่ไม่ scale streaming
- t-digest (อ่าน ที-ไดเจสต์, Ted Dunning 2013) — clustering-based (algorithm ที่จัดข้อมูลเป็น cluster น้ำหนักไม่เท่ากัน, cluster หนาแน่นที่ tail เพื่อให้แม่นที่ percentile สูง), error ~1-2% ที่ p99/p999, memory ~5 KB; ใช้ใน Datadog, Cassandra, Solr, Elasticsearch (Datadog/Solr/Elasticsearch = observability/search tools ที่ใช้กันแพร่หลายในวงการ)
- HDR Histogram (อ่าน เอช-ดี-อาร์ ฮิสโตแกรม = High Dynamic Range, ของ Gil Tene) — bucket-based (แบ่งเป็นช่อง buckets ที่กำหนดล่วงหน้า) ให้ exact percentile ในช่วง range ที่ตั้งไว้, memory คงที่; ใช้ใน Java microservices (Reactive Tracker, Micrometer)
- Prometheus histogram — bucket-based; 2022 update: Prometheus native histograms (sparse histograms) GA ใน v2.40 (Nov 2022, default ตั้งแต่ปี 2024) แก้ปัญหา cross-instance aggregation โดยใช้ sparse buckets ที่ merge ได้แม่นยำแล้ว — รุ่นเก่า (classic histogram) buckets fix ตอน scrape → คำนวณ percentile แบบ post-hoc แม่นยำไม่ได้
กฎทอง: ห้าม "average percentiles" ข้าม services — p99 ของ aggregate ≠ average ของ p99s ทำไม? p99 = ค่าที่ตำแหน่ง 99% หลัง sort ทั้ง dataset ซึ่งไม่ใช่ linear function ของข้อมูล → เฉลี่ย p99 ของ 10 instance ≠ p99 ของข้อมูลรวมทั้ง 10 instance (counterexample: instance A มี p99=10, B มี p99=10 แต่ tail ของ A กับ B อาจรวมกันเป็น p99=20) วิธีที่ถูก: t-digest merge หรือ HDR add ที่ระดับ raw structure ก่อนค่อยคำนวณ percentile, หรือใช้ Prometheus native histograms รุ่นใหม่
บทเรียนรวมของหัวข้อ 5: สิ่งเหล่านี้ไม่ใช่ "bug ในระบบ" — เป็น physical reality ของ network + hardware ทุก distributed system ต้องออกแบบโดยรู้ว่าสิ่งเหล่านี้เกิดขึ้นตลอดเวลา
6. Failure Models — รูปแบบความล้มเหลว
ก่อนออกแบบให้รับมือกับ failure ได้ ต้องรู้ก่อนว่า "ล้มเหลว" มีกี่แบบ เพราะแต่ละแบบต้องใช้ strategy ต่างกัน
ลองนึกว่ามีพนักงานทำงานในออฟฟิศหลายคน — แต่ละคนอาจ "หาย" ได้หลายแบบ:
Crash-Stop — "ตายแล้วตายเลย"
พนักงานหมดสติแล้วไม่กลับมาอีก เราเปลี่ยนตัวใหม่ได้
- พฤติกรรม: process crash แล้ว หยุดไปตลอดกาล ไม่กลับมาอีก
- ตรวจจับง่าย: ping ไม่ตอบ = ตายแน่ ๆ
- วิธีรับมือ: ตัดออกจากระบบ + จัดสรรงานให้ node อื่น
- ตัวอย่างจริง: K8s pod ที่ถูก kill โดยเจตนา (เช่นในขั้นตอน rolling update)
เปรียบเทียบกับโลกจริง: เครื่อง server ที่ Power Supply ขาด → ไม่มีทางกลับมาเอง ต้องเปลี่ยน hardware
Crash-Recovery — "ตายแล้วฟื้น"
พนักงานหมดสติชั่วคราว แล้วฟื้นกลับมาทำงานต่อ — แต่จำงานที่ทำค้างไว้ได้ไหม?
- พฤติกรรม: process crash แล้ว restart กลับมาในที่สุด (อาจใช้เวลาไม่กี่วินาที — ไม่กี่นาที)
- พบบ่อยที่สุด ใน production จริง — Java OOM (Out Of Memory — หน่วยความจำเต็มจน process ถูก kill), deploy ใหม่, host restart
- ความท้าทาย: ต้องจัดการ "state ก่อนล้มเหลว" ให้ถูก
- กรณีที่ตึงเครียด: ถ้า process กำลัง write ตอน crash:
- write นั้นเสร็จไหม?
- ถ้า client retry → จะ double-write?
- ดังนั้น idempotency = สำคัญมาก ในการป้องกัน double-write
ตัวอย่าง: K8s pod ที่ crash แล้วโดน restart อัตโนมัติ — pod ใหม่ต้องอ่าน state จาก persistent disk + ตัดสินใจว่างานค้างไหนจะทำต่อ
Byzantine — "พนักงานนิสัยเสีย / โดน hack"
พนักงานบอก A กับเพื่อน 1 แต่บอก B กับเพื่อน 2 — ตั้งใจหลอก หรือถูกควบคุมโดยศัตรู
- พฤติกรรม: process ส่ง ข้อมูลผิด / ปนเปื้อน / มีเจตนาร้าย
- ส่ง message ต่างกันให้คนละ node
- Return ค่าผิดตั้งใจ
- พยายามทำให้ระบบไม่ agree กัน
- พบน้อยใน: ออฟฟิศ / data center เดียวกัน (trusted environment)
- พบบ่อยใน:
- Public blockchain — ใครก็เข้าได้
- Multi-organization system — ไม่ได้ trust กันสนิท
- IoT — อุปกรณ์ภายนอกอาจถูก hack
วิธีรับมือ: ต้องใช้ Byzantine Fault Tolerance (BFT) algorithm + checksum + multi-source verification — จะพูดลึกในบทที่ 2
Partial Failure — "บางคนได้ บางคนไม่ได้" (โหดสุด!)
ส่งคำสั่งให้พนักงาน 3 คน → 2 คนบอก "เสร็จแล้ว", 1 คนเงียบหายไป — งานนั้นถือว่าเสร็จไหม?
- พฤติกรรม: บาง operation สำเร็จ บางอันล้มเหลว — ไม่ทั้งหมด ไม่ทั้งไม่
- ตัวอย่าง: เขียนข้อมูลไป 3 replicas:
- Replica 1: ACK เสร็จ
- Replica 2: ACK เสร็จ
- Replica 3: timeout — ไม่ตอบ
- คำถาม: replica 3 ได้ data ไหม?
- อาจได้ (แค่ response ตอบช้า)
- อาจไม่ได้ (request ไม่ถึงเลย)
- เราตอบไม่ได้!
- ทำไมโหดสุด: ระบุไม่ได้ว่า "ส่วนไหนสำเร็จ ส่วนไหนล้มเหลว" — เป็นคนละความหมายกับ "ทั้งสำเร็จ" และ "ทั้งล้มเหลว"
วิธีรับมือ: ใช้ quorum protocol (majority vote) — ถ้าส่วนใหญ่ ACK ก็ถือว่าเสร็จ ไม่ต้องสนใจ minority — จะพูดในบทที่ 1
Network Partition / Split Brain — "เครือข่ายขาดเป็น 2 ส่วน"
พนักงานในห้อง A กับห้อง B คุยกันไม่ได้ (กำแพงพัง) — แต่ทั้งสองห้องยังทำงานได้อยู่:
- พฤติกรรม: network แบ่ง cluster ออกเป็น 2 ฝั่ง
- Group A: [Node1, Node2]
- Group B: [Node3, Node4, Node5]
- A คิดว่า B ตาย, B คิดว่า A ตาย — ทั้งคู่ยังรับ request จากภายนอกได้
ความเสี่ยง — split brain:
- A เลือก leader ใหม่ (เพราะคิดว่า leader เก่าใน B ตาย)
- B ก็เลือก leader ใหม่เช่นกัน
- ตอนนี้มี 2 leader ทำงานพร้อมกัน
- เขียน data ขัดแย้งกัน
- พอ network heals → ใครชนะ? ใครต้องรอลบ?
วิธีรับมือ: ใช้ quorum (= ต้องมีเกินครึ่ง majority ถึงจะตัดสินใจได้ — อธิบายเต็มในบท 1) — ต้องมี majority (> N/2) ถึงจะ "เป็น leader ได้" — ทำให้มีเพียงฝั่งเดียวเท่านั้นที่ทำงานต่อได้ (ฝั่งที่เป็น minority หยุดรับ write)
สรุปตาราง — เลือก strategy ตาม failure mode
| Failure Mode | ความถี่ในระบบจริง | Strategy หลัก |
|---|---|---|
| Crash-Stop | กลาง (hardware failure) | Health check + remove + replace |
| Crash-Recovery | สูงสุด (deploy, OOM, ฯลฯ) | Idempotency + state recovery + WAL |
| Byzantine | ต่ำมากใน trusted env (แต่ silent data corruption เช่น RAM bit flip / disk corruption นับเป็น Byzantine-like ที่เกิดได้แม้ใน trusted env) | BFT (เฉพาะ blockchain ส่วนใหญ่) + checksum/ECC |
| Partial Failure | สูงมาก | Quorum + retry + idempotent |
| Network Partition | กลาง (มีแน่นอนสักครั้ง) | Majority quorum + fencing token |
→ ระบบจริงต้องเตรียมรับมือ ทุก failure mode (ยกเว้น Byzantine ในส่วนใหญ่)
7. Two Generals Problem (ปัญหานายพลสองนาย) — ทำไม "agreement 100%" เป็นไปไม่ได้
ปัญหานี้เป็นโจทย์คลาสสิกของ CS ที่ใช้เปรียบเทียบสถานการณ์ของ network — สำคัญมากเพราะอธิบายว่า "ทำไมเราต้องอยู่กับความไม่แน่นอน" — ไม่ใช่เพราะเราโง่ แต่เพราะ math บอกว่า impossible
เรื่องราว — สงครามสองนายพล
ลองนึกภาพสถานการณ์ในสงครามโบราณ:
นายพลทั้งสองอยู่คนละด้านของเมืองศัตรู ตกลงกันว่าจะโจมตี พร้อมกัน — ถ้าโจมตีไม่พร้อม คนที่โจมตีก่อนคนเดียว = แพ้แน่นอน
ติดต่อกันได้ทางเดียวคือ ส่งทหารส่งสารผ่านหุบเขา — ทหารคนนี้มีความเสี่ยง 3 อย่าง:
- ถูกศัตรูดักจับระหว่างทาง
- หลงทาง
- เสียชีวิต
ลองออกแบบ Protocol ให้สองนายพลตกลงกัน
Protocol ที่ 1 — ส่ง message แบบ one-way
A → ส่งทหารบอก B: "โจมตี 06:00 น."
ปัญหา: A ไม่รู้ว่า B ได้รับ message หรือเปล่า — ถ้าโจมตีคนเดียวก็แพ้!
Protocol ที่ 2 — เพิ่ม ACK (acknowledge)
A → B: "โจมตี 06:00"
B → A: "OK รับทราบ" (ACK)
A ได้ ACK → เริ่มโจมตี
ปัญหา: B ส่ง ACK ไปแล้วแต่ ไม่รู้ว่า A ได้รับ ACK หรือเปล่า — B กลัว A ไม่ได้ ACK แล้วไม่โจมตี ทำให้ B โจมตีคนเดียว = แพ้
Protocol ที่ 3 — เพิ่ม ACK ของ ACK
A → B: "โจมตี 06:00"
B → A: ACK
A → B: ACK of ACK (บอก B ว่า "ฉันได้ ACK ของคุณแล้ว")
ปัญหา: A ไม่รู้ว่า B ได้รับ ACK-of-ACK หรือเปล่า — A ก็ยังไม่กล้าโจมตี
Infinite recursion! ไม่มีจำนวนข้อความที่ "เพียงพอ" ที่จะทำให้ทั้งสองฝ่ายมั่นใจ 100% ว่าฝ่ายตรงข้ามก็มั่นใจเช่นกัน
ข้อสรุปทางคณิตศาสตร์ (formalized โดย Akkoyunlu, Ekanadham, Huber ปี 1975 ในงาน "Some constraints and tradeoffs in the design of network communications")
ในระบบที่ communication channel "อาจล้มเหลว" — ไม่มี deterministic protocol ที่ทำให้ทั้งสองฝ่าย "agree" ได้ 100%
ความหมายต่อระบบ distributed จริง
ทุกครั้งที่เราส่ง network request:
- เราไม่รู้แน่นอน ว่า request ถึงปลายทางไหม
- เราไม่รู้แน่นอน ว่าฝ่ายตรงข้ามรู้หรือเปล่าว่าเราส่ง
- ตามทฤษฎี = ไม่มีทางรู้ 100%
ในความเป็นจริง — เราใช้ทางออกแบบ "ประนีประนอม" (compromise) ดังนี้:
- ✅ Timeout — รอแค่ X วินาที ถ้าไม่ได้คำตอบ = สมมติว่าไม่ถึง
- ✅ Retry — ลองส่งใหม่อีก (อาจจะถึงในรอบนี้)
- ✅ Idempotency — ถ้าฝ่ายโน้นได้รับซ้ำ ก็ไม่มีผลข้างเคียง
- ✅ Eventually agree — ยอมรับว่าจะมีจังหวะไม่ตรงกัน แล้ว reconcile ทีหลัง
- ✅ Probabilistic — ถ้าได้ ACK 99% ของเวลา → ก็เพียงพอแล้ว
บทเรียนสำคัญ: distributed system ไม่ใช่เรื่อง "ทำให้สมบูรณ์ 100%" — แต่เป็นเรื่อง "อยู่กับความไม่แน่นอนอย่างไรให้ดีที่สุด"
8. Consistency Models — เกริ่นนำ (Preview)
หัวข้อนี้คือหัวใจของบทที่ 1 — ตรงนี้จะแค่แตะ "ทำไมมันถึงเป็นปัญหา" และ list ของ model ที่ใช้กันให้รู้จักก่อน
ทำไม consistency ถึงเป็นปัญหา?
ในระบบ distributed:
- เก็บข้อมูลไว้ใน replica หลายตัว เพื่อ availability และ scale
- เมื่อ write ที่ replica A → ใช้เวลา propagate ไป B กับ C
- ช่วงเวลาที่ data ยังไม่ sync = ระบบอยู่ในสถานะ inconsistent
ตัวอย่าง:
ผู้ใช้ post รูปที่ server A → 100 ms ต่อมา เพื่อนเปิดดูที่ server B → ไม่เห็นรูป เพราะข้อมูลยังไม่ถึง B
Consistency Models ต่าง ๆ — เรียงจาก strict ไปหลวม
- Linearizable (ลิ-เนีย-ไรซ์-อะ-เบิล = เห็นทันทีแบบ single machine) — strict ที่สุด ทุก read เห็นค่าล่าสุดทันที (เหมือนระบบ single-machine)
- Sequential (ซี-เควน-เชิล = ตามลำดับ) — ทุก node เห็น operation ในลำดับเดียวกัน แต่ไม่จำเป็นต้อง "เรียลไทม์"
- Causal (คอ-สัล = ตามเหตุ-ผล) — operation ที่มีความสัมพันธ์ "เหตุ-ผล" ต้องอยู่ในลำดับเดียวกัน
- Read-Your-Writes (อ่านเห็นที่เขียนเอง) — ผู้ใช้ที่เขียนต้องอ่านเห็นของตัวเองอย่างน้อย
- Eventual (อี-เวน-ช่วล = สุดท้ายจะตรง) — หลวมที่สุด รับประกันแค่ว่า "ในที่สุดจะ sync กัน"
แต่ละ model เป็น trade-off ระหว่าง correctness (ความถูกต้อง) กับ speed (ความเร็ว) กับ availability (พร้อมใช้งาน) — ไม่มีตัวที่ "ดีที่สุด" สำหรับทุกระบบ
CAP กับ PACELC — สรุปสั้น ๆ
ก่อนเข้าตัวสรุป — มาแยกตัวย่อ 4 ตัวที่จะเจอบ่อยในบทถัดไป:
- CAP (อ่าน แคป) — ตัวย่อ Consistency / Availability / Partition tolerance
- PACELC (อ่าน แพค-เอลซี่) — ตัวย่อ Partition → A vs C, Else → Latency vs Consistency
- ACID (อ่าน แอซ-ซิด = กรด) — Atomicity / Consistency / Isolation / Durability (ของ traditional DB)
- BASE (อ่าน เบส) — Basically Available / Soft state / Eventually consistent (ของ NoSQL)
สรุป CAP/PACELC:
- CAP Theorem: เลือกได้แค่ 2 จาก 3 — Consistency, Availability, Partition tolerance
- PACELC (ส่วนต่อจาก CAP): ถ้าเกิด Partition → ต้องเลือกระหว่าง A กับ C มิฉะนั้น Else → ต้องเลือกระหว่าง Latency กับ Consistency
ดู บทที่ 1 — Consistency + CAP สำหรับรายละเอียดเต็ม + ตัวอย่างประกอบ (worked examples)
9. Common Distributed Patterns — Pattern ที่เจอบ่อย
list ของ pattern หลักที่จะเจอในบทถัดไป — ตอนนี้แค่ "รู้ว่ามี":
| Pattern | ใช้แก้ปัญหาอะไร | บทไหน |
|---|---|---|
| Replication | data ปลอดภัย + scale read | บท 1 |
| Sharding | scale write + storage | บท 1 |
| Consensus | N nodes agree | บท 2 |
| Leader Election | 1 node coordinates | บท 2 |
| Heartbeat | detect failure | บท 0 |
| Gossip | spread info ใน cluster | บท 6 |
| Quorum | majority decides | บท 1 |
| WAL (Write-Ahead Log) | crash recovery | บท 1, 2 |
| 2PC | atomic across DBs | บท 3 |
| Saga | distributed transaction | บท 3 |
| Event Sourcing | events not state | บท 3, 5 |
| Outbox | atomic DB + event | บท 3, 5 |
| Lamport / Vector Clock | logical ordering | บท 4 |
| CRDT | conflict-free replication | บท 1, 4, 6 |
| Circuit Breaker | prevent cascade | บท 7 |
→ บทต่อ ๆ ไปจะลึกแต่ละ pattern
10. Common Issues — ปัญหาที่ distributed system เจอบ่อย
ปัญหา 5 ตัวนี้ — ทุกคนที่ทำ distributed มาสักพักจะได้เจอ ถ้ารู้จัก pattern → ป้องกันได้ก่อนเกิด
Split Brain — "เมื่อ cluster แตกเป็น 2 ส่วน ทั้งคู่คิดว่าตัวเองถูก"
ปัญหา:
text
Cluster 3 nodes: A, B, C
B กับ C ขาดการสื่อสารจาก A (network partition)
A side (minority):
- คิดว่า B กับ C ตาย
- เลือก leader ใหม่ (ตัวเอง)
- รับ writes
B + C side (majority):
- คิดว่า A ตาย
- เลือก leader ใหม่ (B หรือ C)
- รับ writes ด้วย
ตอนนี้ = 2 leaders ทำงานพร้อมกัน → เขียน data ขัดกัน
พอ network heal → conflicts!ทำไมอันตราย?
text
ลูกค้าโอนเงิน 1000 บาท ที่ A
ลูกค้าซื้อของ 800 บาท ที่ B (พร้อมกัน)
ทั้ง 2 ฝั่งคิดว่าตัวเอง = leader → process ทั้งคู่
→ ยอดเงินคำนวณผิด (อาจติดลบ หรือซ้ำซ้อน)ก่อน Solutions — รู้จักคำเหล่านี้ก่อน (จะลึกในบทถัดไป):
- Quorum = ต้องมีเสียงข้างมาก (majority, > N/2) ถึงตัดสินใจได้ — ดูเต็มในบทที่ 1
- Fencing token = ตัวเลขที่เพิ่มขึ้นเรื่อย ๆ (monotonic counter) ใช้ปฏิเสธ leader เก่าที่ฟื้นกลับมา — ดูเต็มในบทที่ 2
- Watchdog timeout = ตัวจับเวลาที่ถ้าไม่ได้รับ "OK" ภายในเวลาที่กำหนด → ทำ action บางอย่าง (เช่น step down)
Solutions:
- ✅ Quorum: ต้องมี majority (N/2 + 1) ถึงเป็น leader ได้
- N=3 ต้องมี 2 ตัว → A side มีตัวเดียว = ไม่เป็น leader = หยุดรับ write
- B+C side มี 2 ตัว = เป็น leader ได้ = continue
- ✅ Fencing token: monotonic counter — old leader ที่ฟื้นมาจะถูก reject (token เก่า)
- ✅ Watchdog timeout: ถ้าขาด majority นานเกิน → step down เป็น follower
→ Standard solution = quorum (ดูบท 1 + 2)
Cascade Failure — "ล้มเป็น domino"
ปัญหา:
text
Service A → Service B → Service C → DB
DB ช้า (slow query):
1. Service C: query block 30s → thread pool เต็ม
2. Service B: เรียก C ก็ block → thread pool เต็ม
3. Service A: เรียก B ก็ block → thread pool เต็ม
4. User: เห็น API ค้าง → กดซ้ำ → request เพิ่มขึ้น → service ตายเร็วขึ้น
→ Cascade: ปัญหาเล็กที่ DB ทำให้ทั้งระบบล่มReal example: Slack 2021 outage — Vitess (MySQL proxy) CPU เต็ม → ทุก service ค้าง 5 ชั่วโมง
Solutions:
- ✅ Circuit Breaker: ถ้า C fail บ่อย → B "ตัดวงจร" ไม่เรียก C ชั่วคราว → fail fast
- ✅ Bulkhead: แยก thread pool ตาม dependency (เหมือนเรือมีกั้นห้อง)
- ✅ Timeout: ทุก network call ต้องมี timeout (เช่น 5 sec) ไม่ใช่ block ไปเรื่อยๆ
- ✅ Graceful degradation: ถ้า C ตาย → B ใช้ cached data หรือ default value
→ ดู บท 7 สำหรับ Circuit Breaker pattern
Thundering Herd — "ฝูงม้าวิ่งพร้อมกัน"
ปัญหา:
text
Scenario 1 — Server มาแล้วล้มอีกครั้ง:
1. Service down → ทุก client retry (10,000 requests/sec)
2. Service กลับมา → 10,000 hit ทันที
3. Service โหลดเกิน → down อีก
4. Loop ไม่จบ
Scenario 2 — Cron job พร้อมกัน:
ทุก server มี cron @ 00:00 → 1000 servers query DB ตอนเที่ยงคืน → DB overloadSolutions:
- ✅ Exponential backoff + jitter: retry ที่ 1s, 2s, 4s, 8s... + random 0-1s
- Jitter สำคัญ! ถ้าไม่มี jitter → ทุก client retry พร้อมกัน
- ✅ Rate limiting: server จำกัด max requests/sec — กระจาย load ตามเวลา
- ✅ Slow start: หลัง restart → ค่อย ๆ รับ traffic เพิ่ม (10% → 50% → 100%)
- ✅ Token bucket: client มี "token" จำกัด → forced spacing
คำอธิบายเป็น step ก่อนอ่านโค้ด TypeScript:
- ให้
retryเป็นจำนวนครั้งที่เคย retry ไปแล้ว (0, 1, 2, ...) - คำนวณ
base=1000 × 2^retry(ms) — แบบ exponential แต่ cap ไว้ที่ 60,000 ms (60 วินาที) - คืนค่า random ในช่วง [0, base] — กระจาย retry ของแต่ละ client ให้ไม่ตรงกัน
- ตัวอย่าง: retry=0 → base=1s, retry=3 → base=8s, retry=10 → base=60s (cap)
(เกร็ดศัพท์ TypeScript: Math.pow(2, retry) = 2 ยกกำลัง retry, Math.random() = สุ่ม 0-1, Math.min(a, b) = เอาตัวที่น้อยกว่า)
typescript
// ตัวอย่าง exponential backoff with FULL jitter
// (Marc Brooker, AWS Architecture Blog 2015 — "Exponential Backoff And Jitter")
function backoff(retry: number): number {
const cap = 60_000; // cap at 60s
const base = Math.min(cap, 1000 * Math.pow(2, retry)); // exponential ceiling
return Math.random() * base; // full jitter (สุ่มเต็มช่วง): random ใน [0, base]
}
// ทางเลือก: decorrelated jitter (สุ่มแบบไม่ผูกกับครั้งก่อน, เหมาะกับ load ที่ steady ต่อเนื่อง)
let sleep = 1000;
function decorrelatedJitter(): number {
sleep = Math.min(60_000, Math.random() * sleep * 3);
return sleep;
}⚠️ อย่าเขียนแบบ
base + small_randomผิดบ่อย — ถ้า jitter แค่ 0-1s บน base 60s ทุก client ยังพร้อมกันใน window ใกล้ๆ กัน → thundering herd อยู่ดี Full jitter (random ใน [0, base]) กระจาย retry เต็มช่วง — เป็น default ที่แนะนำสำหรับ idempotent retry ทั่วไป
Stampeding Herd (Cache) — "ฝูงม้าตอนวัวขาดน้ำ"
💡 ต่างจาก Thundering Herd ยังไง?
- Thundering Herd: ตัวกระตุ้น = service ฟื้นกลับมา / cron พร้อมกัน → ทุก client ยิง request พร้อมกัน
- Stampeding Herd: ตัวกระตุ้น = cache key หมดอายุพร้อมกัน → ทุก client เห็น cache miss พร้อมกัน → ยิง DB
- ทั้งคู่เป็น "ฝูงม้าวิ่งพร้อมกัน" แต่สาเหตุต่างกัน, solution ก็ต่างกัน (อันแรกใช้ jitter + slow start, อันหลังใช้ XFetch + single-flight)
ปัญหา:
text
Popular cache key (เช่น "front-page-hot-products") TTL หมด @ 12:00:00
ในวินาทีนั้น 10,000 concurrent requests เห็น miss พร้อมกัน
→ 10,000 query DB พร้อมกัน
→ DB overload → ช้า → request ค้าง → cascadeSolutions:
- ✅ Probabilistic early expiry (XFetch): ก่อน TTL หมด → บาง request "อาสา" refresh
- ✅ Single-flight: 1 client ดึง DB, อื่นๆ wait → 1 query เท่านั้น
- ✅ Stale-while-revalidate: serve stale + refresh ใน background
→ ดู บท 8 — Cache Stampede ลึก
Slow Network — "ช้า ๆ ที่ทำให้ทุกอย่างพัง"
ปัญหา:
text
Network ปกติ p50=1ms, p99=5ms
วันแย่: p99 พุ่งเป็น 500ms (10x ช้ากว่าปกติ)
→ Request ปลายแถว (1%) ใช้เวลา 500ms
→ Thread pool ค่อย ๆ เต็ม (เพราะ thread ไม่กลับมา)
→ ในที่สุดทุก request ตันDetection:
- 📈 ดู tail latency (p99, p999) — ไม่ใช่ average
- 📈 Alert ถ้า p99 > 2x baseline
- 📈 ใช้ distributed tracing เพื่อหา bottleneck
Mitigation:
- ✅ Hedge requests: ส่ง request ไป 2-3 servers พร้อมกัน → ใช้ตัวที่ตอบเร็วสุด
- ✅ Speculative execution: ถ้า request ใช้เวลา > p95 → ส่งซ้ำไปอีก server (Google search ใช้)
- ✅ Aggressive timeout: ไม่ปล่อยให้ค้าง — ตัดหลัง p99 + 50%
11. Networking Basics (Recap) — พื้นฐาน network ที่ต้องรู้
ทำ distributed system โดยไม่เข้าใจ network = เหมือนขับรถโดยไม่รู้กฎจราจร เจอปัญหาแน่ๆ หัวข้อนี้ทบทวนพื้นฐานที่จะเจอเรื่อยๆ ในบทถัดไป
TCP/IP Stack — "บันไดของการสื่อสาร"
เวลาส่ง HTTP request — ข้อมูลเดินผ่าน 7 layers (model OSI = Open Systems Interconnection — โมเดลอ้างอิงเครือข่าย) ก่อนเป็น packet จริง ๆ
💡 ไม่ต้องท่อง 7 layers แบบ exam — รู้แค่ว่าหลัก ๆ มี 3 ชั้น: Application (L7) / Transport (L4) / Network (L3) ที่เหลือเป็น mental model สำหรับ debug ("ปัญหาอยู่ layer ไหน?")
text
Layer 7: Application — สิ่งที่เราเขียน (HTTP, gRPC, WebSocket)
Layer 6: Presentation — TLS encryption (HTTPS = HTTP + TLS)
Layer 5: Session — Session management
Layer 4: Transport — TCP / UDP (เลือกความน่าเชื่อถือ)
Layer 3: Network — IP routing (เส้นทางผ่าน internet)
Layer 2: Data Link — Ethernet, WiFi (LAN protocol)
Layer 1: Physical — สายเคเบิล, radio wavesAnalogy: ส่งจดหมาย
- L7 (Application): เนื้อจดหมายภาษาไทย
- L6 (TLS): เอาจดหมายใส่ซองล็อค
- L4 (TCP): เซ็นรับรองว่าส่งครบ ถ้าหายส่งซ้ำ
- L3 (IP): ที่อยู่ผู้รับ
- L1 (Physical): รถส่งของ
→ ทำไมต้องรู้? Debug network issue → "layer ไหนพัง?"
TCP vs UDP — เลือกตาม use case
| TCP | UDP | |
|---|---|---|
| ความน่าเชื่อถือ | ✅ Retransmit lost packets | ❌ ส่งแล้วลืม |
| Ordering | ✅ Ordered | ❌ มาถึงตามลำดับใดก็ได้ |
| Connection | 3-way handshake | Connectionless |
| Latency | สูงกว่า | ต่ำกว่า (no handshake) |
| Overhead | สูง | ต่ำ |
ใช้ TCP เมื่อ:
- ต้องการความถูกต้อง 100% (HTTP, gRPC, SSH, FTP)
- File transfer
- Database connection
ใช้ UDP เมื่อ:
- Real-time (loss OK > delay): video call, gaming, voice
- Broadcast (DNS, DHCP)
- Stateless (DNS queries)
- QUIC (HTTP/3 base) — UDP + reliable layer ใน application
Trade-off ตัวอย่าง:
text
Video call:
- ใช้ TCP: ถ้าเสีย packet 1 frame → wait retransmit → frame ถัดไปติด
- ใช้ UDP: drop frame นั้น → frame ถัดไปเล่นเลย → ดีกว่า
File download:
- ใช้ TCP: เสีย byte 1 → retransmit → file ครบ ✅
- ใช้ UDP: เสีย byte 1 → file corrupt ❌HTTP/1 vs HTTP/2 vs HTTP/3 — Evolution ของ HTTP
HTTP/1.1 (1997):
text
- 1 request ต่อ connection ต่อเวลา (head-of-line blocking)
- ต้อง open multiple connections เพื่อ parallel
- Plain text headers (ใหญ่)
Workaround เก่า: browser open 6-8 connections per domain
ปัญหา: connection overhead, slow startHTTP/2 (2015):
text
- Multiplexing — multiple requests ใน 1 connection (parallel!)
- Header compression (HPACK) — ลดขนาดได้ 80%
- Server Push — server ส่ง resources โดยไม่รอ request ⚠️ deprecated ดูหมายเหตุ
- Binary protocol (เร็วกว่า parse text)
ผลลัพธ์: เร็วกว่า HTTP/1.1 ~30-50% สำหรับ web pages⚠️ HTTP/2 Server Push deprecated แล้ว (2022):
- Chrome 106 (Sep 2022) ปิด Server Push by default; Firefox ลบ support 2024
- เหตุผล: real-world data ชี้ว่า hit rate ต่ำ + มักส่ง resource ที่ browser มี cached อยู่แล้ว
- ปัจจุบันใช้
103 Early Hints(RFC 8297 = มาตรฐาน HTTP ออกปี 2017) แทน — server ส่ง preliminary response (สถานะ 103) บอก browser ให้ preload resources ที่จะตามมา ก่อน final response (200) จะเสร็จ- Cloudflare, Fastly, Vercel, Akamai รองรับ 103 Early Hints ตั้งแต่ 2022
HTTP/3 (2022):
text
- ใช้ QUIC (UDP-based) แทน TCP
- ทำไมดีกว่า?
- TCP head-of-line blocking ที่ transport layer
(เสีย 1 packet → block ทุก stream)
- QUIC: แต่ละ stream อิสระจริงๆ
- Faster handshake: 0-RTT (รู้จัก server แล้ว = no handshake)
- Better connection migration (มือถือเปลี่ยน WiFi → ไม่ต้อง reconnect)
ใช้ใน: Google services, Cloudflare, Facebook (default HTTP/3 since 2023)Latency comparison (cold start):
text
HTTP/1.1: DNS + TCP handshake + TLS handshake + request
= ~3-4 RTTs
HTTP/2: DNS + TCP + TLS (1.3) + request
= ~2-3 RTTs
HTTP/3: DNS + QUIC handshake (combines TCP + TLS)
= ~1 RTT (or 0-RTT for resumption)→ Modern best practice: HTTPS + HTTP/2 ขึ้นไป + connection pooling
12. Network Latency Numbers — ตัวเลข latency ที่ควรจำ
วิศวกรระบบกระจายทุกคนควรจำ "ลำดับขนาด" ของ latency แต่ละชั้นไว้ในหัว เพราะมันคือพื้นฐานของการตัดสินใจออกแบบ — การข้าม network ช้ากว่าอ่าน RAM เป็นแสนเท่า และข้ามทวีปช้ากว่าใน datacenter เดียวกันหลายร้อยเท่า ไม่ต้องจำเป๊ะ แต่ให้รู้ว่าอะไรช้ากว่าอะไรกี่เท่า:
💡 Cheat sheet หน่วยเวลา (ทบทวน): 1 ms = 1,000 μs = 1,000,000 ns (ms = millisecond / μs = microsecond / ns = nanosecond)
text
Jeff Dean numbers (อ้างจาก Jeff Dean 2012 + อัปเดต 2024-2026)
หน่วยความจำ + ดิสก์ในเครื่อง (intra-machine):
L1 cache: 0.5 ns
L2 cache: 7 ns
RAM: 100 ns
NVMe SSD random read (4 KB): ~10-100 μs (2026, แทน SATA SSD ~150 μs ของ Jeff Dean 2012)
HDD seek: 10 ms (legacy spinning disk)
เครือข่าย (network):
Network (1 Gbps, 1 KB): 10 μs
Network (1 Gbps, 1 MB): 10 ms
Network (intra-DC RTT): ~0.5 ms (2026)
Network (same region): 1-5 ms
Network (cross-region/continent): 100-200 ms (2026 — เทียบ Jeff Dean 2012 ที่ใช้ ~150 ms)
Network (geosync satellite): 600 ms ← ดาวเทียมค้างฟ้า→ จำแค่ "ลำดับขนาด" (order of magnitude) พอ — ไม่ต้องจำตัวเลขเป๊ะ; ตัวเลข Jeff Dean 2012 ต้นฉบับยังมีประโยชน์เป็น mental reference แต่ device รุ่นใหม่ (NVMe SSD, modern DC backbone) เร็วขึ้นมาก
Useful Mental Anchors — ตัวเลขที่ควรท่องจำ
text
1 ns vs 1 sec = เทียบเป็น 1 วินาที vs ~32 ปี (10^9 = 31.7 ปี)
1 ms vs 1 sec = เทียบเป็น 1 วินาที vs ~16.7 นาที (10^3 = 1000 sec)
100 ms = ช้าจนคนเริ่ม "รู้สึก" ว่าหน่วง
1 sec = "หน้าเว็บเสียหรือเปล่า?" (ผู้ใช้เริ่มจะปิดหนี)
US ↔ Europe analogy (สำหรับ mental anchor):
- light speed in fiber = ~5 ms ต่อ 1000 km
- ระยะ NYC → London ~5,500 km → ขั้นต่ำที่ฟิสิกส์ยอม ~28 ms one-way หรือ ~55 ms RTT
- ถ้าเทียบ "1 ns scale = 1 sec scale" → cross-Atlantic 55 ms ≈ ~1.7 ปีในชีวิตคน
- ถ้าเทียบ "1 ms = 1 sec" scale → ~9.5 นาที (ไม่ใช่ 14 ปีตามที่บางตำราเขียนผิด)
HDD seek (10 ms) analogy:
- ถ้า L1 cache = 1 วินาที → HDD seek ≈ ~7.7 เดือน (10 ms / 0.5 ns = 2 × 10^7 เท่า)
ความหมาย (Implication):
- CPU vs RAM: เหมือนความเร็ว "คิดในหัว" เทียบกัน
- RAM vs network: เหมือน "ถามเพื่อนข้าง ๆ" vs "ส่งจดหมาย"
- Local network vs cross-region: เหมือน "คุยกันที่ร้านกาแฟ" vs "โทรข้ามประเทศ"→ สรุป (takeaway): request ข้าม region ไม่เคย เป็นแค่ "โทรเพิ่มอีกครั้งเฉย ๆ"
- เทียบกับ RAM (100 ns): cross-region 150 ms = ช้ากว่า ~1,500,000 เท่า (100,000–1,000,000x ลำดับขนาด)
- เทียบกับ same-DC (~0.5-1 ms): cross-region ช้ากว่า ~100-300 เท่า
- ดังนั้น cache + co-location ภายใน region เดียวกัน = พื้นฐานของระบบที่เร็ว
13. Networking Deep Dive (สำหรับมือใหม่)
หัวข้อนี้สำคัญแต่หลายคน assume เข้าใจ — มาทบทวนสั้นๆ
TLS Handshake (HTTPS) — การจับมือก่อนคุย encrypted
💡 RTT = Round-Trip Time = เวลาวิ่งไป-กลับ 1 รอบ (เช่น Bangkok → Singapore → Bangkok) — เป็นหน่วยที่ใช้ตลอด section นี้
Cost: ~2 RTTs before data flows (แปล: ต้องเสียเวลาวิ่งไป-กลับ ~2 รอบ ก่อนข้อมูลจริงจะเริ่มไหล)
Implications (ผลที่ตามมา):
- New connections = expensive — เปิด connection ใหม่ทุกครั้ง = แพง (เสีย handshake + slow start)
- Connection pooling (keep-alive) = critical — การใช้ connection ซ้ำสำคัญมากต่อ performance
- HTTPS-everywhere แล้วก็ยังเร็วได้ถ้า reuse connection
DNS Resolution — แปลชื่อโดเมน → IP address
text
"api.example.com มี IP อะไร?"
Client → Local resolver (cache ของ OS, ~0ms ถ้ามีใน cache)
→ ISP resolver (~1-10ms, มักมี cache)
→ Root → TLD → Authoritative servers (ถ้าไม่มี cache เลย ~100ms)
Cache TTL: 60 วินาที ถึงหลายวัน (TTL = อายุของ cache)
Stale cache: บาง resolver ยังตอบ IP เก่าหลัง TTL หมด ถ้า origin ล่ม
ข้อผิดพลาดที่พบบ่อย:
- ตั้ง TTL สั้น "เพื่อความยืดหยุ่น" แต่ app ดัน cache ไว้นานกว่า
- app ถือ IP เก่าไว้ ไม่ทำตาม TTL
- ผลคือ: failover ไม่ทำงาน (ยังวิ่งไปหา server เก่าที่ตายแล้ว)→ DNS เปลี่ยน IP ไม่ทันที ถึงปลายทางทุกที่ — ต้องวางแผน load balancer / health check
Connection Pooling — ใช้ connection ซ้ำให้คุ้มค่า
text
ไม่มี pool (เปิด-ปิด connection ทุกครั้ง):
แต่ละ request:
TCP handshake (1 RTT)
+ TLS handshake (1-2 RTT)
+ request
+ close
= 3-4 RTTs ต่อ 1 request
มี pool (keep-alive — ใช้ connection ซ้ำ):
request แรก: 3-4 RTTs (ตอน setup)
request ถัดไป: 1 RTT (ส่งแค่ข้อมูล)
ได้ performance เพิ่ม 3-4 เท่า แบบฟรี ๆ
วิธีตั้งค่า:
- HTTP client: เปิด keep-alive (lib สมัยใหม่เปิดให้อยู่แล้ว)
- Pool size: ~ จำนวน request ที่ทำพร้อมกัน
- Idle timeout: ตั้งให้ < timeout ฝั่ง server (กัน connection ค้างใช้ไม่ได้)
- Max lifetime: หมุน connection ใหม่เรื่อย ๆ เพื่อ refresh DNS + กระจายโหลด LBHTTP/REST vs gRPC vs GraphQL — เปรียบเทียบ protocol
text
HTTP/REST (JSON ผ่าน HTTP/1.1 หรือ 2):
✅ Universal — ใช้ได้ทุกที่
✅ Cacheable — cache ได้ตาม semantic ของ HTTP
✅ Human-readable — คนอ่านออก ดีบักง่าย
❌ Verbose — JSON เปลืองพื้นที่
❌ No schema enforcement — ไม่บังคับ schema
ใช้กับ: public API, แอปบน browser, การต่อระบบแบบง่าย ๆ
gRPC (Protobuf ผ่าน HTTP/2):
✅ Binary — เล็กและเร็วกว่า
✅ Strong schema — มี schema ชัดเจน (Protobuf)
✅ Streaming — สื่อสารสองทางได้
✅ Built-in code gen — generate code ให้อัตโนมัติ
❌ Browser ต้องมี proxy ถึงใช้ได้
❌ ดีบักยาก (เป็น binary คนอ่านไม่ออก)
ใช้กับ: การเรียกกันภายในระหว่าง service, ทีมที่ใช้หลายภาษา
GraphQL:
✅ Client เลือก field ที่อยากได้เอง (กัน over-fetching = ดึงข้อมูลเกิน)
✅ Single endpoint — มี endpoint เดียว
✅ Strong schema — มี schema ชัดเจน
❌ Caching ซับซ้อน (ไม่ได้อิงตาม URL)
❌ เสี่ยง N+1 query (1 query กลายเป็น N query ตาม — ต้องใช้ DataLoader)
❌ ฝั่ง server ซับซ้อนขึ้น
ใช้กับ: frontend ซับซ้อนที่ต้องการข้อมูลหลากหลาย, BFF layerBFF (Backend for Frontend) = backend layer ที่ปรับแต่งข้อมูลให้เหมาะกับ frontend แต่ละแบบ (ดู glossary บท 11)
→ Heuristic (กฎคร่าว ๆ): REST สำหรับภายนอก/browser, gRPC สำหรับภายใน, GraphQL สำหรับ BFF
NTP Internals — ทำไมนาฬิกาแกว่ง
text
NTP แก้นาฬิกาให้ตรงด้วย 2 วิธี:
1. Slewing (ค่อย ๆ ปรับ): เร่ง / หน่วงนาฬิกาทีละนิด
- Smooth — ไม่กระโดด
- เร็วสุด ~500 ppm (กรณีแย่สุด 1 วินาที ต่อ 33 นาที)
- เป็น mode default ใน Linux
2. Stepping (กระโดด): ตั้งเวลาใหม่ทันที
- เร็ว
- เสี่ยง: เวลาเดิน "ถอยหลัง" (event เสียลำดับ!)
- ทำงานเมื่อ drift > 128 ms (ตั้งค่าได้)
bug ที่เกิดจริง:
- App log ว่า "Event at 10:00:05"
- นาฬิกากระโดดถอยหลังไป 10:00:03
- event ถัดไป "10:00:04" → ดันมาอยู่ "ก่อน" event 10:00:05!
- query ที่อิงเวลา = ได้ผลผิด
วิธีเลี่ยง (Mitigation):
- ใช้ monotonic clock (= นาฬิกาที่เดินหน้าอย่างเดียว ไม่ถอยหลัง — เหมาะวัด duration) สำหรับเรื่องลำดับ
- ใช้ ULID หรือ Snowflake สำหรับสร้าง unique ID
- **ULID** (อ่าน ยู-ลิด = Universally Unique Lexicographically Sortable Identifier) — ID ที่ sort ตามเวลาได้
- **Snowflake** = ID schema ของ Twitter ที่รวม timestamp + machine ID + counter เข้าด้วยกัน
- อย่าเชื่อ wall clock (= นาฬิกาแขวนผนัง = เวลานาฬิกาปกติที่อาจเดินถอยหลังได้) ในการตัดสิน "อะไรเกิดก่อน"
leap second smearing ของ Google:
- การเพิ่ม leap second 1 วินาที / ไม่กี่ปี → ระบบที่ไม่ระวังจะ crash
- Google: เกลี่ยทีละนิดตลอด 24 ชั่วโมง (แต่ละวินาทียาวขึ้นนิดเดียว)
- AWS: ใช้วิธีคล้ายกัน14. Why Things Fail in Distributed — สาเหตุที่ระบบล่ม
ก่อนออกแบบให้ทนทาน — ต้องรู้ก่อนว่า "ล่มเพราะอะไร" สาเหตุแบ่งเป็น 5 หมวด แต่ละหมวดต้องการ mitigation ต่างกัน
1. Hardware Failure — ฮาร์ดแวร์พัง (5-10% ต่อปี)
text
ใน data center ขนาด 10,000 servers:
- 500-1000 servers fail per year
- เฉลี่ย 1-3 servers fail ต่อวัน
ประเภท hardware ที่พังบ่อย:
- Disk (HDD: 2-4% AFR, SSD: 0.5-1% AFR)
- RAM (silent bit flips — ECC RAM ช่วย)
- NIC (network card)
- Power supply
- Motherboard (rare)Mitigation:
- ✅ Replication: เก็บ data ใน ≥ 3 servers
- ✅ Hot spare: server สำรองที่พร้อมแทนที่
- ✅ RAID for disk (แต่ aware ของ RAID's own pitfalls)
- ✅ ECC RAM (รุ่น server)
2. Software Bug — บั๊กในซอฟต์แวร์
text
ส่วนใหญ่ที่เจอใน production:
- Memory leak → OOM kill ใน Linux
- Race condition (concurrency bugs ที่ทดสอบยาก)
- Deadlock (lock A waits B, B waits A)
- Resource exhaustion (file descriptors, connections, threads)
- Infinite loop (regex backtracking — ดู Cloudflare 2019 outage)Mitigation:
- ✅ Code review + automated testing
- ✅ Canary deployment (เริ่ม 5% ของ traffic แล้วค่อย ramp up)
- ✅ Blue-green deployment (quick rollback)
- ✅ Memory limits + auto-restart
- ✅ Chaos engineering (find bugs before they find you)
3. Network Failure — เครือข่ายล้มเหลว
text
สาเหตุที่ network พัง:
- Cable cut (เกิดได้จริง! รถขุดทาง, ไฟไหม้ใต้ดิน)
- Switch misconfig (พบบ่อยในการ deploy network changes)
- DDoS attack
- BGP routing issue (เคยทำให้ Facebook ล่ม 6 ชม. ในปี 2021)
- DNS failure (เคย DDoS Dyn DNS ทำ Twitter/Spotify/Reddit ล่ม 2016)
- Submarine cable break (ต่อ Asia-US-Europe)Mitigation:
- ✅ Multi-region (network ใน region อื่นยังใช้ได้)
- ✅ Multi-cloud (ถ้า AWS down → ใช้ GCP)
- ✅ DDoS protection (Cloudflare, AWS Shield)
- ✅ Multi-CDN strategy
- ✅ Retry with backoff + jitter
4. Human Error — สาเหตุ #1 ของ outage
text
หลายแหล่งประเมินว่า "ส่วนใหญ่" ของ outages เกิดจาก human error — **Uptime Institute** (องค์กรอิสระที่จัด ranking + report ของ data center มาตั้งแต่ปี 1993, มี Tier Standard ที่ใช้กันทั่วโลก) **Annual Outage Analysis** รายงานเลขในช่วง 40-75% ขึ้นกับนิยาม *(ตัวเลข exact ต้องการ source — flagged 2026-06; ตรวจ Uptime Institute report ฉบับล่าสุดก่อนอ้าง)*
ประเภท:
- Deploy bad code (config wrong, regex bad)
- Database migration ผิด (lose data!)
- Accidental DELETE / UPDATE without WHERE clause
- rm -rf ผิดที่ (อย่าหัวเราะ — เกิดขึ้นจริง!)
- Drop production database (เคยเกิดกับ GitLab 2017)
- Push to wrong branch
- Reveal API keys ใน public repoMitigation:
- ✅ Code review (no direct push to main)
- ✅ Pre-prod environment (dev, staging, prod)
- ✅ Automation (ลด manual steps)
- ✅ Confirmation prompts ("Are you sure?")
- ✅ Backup + tested restore
- ✅ Least privilege (production access limited)
- ✅ Audit log
- ✅ "Blameless postmortem" culture — focus on process, not people
5. External Dependencies — dependency ภายนอก
text
ระบบเรา depend on external — ก็โดน blast radius
- Cloud provider outage (AWS region down)
- Third-party API down (Stripe, Twilio, SendGrid)
- DNS provider down (Cloudflare, Route53 incidents)
- Certificate authority issues (Let's Encrypt OCSP)
- Submarine cable cuts (intercontinental traffic)
- Natural disasters (earthquake, flood, fire)Mitigation:
- ✅ Multi-region failover
- ✅ Don't single-source critical dependencies
- ✅ Cache external API responses
- ✅ Graceful degradation (ถ้า SendGrid down → queue email retry)
- ✅ Status page monitoring of dependencies
บทเรียนรวม
text
"In distributed system, failure is a feature, not a bug."
(แปล: ในระบบกระจาย การล้มเหลวคือ "ฟีเจอร์ปกติของระบบ" ไม่ใช่ "บั๊กที่ต้องกำจัด" — ต้องออกแบบรองรับ)
"Slow is the new down."
(แปล: ช้า = ตายแบบใหม่ — ในระบบกระจาย service ที่ตอบช้ามาก ๆ มีผลเหมือนตาย
เพราะ thread pool เต็ม, cascade ลามทั้งระบบ — ต้องคิด timeout/circuit breaker)
"Things will fail."
(แปล: ของจะพัง — ไม่ใช่ "ถ้า" แต่เป็น "เมื่อ" → ออกแบบเผื่อไว้เลย)
ทุก failure mode จะเกิด eventually ใน production scale.
หน้าที่ของ engineer ไม่ใช่ "ป้องกัน 100%"
แต่เป็น "ลด impact + ฟื้นเร็ว"
Defensive programming mindset:
✅ Assume network fails → timeout + retry
✅ Assume server crashes → replication
✅ Assume disk corrupts → checksums + backup
✅ Assume human errors → automation + review→ สรุป (takeaway): สมมติว่าทุกอย่างจะเกิดขึ้นสักวัน → ออกแบบเผื่อไว้เลย
15. Failure Detection — รู้ได้ยังไงว่า node "ตายแล้ว"
ฟังดูง่าย แต่จริงๆ ยากมาก — เพราะเราแยก "ตาย" กับ "ช้ามาก" ไม่ออก
ปัญหาพื้นฐาน
text
Node A ส่ง request ไป Node B แล้วไม่ได้คำตอบใน 5 วินาที
เป็นไปได้ 3 กรณี:
1. B ตายจริง ๆ → ควรเอาออกจาก cluster
2. B ยังไม่ตาย แค่ทำงานหนัก (CPU 100%, GC pause)
3. Network ช้าระหว่าง A กับ B
ถ้าเราคิดว่า B ตาย ทั้งที่ B ยังไม่ตาย:
→ เอา B ออก แล้วเลือก leader ใหม่
→ ตอนนี้มี 2 leader (B กับ leader ใหม่)
→ Split brain!
ถ้าเราคิดว่า B ยังอยู่ ทั้งที่ B ตายจริง:
→ ส่ง request ไป B เรื่อย ๆ
→ timeout, slow, user ไม่พอใจ
→ Cluster ใช้ไม่ได้→ Failure detection = trade-off ระหว่าง "เร็ว" กับ "แม่นยำ" เลือกเอง
Heartbeat — วิธีพื้นฐานที่ใช้กันทั่วโลก
หลักการ: ให้แต่ละ node ส่งสัญญาณ "ผมยังอยู่" เป็นจังหวะ
Parameters ที่ต้อง tune:
| Parameter | ค่าปกติ | ผลกระทบ |
|---|---|---|
| Heartbeat interval | 1-5 sec | ถี่ = network overhead สูง, ห่าง = ตรวจจับช้า |
| Timeout (no heartbeat) | 3-5x interval (15-25 sec) | สั้น = false positive เยอะ, ยาว = ตรวจจับช้า |
ปัญหาของ binary heartbeat (alive/dead):
text
GC pause ของ Java app: 5-10 วินาที (เกิดได้บน G1 ที่ไม่ tune หรือ JVM รุ่นเก่า pre-JDK 17!)
หมายเหตุ: modern ZGC/Shenandoah/Generational ZGC (JDK 21+) pause sub-millisecond — กรณี 5-10s ที่ใช้ตัวอย่างนี้
เป็น worst case ของ G1 ที่ไม่ tune หรือ heap ใหญ่มาก ๆ ไม่ใช่ baseline ปกติของปี 2026
ถ้า timeout = 5 วินาที → false positive ทุกครั้งที่ GC (ในกรณี G1 untuned)
แก้ยังไง?
- เพิ่ม timeout → ตรวจจับช้า
- ลด GC → ทำได้แค่บางส่วน
- → ใช้ Phi Accrual แทน (ข้างล่าง)Phi Accrual Failure Detector — ความน่าจะเป็น แทน yes/no
แทนที่จะตอบว่า "alive" หรือ "dead" → ตอบเป็น suspicion level (Phi) ซึ่งเป็นตัวเลข
text
Phi = -log10(P(observed delay | node ยังอยู่))
= ค่าบ่งบอกความน่าสงสัยว่า node ตายแล้ว
(formal: ความน่าจะเป็นที่จะเห็น delay ขนาดนี้ "ถ้า node ยังอยู่" — ยิ่งน้อย = ยิ่งน่าสงสัยว่าตาย)
- Phi = 0 → มั่นใจมากว่า node ยังอยู่
- Phi = 1 → ~90% น่าจะตาย
- Phi = 2 → ~99% น่าจะตาย
- Phi = 3 → ~99.9% น่าจะตาย
- Phi = 8 → ~99.999999% น่าจะตาย (เกือบแน่นอน)
หมายเหตุ: Phi ไม่ได้ bound 0-10 — เป็น real number ที่เพิ่มได้เรื่อย ๆ
ตัวเลขข้างบนเป็นการประมาณ; production threshold ที่ใช้จริงต่างกัน:
- Cassandra default threshold = 8
- Akka ใช้ 8-12 ตาม config
ใช้ threshold ที่ต้องการ (เช่น Phi > 8 = ประกาศตาย)ดียังไง?
text
Phi คำนวณจาก:
- ค่าเฉลี่ย + ส่วนเบี่ยงเบนของ heartbeat ที่ผ่านมา
- จัดการ GC pause + network jitter ได้ดีกว่า
ตัวอย่าง:
- ปกติ heartbeat มาทุก 1 วินาที ± 0.1
- วันนี้หายไป 3 วินาที (= 20 sigma จากปกติ) → Phi สูง
- แต่ถ้า heartbeat มาแบบ jittery 0.5-3 วินาทีอยู่แล้ว → 3 วินาทีอาจ normal → Phi ต่ำใช้ใน: Cassandra, Hashicorp Memberlist, Akka
Failure Detector Properties (ทฤษฎี)
text
Strong Completeness (ความครบถ้วน):
"ทุก node ที่ตายจริง จะถูกตรวจจับสุดท้าย"
→ ไม่มี node ตายที่เราคิดว่ายังอยู่ตลอด
Strong Accuracy (ความแม่นยำ):
"ไม่มี false positive — node ที่ยังอยู่ จะไม่ถูกประกาศตาย"
(false positive = ตรวจผิดว่าตายทั้งที่ยังอยู่)
ผลทฤษฎี (FLP impossibility):
ก่อนเข้าทฤษฎี — สรุปเป็นภาษาคน 2-3 ประโยค:
ในโลก distributed ที่ network ไม่รับประกันเวลาตอบ — ไม่มี algorithm ใดที่ตรวจ failure ได้
ทั้ง 100% แม่นยำและ 100% เร็วพร้อมกัน นี่คือผลทางคณิตศาสตร์ที่ได้ชื่อย่อจากผู้พิสูจน์
FLP = ชื่อย่อจากผู้พิสูจน์ Fischer, Lynch, Paterson (1985)
ในเครือข่าย asynchronous (= เครือข่ายที่ไม่รับประกันเวลาตอบ — ส่งไปอาจช้าหรือเร็วก็ได้)
เราจะมีทั้งสองคุณสมบัติ 100% พร้อมกัน "ไม่ได้" — ต้องเลือกแลกสรุป (takeaway): ตรวจจับ "ตาย" ให้แม่น 100% พร้อมเร็ว 100% เป็นไปไม่ได้ในทางทฤษฎี — ดู FLP เพิ่มใน glossary บท 11
→ ในทางปฏิบัติ: เราเลือก Eventually Accurate (ตรวจผิดได้แต่จะแก้เอง) + ใช้ quorum + fencing token เพื่อกัน split brain
16. Hard Problems in Distributed Systems — 8 ปัญหาหลักที่ต้องแก้
ทุก distributed system จะต้องเจอ 8 ปัญหานี้ — แต่ละข้อมีหลักทฤษฎี + tools + best practices ต่างกัน หัวข้อนี้คือ "map" ของบทเรียนทั้งหมด — แต่ละปัญหาจะลึกในบทถัดไป
1. Time + Ordering — "อะไรเกิดก่อน?"
text
ปัญหา:
- Node 1 มี event A เวลา 10:00:00.005
- Node 2 มี event B เวลา 10:00:00.003
- B เกิดก่อนตาม wall clock — แต่ clock 2 nodes ไม่ตรงกัน
ทำไมยาก:
- NTP sync ก็มี skew ±10-100 ms
- "Causal order" ≠ "real-time order"
Solutions:
- Lamport clock (logical, preserves causality)
- Vector clock (detects concurrency)
- HLC (Hybrid — physical + logical)
- TrueTime (Google Spanner — hardware-based)→ ลึกใน บทที่ 4 — Time + Ordering
2. Consistency — "Data ตรงกันแค่ไหน?"
text
ปัญหา:
- เขียน data ที่ replica A
- อ่าน data ที่ replica B
- B อาจยังไม่ sync จาก A → อ่านได้ค่าเก่า
Spectrum:
- Linearizable (strongest, slow)
- Sequential
- Causal
- Read-Your-Writes
- Eventual (weakest, fast)
CAP/PACELC: เลือก trade-off→ ลึกใน บทที่ 1 — Consistency + CAP
3. Consensus — "เห็นด้วยพร้อมกัน?"
text
ปัญหา:
- N nodes ต้อง "agree" ค่าเดียวกัน
- มี node ตายได้, network partition ได้
- ทำยังไงให้ทุกคนเห็นค่าเดียวกัน?
ทำไมยาก:
- FLP impossibility — ไม่มี perfect consensus ใน async network
- Network partition + crash failure
Solutions:
- Paxos (1989 written, 1998 published in TOCS, hard)
- Raft (2014, easier)
- ZAB (ZooKeeper)
- BFT (Byzantine — for blockchain)→ ลึกใน บทที่ 2 — Consensus
4. Replication — "Data ปลอดภัยแค่ไหน?"
text
ปัญหา:
- 1 copy = lose disk = lose data
- Multi-copy = sync ระหว่าง copies?
Strategies:
- Single-leader (most SQL DBs)
- Multi-leader (CouchDB, Active Directory)
- Leaderless (Cassandra, DynamoDB)
Sync vs Async:
- Sync = no data loss, slow
- Async = fast, possible data loss
- Semi-sync = compromise5. Partitioning (Sharding) — "Data อยู่ที่ไหน?"
text
ปัญหา:
- Data ใหญ่เกินกว่า 1 server (1 TB+)
- Split data ข้าม servers
Strategies:
- Hash sharding (uniform)
- Range sharding (ordered queries)
- Geographic sharding (compliance)
Challenges:
- Hot partition (1 popular user → load ไม่กระจาย)
- Resharding ตอน scale (ย้าย data)
- Cross-shard joins (slow)→ ลึกใน บทที่ 1 + 10 (Consistent Hashing)
6. Transactions — "Atomic ข้าม nodes ได้ไหม?"
text
ปัญหา:
- ACID single DB: ง่าย
- ACID ข้าม services / DBs: ยากมาก
Approaches:
- 2PC (slow, blocking)
- 3PC (rarely used)
- Saga (compensating transactions)
- Event Sourcing + CQRS
- Outbox pattern (atomic DB + event)→ ลึกใน บทที่ 3 — Distributed Transactions
7. Failure Detection — "Node ตายหรือยัง?"
text
ปัญหา:
- Heartbeat หาย 5s = ตาย? หรือ slow?
- Trade-off: speed vs accuracy
Solutions:
- Heartbeat + timeout (simple)
- Phi Accrual (adaptive — Cassandra)
- SWIM (scalable — Memberlist, Consul)→ ลึกใน section 15 ข้างบน + บทที่ 9
8. Membership — "ใครอยู่ใน cluster?"
text
ปัญหา:
- Add new node → others ต้องรู้
- Remove dead node → others ต้องรู้
- Network partition → ข้อมูล "ใครอยู่" ไม่ตรง
Solutions:
- Gossip protocol (Cassandra, Consul)
- SWIM protocol (Memberlist, HashiCorp)
- Centralized registry (ZooKeeper, etcd, Consul)→ ลึกใน บทที่ 9
Summary Table
| # | Problem | Hardest part | บทไหน |
|---|---|---|---|
| 1 | Time + Ordering | Clock drift | บท 4 |
| 2 | Consistency | CAP trade-off | บท 1 |
| 3 | Consensus | FLP impossibility | บท 2 |
| 4 | Replication | Sync vs async | บท 1, 2 |
| 5 | Partitioning | Hot keys | บท 1, 10 |
| 6 | Transactions | Cross-system atomicity | บท 3 |
| 7 | Failure Detection | Slow vs dead | บท 0, 9 |
| 8 | Membership | Gossip/Consensus | บท 9 |
💡 มือใหม่อ่านตารางนี้ยังไง: นี่คือ "map" สำหรับบทต่อ ๆ ไป — ตอนนี้รู้แค่ว่ามีปัญหา 8 ตัว ไม่ต้องเข้าใจลึก ไม่ต้องท่อง แต่ละข้อจะถูกอธิบายเต็มในบทที่ระบุไว้
→ สรุป (takeaway): แต่ละปัญหาต้องออกแบบอย่างระมัดระวัง — จะลงลึกในบทถัด ๆ ไป
17. Don't Roll Your Own Distributed Algorithm — อย่าเขียนเอง
ของอันตรายที่สุดในวงการ distributed = "ฉันจะลองเขียน consensus algorithm เองดู"
ทำไมห้าม?
text
1. Distributed algorithm ที่ "ดูเหมือนถูก" มักจะ "ผิด" ในกรณี edge
- Race conditions ที่ไม่เกิดใน dev
- Network partition + crash recovery ผสมกัน
- Byzantine failures ที่ไม่คาดคิด
2. ไม่ใช่แค่เขียน — ต้อง "พิสูจน์ทางคณิตศาสตร์" ว่าถูก
- Lamport เขียน Paxos ใช้เวลา 10 ปีกว่าจะถูก
- Even researchers ผิดบ่อย — ดู papers ที่ retract
3. Production scenarios ที่ทดสอบยาก
- Clock skew
- GC pause นาน
- Network jitter
- Concurrent partitions
- Cascade failuresของที่ห้ามเขียนเอง — ใช้ library/service แทน
| สิ่งที่ห้ามเขียนเอง | ใช้แทน |
|---|---|
| Consensus (Raft, Paxos) | etcd / Consul (Raft) / ZooKeeper (ZAB) |
| Distributed Lock | etcd lease / Redis Redlock / ZooKeeper ephemeral node |
| Leader Election | etcd / ZooKeeper / Kubernetes Lease |
| Distributed Transaction | Saga via Temporal / Step Functions / 2PC via XA |
| Event Log + Ordering | Kafka / Pulsar / Redpanda |
| Distributed Cache | Redis Cluster / Hazelcast / Memcached |
| Distributed DB | CockroachDB, Spanner, Cassandra, DynamoDB |
| HA Postgres | Patroni (uses etcd) / pg_auto_failover |
| Service Discovery | K8s Service / Consul / etcd |
| Distributed Tracing | OpenTelemetry / Jaeger / Zipkin |
กรณีศึกษา — Redis Redlock controversy
text
2013: Redis creator Salvatore Sanfilippo เสนอ "Redlock" algorithm
→ distributed lock ที่ใช้ Redis cluster
2016: Martin Kleppmann (DDIA author) write critique:
"Redlock is broken under common failure scenarios"
- Clock drift → lock invalidated incorrectly
- GC pause → 2 clients hold lock simultaneously
Sanfilippo + community discuss for years
→ Consensus ปัจจุบัน (2026): Redlock ไม่ปลอดภัยสำหรับงาน critical correctness
(financial, inventory) แต่ใช้ได้สำหรับ best-effort coordination
(cache warmup, rate limit)
Lesson:
- แม้แต่ผู้เชี่ยวชาญ (Sanfilippo) ก็ออกแบบ algorithm ผิดได้
- ที่เรียกว่า "battle-tested" = ผ่าน production scenarios จริง 10+ ปี
- มือใหม่/มือกลาง — อย่าพยายามแข่ง✅ Production recommendation for distributed lock (ปี 2026):
- etcd lease (preferred) — built on Raft consensus + linearizable + automatic lease expiry
- Pattern: client acquires lease, key TTL'd by lease; if client dies, key auto-deletes
- Combined with fencing token (monotonic counter) → ป้องกัน "zombie lock holder"
- ZooKeeper ephemeral nodes — similar guarantees, older but mature
- K8s Lease object — built-in coordination primitive (uses etcd underneath)
- Postgres advisory lock — ถ้าใช้ Postgres อยู่แล้ว, single-region
❌ ห้ามใช้ Redis Redlock สำหรับ critical correctness (financial, inventory) — clock skew + GC pause issues; OK สำหรับ best-effort coordination (cache warmup, rate limit)
📚 อ่าน Kleppmann "How to do distributed locking" (2016) — เป็น foundational reading
Formal Methods — เครื่องมือพิสูจน์ algorithm ถูก
⚠️ (ขั้นต่อยอด · Senior+ level) — basic ที่ไม่ได้ออกแบบ consensus / replication protocol เอง ข้าม section นี้ไปได้ ไม่กระทบความเข้าใจของ บทถัดไป; section นี้เป็น pointer สำหรับคนที่ต้องการลึกระดับ research/critical infra
ถ้าจำเป็นต้องเขียน distributed algorithm จริง (research, niche product) → ใช้ formal verification tools เพื่อพิสูจน์ correctness ก่อน implement:
⚠️ ทั้งหมดในรายการนี้ไม่จำเป็นต้องรู้สำหรับ application dev — อ่านเพื่อ "รู้ว่ามี" พอ; ใช้จริงเมื่อต้องออกแบบ infra ระดับ critical (consensus / kernel / verified compiler)
text
TLA+ (อ่าน ที-แอล-เอ-พลัส, Leslie Lamport, 1999):
- Specification language = ภาษาเขียน "สเปก" (เงื่อนไข + invariant — เงื่อนไขที่ต้องเป็นจริงเสมอ)
แล้ว model checker (= โปรแกรมที่ลองทุก state ที่เป็นไปได้ของระบบ) ตรวจว่าเงื่อนไขเป็นจริงเสมอไหม
โดยไม่ต้อง run code จริง
- ระบุ system behavior แล้ว model checker ค้น invariant violation
(= กรณีที่เงื่อนไขถูกละเมิด)
- AWS ใช้สำหรับ S3, DynamoDB, EBS — เปิดเผยใน paper "Use of Formal Methods at AWS"
- Microsoft ใช้สำหรับ Azure Cosmos DB
- Free + open source (Lamport's site)
P language (Microsoft Research):
- Asynchronous event-driven systems
- ใช้ใน Azure (USB drivers, Kubernetes scheduler verification)
- Modern syntax friendlier than TLA+
Alloy (MIT):
- Lighter weight than TLA+
- Good for ontology + structural specs
- Use case: API design verification
Coq / Lean (proof assistants):
- Full theorem proving (mathematical proofs)
- Used in: CompCert (verified C compiler), seL4 (verified microkernel)
- Steeper learning curve, full rigor
When to invest:
- Custom consensus / replication protocol → TLA+
- Concurrent data structure → TLA+
- Complex state machine with multiple actors → P language
- Don't need formal proof for typical CRUD app📚 เรียนต่อ: "Specifying Systems" (Lamport, free pdf), learntla.com (Hillel Wayne tutorial), AWS paper "How AWS uses formal methods" (Newcombe et al. 2015)
ยกเว้น — เมื่อไหร่ "เขียนเอง" OK?
text
✅ การเรียนรู้ / ทำเข้าใจ algorithm
- implement Raft จาก paper เพื่อเข้าใจ
- "Build a simple X for learning" → great
✅ Specific niche ที่ไม่มี library
- Domain-specific protocol
- แต่ต้อง test extensively
❌ Production system ที่ critical
- "Just use etcd" — saved your future self ปวดหัวReal-world Practical Setup
text
Microservices ระบบทั่วไปต้องการ:
- Service discovery → K8s Service
- Config management → K8s ConfigMap / Consul
- Distributed lock → Redis / etcd
- Event log → Kafka (managed: MSK, Confluent Cloud)
- DB (transactional) → Postgres (managed: RDS, CloudSQL)
- Cache → Redis (managed: ElastiCache)
- Job queue → SQS / RabbitMQ / Temporal
ทุกอย่างใช้ managed service / proven open source
→ Focus เวลาเขียน business logic แทน infrastructure→ Rule of thumb (สำหรับ production use เท่านั้น): ถ้าโจทย์ของคุณคือ "implement Raft สำหรับ production system" → คุณเลือกผิดทาง (ถ้าทำเพื่อเรียน — ดูข้อยกเว้นด้านบน "เมื่อไหร่ 'เขียนเอง' OK")
18. Real Outages — เคสที่เคยล่มจริง (เรียนจาก postmortem)
เรียนจาก postmortem ที่บริษัทใหญ่เขียน — เพราะ "นักออกแบบดี ๆ ก็ล่ม" และเขาเขียนเล่าให้เราเรียน
AWS DynamoDB (กันยายน 2015)
text
สาเหตุ:
- DynamoDB ต้องอ่าน metadata จาก service กลาง
- ทีม scale up cluster → metadata service โดน request พุ่ง 10x
- Metadata service ช้า → ทุก request DynamoDB ค้าง
- Cascade ไป S3, IAM (ใช้ DynamoDB เป็น dependency)
ความเสียหาย:
- 5 ชั่วโมง outage ใน us-east-1
- ลูกค้าทั่วโลกที่ใช้ us-east-1 ได้รับผลกระทบ
บทเรียน:
✅ อย่าให้ metadata service เป็น sync dependency กับ data plane
✅ ทดสอบ scale-up ภายใต้ภาระ ไม่ใช่แค่ scale-down
✅ Cache metadata อย่างก้าวร้าว (ป้องกัน thundering herd)Source: AWS Service Health Dashboard postmortem
Cloudflare (กรกฎาคม 2019)
text
สาเหตุ:
- มี config ใหม่: regex สำหรับ WAF (Web Application Firewall)
- Regex มี backtracking ที่แย่ → input บางตัว = CPU 100%
- Deploy → Workers cluster ทุก node CPU เต็ม → ไม่ตอบ request
ความเสียหาย:
- 27 นาที outage ทั่วโลก
- 502 errors ทุกที่ที่ใช้ Cloudflare
บทเรียน:
✅ Regex ทดสอบกับ adversarial input (ReDoS attack pattern)
✅ Deploy ทีละ region (canary, ไม่ใช่ทั้งโลกพร้อมกัน)
✅ Kill switch / rollback ต้องเร็ว (< 5 นาที)
✅ Limit "blast radius" — ความเสียหายไม่ลามไปทั้งหมดSource: Cloudflare blog postmortem
Slack (มกราคม 2021)
text
สาเหตุ:
- กลับมาจาก holiday → traffic เพิ่มฉับพลัน (เกิน peak ปกติ)
- Vitess (MySQL proxy) ที่เปิดมา 2 ปีไม่เคย scale
- CPU saturation ระดับ proxy → query ค้าง
- Cascade: ทุกอย่างที่ depend on Slack หลังบ้านโดน
ความเสียหาย:
- ~5 ชั่วโมง outage บางส่วน
- ทั้งโลกรู้ในวันแรกของการ work-from-home หลังหยุดยาว
บทเรียน:
✅ Capacity planning ต้องดู growth rate ไม่ใช่แค่ peak ปกติ
✅ Monitor database tier ใกล้ชิด (เช่น connection pool, query latency p99)
✅ Headroom: ไม่ใช่ "ใช้ 80% ของ capacity ปกติ" แต่ "ใช้ 60% เพื่อรองรับ surge"Source: Slack engineering blog
Google Cloud (พฤศจิกายน 2021)
text
สาเหตุ:
- มี config error ใน load balancer global
- ส่งผลให้ traffic ของ Google services ไม่ route ออก
- Cascade: YouTube, Gmail, Drive, Workspace — ทั้งหมดล่ม
ความเสียหาย:
- ~2 ชั่วโมง outage ของ Google services ส่วนใหญ่
- โลกตื่นตระหนก (ใครๆ ก็ใช้ Gmail)
บทเรียน:
✅ Config change ต้องผ่าน canary หรือ progressive rollout
✅ Global config = global blast radius — ระวังให้ดี
✅ Test rollback procedure regularly (game day)Source: Google Cloud status
Fastly (มิถุนายน 2021)
text
สาเหตุ:
- ลูกค้าหนึ่งปรับ config ที่ trigger bug ใน Fastly CDN
- Bug ทำให้ทุก edge node ทั่วโลกค้าง
ความเสียหาย:
- 1 ชั่วโมง outage
- เว็บใหญ่ๆ ล่ม: NYT, Reddit, GitHub, Twitch, Amazon
บทเรียน:
✅ Multi-CDN strategy (มี backup CDN อีก vendor)
✅ Customer config ไม่ควรทำให้ shared infrastructure ล่ม
✅ Test config changes ใน staging ก่อน productionSource: Fastly blog postmortem
AWS us-east-1 (ธันวาคม 2021) — Kinesis cascade
text
สาเหตุ:
- ทีม scale up Kinesis Data Streams cluster
- OS thread limit (system-wide) ถูกชนใน frontend fleet
- Kinesis ใช้เป็น dependency ของ Cognito, CloudWatch, EventBridge, IAM
- Cascade: ทุก service ที่ depend on Kinesis ค้าง
ความเสียหาย:
- ~7 ชั่วโมง outage บางส่วน
- ลูกค้า: Disney+, Netflix, Robinhood, Tinder, Slack — ทั้งหมดล่ม
- "Cascading dependency" ทำให้ไม่สามารถ console เข้าไปแก้ได้ (Cognito ตาย → ไม่ login)
บทเรียน:
✅ Control plane dependencies ต้องไม่ผ่าน data plane ที่ตัวเองเสริมให้
✅ Break-glass procedures (เข้า console ตอน auth ล่ม) ต้องมี + ทดสอบ
✅ OS-level resource limits (thread, fd, port) ต้อง monitorSource: AWS Summary of Dec 7, 2021 outage
OpenAI ChatGPT (พฤศจิกายน 2023, มิถุนายน 2024) — K8s DNS scaling
text
สาเหตุ (Nov 2023):
- Kubernetes control plane size limit ถูกชน (etcd watch fanout)
- CoreDNS hit cardinality explosion → query timeouts
- Auto-scaling อิง metrics ที่ใช้ DNS → loop
ความเสียหาย:
- ChatGPT outage หลายชั่วโมง 2 ครั้งใน ~7 เดือน (Nov 2023 + Jun 2024)
- 200M+ weekly users affected ตามรายงาน OpenAI ในช่วงนั้น
บทเรียน:
✅ etcd default max DB size = 8 GB (config ได้ถึง ~100 GB)
— OpenAI hit cluster-specific limits จาก watch fanout + cardinality
ไม่ใช่ generic etcd limit
✅ K8s upstream supported = ~5,000 nodes/cluster
(บางลูกค้า run ถึง 15,000+ ด้วย custom tuning)
✅ DNS = often the cascading failure vector ใน K8s
✅ Capacity planning ของ control plane ≠ data planeSource: OpenAI status reports
CrowdStrike (กรกฎาคม 2024) — false-positive client update
text
สาเหตุ:
- CrowdStrike Falcon sensor update มี logic error
- Sensor พ่วงกับ Windows kernel → BSOD ทันทีเมื่อ boot
- Update โดน push global พร้อมกัน (ไม่มี canary)
ความเสียหาย:
- ~8.5 ล้าน Windows machines ทั่วโลก crash (ตัวเลข preliminary ของ Microsoft, 20 Jul 2024)
- Airlines ล่ม (Delta ฟ้องว่าสูญเสีย $500M+ — เลขจาก lawsuit, ยังไม่ใช่ confirmed loss)
- Hospital, bank, retail — ทั้งหมดล่ม
- มักถูกอ้างว่า "ใหญ่ที่สุดในประวัติศาสตร์ IT outage" (subjective — ขึ้นกับ metric)
บทเรียน:
✅ Client-side updates = global blast radius ที่ใหญ่ที่สุด
✅ Canary deployment ต้องครอบ "ทุก distributed system" รวม client agents
✅ Kernel-level integrations ต้อง defense-in-depth
✅ Recovery: physical access ต้องเข้า safe mode → labor-intensiveSource: CrowdStrike PIR
Pattern ที่ซ้ำๆ ใน outage ใหญ่ ๆ
text
1. "Single dependency" ที่ทุกอย่างเกาะ
- Metadata service, DNS, Auth service, Config plane
→ Mitigate: cache, fallback, degraded mode
2. "Global change" ที่ deploy พร้อมกัน
- Config, code, regex, schema
→ Mitigate: canary, progressive rollout, region-by-region
3. "Cascade failure" จาก hot dependency
- Service A ช้า → Service B เต็ม → Service C เต็ม → ...
→ Mitigate: timeout, circuit breaker, bulkhead
4. "Capacity gap" ที่ไม่ได้วางแผน
- Traffic spike เกิน 2x ปกติ → ระบบล่ม
→ Mitigate: auto-scale, headroom 30-50%, load test ระดับ surge→ บทเรียนรวม: นักออกแบบเก่ง ๆ ยังล่ม — เพราะ distributed system มันยากจริง ๆ System design ไม่ใช่ "ทำให้ perfect" แต่เป็น risk management
19. Distributed Mental Model — เปลี่ยน mindset จาก single-machine
หลายคนทำ distributed system โดยใช้ mental model ของ single-machine programming → bug เต็มไปหมด หัวข้อนี้คือ "mental model shift" ที่ทุกคนต้องทำเมื่อย้ายจาก monolith ไป distributed
Mental Model Shift — เปลี่ยน mindset จากเดิมเป็นใหม่
Shift 1: Function Call ≠ Network Call — โทรในเครื่อง ≠ โทรข้าม network
text
เก่า (single machine):
result = someFunction(input)
→ return value หรือ throw exception
→ ใน 100 nanoseconds
→ 2 possible outcomes
ใหม่ (distributed):
result = await remoteService.call(input)
→ return value, error response, network error
→ หรือ TIMEOUT (ไม่รู้ว่าทำสำเร็จไหม!)
→ ใน 1-1000+ milliseconds (variable!)
→ 4 possible outcomes (รวม "unknown")ผลกระทบในการเขียนโค้ด:
typescript
// เก่า — สมมติว่า return = สำเร็จ
function pay(amount) {
const charge = paymentService.charge(amount);
log("Charged " + charge.id);
sendEmail(user, "You paid " + amount);
}
// ใหม่ — เผื่อ timeout
async function pay(amount, idempotencyKey) {
try {
const charge = await paymentService.charge(amount, idempotencyKey);
log("Charged " + charge.id);
await sendEmailAsync(user, "Paid " + amount); // separate, can retry
} catch (e) {
if (e.isTimeout) {
// ไม่รู้ว่า charge สำเร็จไหม
// → ใช้ idempotencyKey check + reconcile background
await scheduleReconciliation(idempotencyKey);
} else {
throw e; // genuine failure
}
}
}Shift 2: Sequence of Operations ≠ Causal Order Across Nodes — ลำดับใน code ≠ ลำดับเหตุ-ผลข้าม node
text
เก่า:
step1();
step2();
step3();
→ ลำดับชัดเจน, แต่ละ step เสร็จก่อนถัดไป
ใหม่:
service A sends "OrderCreated" event
service B sends "InventoryReserved" event (might happen first!)
service C sends "PaymentCharged" event (might happen between!)
→ ลำดับใน database / log ไม่ตรงกับ "เวลาจริง"
→ ต้องใช้ causal ordering (Lamport / Vector clock)Shift 3: Database ≠ Single Source of Truth — DB ไม่ใช่ที่เดียวที่เก็บความจริง
text
เก่า:
Database = ground truth
Read DB → ได้ค่าที่ถูกแน่นอน
Write DB → ทุกคน read ครั้งถัดไปเห็นค่าใหม่
ใหม่:
Primary DB + replicas + cache + search index + materialized views
ทุกตัวมีค่าของตัวเอง ที่อาจไม่ตรงกัน (eventually consistent)
→ "Source of truth" = depends on consistency level
→ Read from cache → อาจเก่า
→ Read from replica → อาจ lag
→ Read from primary → ช้า แต่ตรงShift 4: Disk Storage ≠ Safe — disk เดียว ไม่ปลอดภัย
text
เก่า:
Write to disk → safe forever
Disk = persistent, reliable
ใหม่:
Single disk fail rate: 2-4% per year (HDD), 0.5-1% (SSD)
ใน data center 10K servers → 200-400 disks fail per year
→ Disk เดียว = ไม่ปลอดภัย
→ ต้องการ replication (RF ≥ 3)
→ ต้องการ backup ที่ตรวจสอบ restore ได้
→ ต้องการ checksum (silent data corruption)Shift 5: "If" → "When" — เปลี่ยน "ถ้า" เป็น "เมื่อ"
text
เก่า:
"ถ้า server crash..."
"ถ้า network ขาด..."
→ treated as edge case, ignored
ใหม่:
"เมื่อ server crash..."
"เมื่อ network ขาด..."
→ expected event, planned for
ใน production scale:
- Server crash = ทุกวัน
- Network partition = ทุกเดือน
- Region outage = ทุก 1-2 ปีComparison Table — ตารางเปรียบเทียบ mindset
| Aspect | Single-Machine Mental Model | Distributed Mental Model |
|---|---|---|
| Function call | Returns or throws (2 outcomes) | Returns, throws, or timeout (3 outcomes) |
| Sequence | Linear, deterministic | Events at multiple nodes, partial ordering |
| State | Single source of truth | Multiple views, eventual agreement |
| Storage | Disk = safe | Need replication + backup |
| Time | 1 clock, accurate | Multiple clocks, drift |
| Failure | Exception is rare | Failure is normal |
| Latency | ns-μs (predictable) | ms-sec (variable, tail latency) |
| Testing | Unit tests sufficient | Need chaos testing |
Common Mental Model Bugs — bug ที่เกิดจาก mental model ผิด
text
1. ลืม idempotency (ลืมทำให้ทำซ้ำได้ปลอดภัย)
- "ถ้า fail ก็ retry สิ" → หักเงินซ้ำ (double-charge)
2. ใช้ wall clock จัดลำดับ
- "ก็แค่เทียบ timestamp" → ลำดับผิดเพราะนาฬิกาเหลื่อม (clock drift)
3. คิดว่ายิงครั้งเดียวแล้วสำเร็จแน่ (single-shot success)
- "API ตอบ 200 = เสร็จ" → response อาจหาย, ถูก retry, เกิด duplicate
4. มองข้าม partial failure (บางส่วนสำเร็จ บางส่วนล้ม)
- "เขียน 2 ใน 3 replica สำเร็จ แล้ว return success" → แล้วตัวที่ 3 ล่ะ?
5. ทำทุกอย่างแบบ synchronous (รอทีละขั้น)
- Order → Payment → Inventory → Email (รอกันหมด) → 1 ตัวช้า = ช้าหมด
6. ไว้ใจ network
- ไม่มี timeout, ไม่มี retry, ไม่มี circuit breaker → ล้มลามทั้งระบบ (cascade failure)→ Mental model = foundation — ถ้าผิด ตรงนี้ → code ทั้งหมดจะ buggy ใน production scale
20. หนังสือและ Paper ที่ควรอ่านต่อ
อ่านเล่มนี้จบยังไม่พอครับ — distributed systems เป็นสาขาที่ลึกมาก ถ้าอยากเก่งจริงต้องอ่านต่อ หัวข้อนี้รวบรวม "แหล่งความรู้ที่คนวงการอ่านกันทุกคน" เรียงตามลำดับว่าควรอ่านอันไหนก่อน
ภาษาของหนังสือ/paper เกือบทั้งหมดเป็นภาษาอังกฤษ — ไม่ต้องกลัวครับ เริ่มจากอ่าน abstract กับ introduction ก่อน ค่อยลงลึก ส่วนใหญ่ของ paper อ่านได้จริงในเวลาไม่นาน
หนังสือที่ควรอ่าน (เรียงตามลำดับความจำเป็น)
1. เล่มต้องอ่าน ⭐ — Designing Data-Intensive Applications (Martin Kleppmann)
ในวงการเรียกย่อ ๆ ว่า DDIA — ถือเป็น "คัมภีร์" ของ distributed systems เลยก็ว่าได้
- ทำไมต้องอ่าน: ครอบคลุมทุกเรื่องในเล่มของเรา แต่ลึกกว่ามาก ทั้ง storage engine, replication, consensus, batch/stream processing
- ใครเขียน: Martin Kleppmann — เป็นทั้งนักวิจัยและคนทำงาน production จริง ใช้เวลาเขียน 4 ปี
- เหมาะกับใคร: คนที่จบเล่มนี้แล้วและอยากเข้าใจให้ลึกขึ้น หรือ senior engineer ที่ต้องออกแบบระบบ
- ความยาก: ⭐⭐⭐⭐ (4/5) — มี math เล็กน้อย แต่อธิบายดีมาก
- เวลาที่ใช้: ประมาณ 1-2 เดือนถ้าอยากเข้าใจจริง ๆ (ไม่ใช่อ่านผ่าน ๆ)
เคล็ดลับการอ่าน DDIA: เริ่มจากบทที่ 5 (Replication) กับบทที่ 7 (Transactions) ก่อน เพราะเป็นพื้นฐานของบทอื่น
2. เล่มลึกของ database — Database Internals (Alex Petrov)
ถ้า DDIA สอน "ระบบโดยรวม" เล่มนี้จะสอน "ลึกระดับ engine ของ database" เลย
- เนื้อหาหลัก: B-tree vs LSM-tree, Write-Ahead Log (WAL), MVCC (multi-version concurrency control), consensus algorithm ภายใน DB
- เหมาะกับใคร: คนที่ทำงานกับ database โดยตรง เช่น DBA, คนที่ปรับ performance, หรือคนที่อยากเข้าใจว่า PostgreSQL/Cassandra ทำงานยังไงข้างใน
- ความยาก: ⭐⭐⭐⭐ (4/5) — ลึกในเชิงเทคนิค ต้องมีพื้นฐาน data structure
3. เล่มออกแบบ microservices — Building Microservices (Sam Newman)
เน้น practical guide สำหรับคนที่ต้องออกแบบหรือย้ายระบบเป็น microservices
- เนื้อหาหลัก: วิธีแบ่ง service, รูปแบบการสื่อสารระหว่าง service, การ deploy และ observability
- เหมาะกับใคร: architect ที่ออกแบบระบบใหม่ หรือ team lead ที่นำทีมย้ายจาก monolith
- ความยาก: ⭐⭐⭐ (3/5) — อ่านง่ายกว่า DDIA เพราะเน้นภาคปฏิบัติ
4. เล่ม SRE จาก Google (ฟรี!) — Site Reliability Engineering
Google เปิดให้อ่านฟรี online ที่ sre.google/sre-book/table-of-contents
- เนื้อหาหลัก: SLI/SLO/SLA, error budget, วัฒนธรรม postmortem, on-call best practices, การวางแผน capacity
- เหมาะกับใคร: DevOps, SRE, รวมถึงทุกคนที่ดูแลระบบ production
- ความยาก: ⭐⭐⭐ (3/5)
- ราคา: ฟรี
ถ้าทีมของคุณยังไม่มี postmortem culture หรือยังไม่ได้กำหนด SLO ชัด ๆ — เล่มนี้คือคำตอบ
5. เล่ม bonus — Release It! (Michael Nygard)
หนังสือคลาสสิคเรื่อง resilience pattern — Circuit Breaker pattern ที่เราใช้กันทุกวันนี้ ก็ดังขึ้นมาจากเล่มนี้
- Pattern ในเล่ม: Circuit Breaker, Bulkhead, Timeout, Steady State และอีกหลายตัว
- จุดเด่น: มี postmortem ของระบบที่ล่มจริง ๆ มาเล่าให้ฟัง อ่านสนุกแถมได้บทเรียน
- ความยาก: ⭐⭐ (2/5) — อ่านง่ายที่สุดในกลุ่มนี้
- เหมาะกับใคร: ทุกคนที่ทำงานกับระบบ production
Paper ที่ควรอ่าน (เรียงตามความสำคัญ)
🏆 ระดับต้องอ่าน (foundational)
Paper 1 — Time, Clocks, and the Ordering of Events (Lamport, ปี 1978)
- ผู้เขียน: Leslie Lamport — เจ้าของรางวัล Turing Award (ของแขนง CS เหมือน Nobel Prize)
- ทำไมสำคัญ: เป็น paper ต้นกำเนิดของ Lamport clock — รากฐานของเรื่อง time/ordering ใน distributed system ทั้งหมด
- เวลาที่ใช้อ่าน: แค่ 30 นาที (paper สั้น ~5 หน้า)
- ความเหมาะสำหรับมือใหม่: สูงมาก — paper สั้น เนื้อหาเข้าใจได้
Paper 2 — In Search of an Understandable Consensus Algorithm (Raft, ปี 2014)
- ผู้เขียน: Diego Ongaro และ John Ousterhout จาก Stanford University
- ทำไมสำคัญ: Raft เป็น consensus algorithm ที่ใช้ใน etcd, Consul, TiDB, MongoDB และอีกหลายเครื่องมือ
- จุดเด่น: ออกแบบมาเพื่อให้ "เข้าใจง่าย" โดยเฉพาะ ตรงข้ามกับ Paxos ที่อ่านยาก
- เวลาที่ใช้อ่าน: ประมาณ 1 ชั่วโมง
🥇 ระดับสำคัญ (industry reference)
Paper 3 — Spanner: Google's Globally-Distributed Database (Google, ปี 2012)
- ทำไมสำคัญ: เปิดตัวแนวคิด TrueTime และ globally consistent SQL — เปลี่ยน landscape ของ distributed DB
- DB ที่ได้แรงบันดาลใจจาก paper นี้: CockroachDB, YugabyteDB, TiDB
Paper 4 — Dynamo: Amazon's Highly Available Key-value Store (Amazon, ปี 2007)
- ทำไมสำคัญ: เปิดตัวแนวคิด quorum-based replication, consistent hashing, vector clock มาให้วงการรู้จัก
- DB ที่ได้แรงบันดาลใจจาก paper นี้: Cassandra, Riak, DynamoDB
Paper 5 — Kafka: A Distributed Messaging System for Log Processing (LinkedIn, ปี 2011)
- ทำไมสำคัญ: จุดเริ่มต้นของยุค event-driven architecture — แทบทุกระบบ event-driven ปัจจุบันก็ inspired มาจาก Kafka
🥈 ระดับเฉพาะทาง แต่สำคัญ
Paper 6 — The Byzantine Generals Problem (Lamport, ปี 1982)
- ทำไมสำคัญ: เป็นรากฐานของ BFT (Byzantine Fault Tolerance) — สำคัญมากสำหรับ blockchain
- ความยาก: สูงขึ้น มี math เยอะ
- คำแนะนำ: อ่านได้ถ้าทำงานสาย blockchain หรือ critical system; ไม่อ่านก็ไม่เป็นไรในงาน backend ทั่วไป
Paper 7 — Paxos Made Simple (Lamport, ปี 2001)
- ทำไมสำคัญ: เป็น consensus algorithm ตัวแรก รากฐานของ Raft
- เรื่องเตือนใจ: ชื่อบอกว่า "Made Simple" แต่จริง ๆ อ่านยากมาก (เป็นเรื่องตลกในวงการ)
- คำแนะนำ: อ่าน Raft paper แทนจะดีกว่า — เข้าใจง่ายกว่ามาก
Paper 8 — MapReduce: Simplified Data Processing on Large Clusters (Google, ปี 2004)
- ทำไมสำคัญ: จุดเริ่มต้นของยุค big data
- ระบบที่ inspired มา: Hadoop, Spark
แหล่งความรู้ออนไลน์ (Online Resources)
บล็อก engineering ที่แนะนำให้อ่านเรื่อย ๆ
ทีม engineering ของบริษัทใหญ่ ๆ จะแชร์ปัญหาและทางแก้จริงในบล็อก อ่านเรื่อย ๆ จะได้ "ไอเดียจากของจริง" ครับ
- High Scalability (
highscalability.com) — รวม case study จากระบบใหญ่ ๆ - Discord Engineering — เก่งเรื่อง real-time + scaling
- Stripe Engineering — system design ระดับเทพ
- Uber Engineering — large-scale architecture
- Netflix TechBlog — chaos engineering, microservices
- Cloudflare Blog — เครือข่ายและ security
- Google SRE Blog — ภาคต่อของหนังสือ SRE
- AWS Architecture Blog — cloud architecture pattern
งานสัมมนาและ conference ที่ควรตาม
- QCon — talk เรื่อง architecture
- KubeCon — งาน cloud-native ระดับโลก
- Strange Loop — เน้นงาน research distributed system
- Hydra Conference — เฉพาะทาง distributed system โดยตรง
- AWS re:Invent — งานใหญ่ของ cloud architecture
- Velocity Conference — performance และ operations
คอร์สเรียนที่แนะนำ
- MIT 6.824: Distributed Systems — คอร์สระดับโลก มี Raft lab ให้ทำ ฟรีบน YouTube + course site
- DDIA reading group — ในชุมชนต่าง ๆ มีกลุ่มอ่าน DDIA ด้วยกัน หาเจอใน Discord, Reddit
ลำดับการอ่านที่แนะนำ (Reading Roadmap)
ระดับ Junior — เริ่มต้น
- SRE book (ฟรี เน้นภาคปฏิบัติ) → ประมาณ 1 เดือน
- Building Microservices → ประมาณ 2 สัปดาห์
- อ่านเล่มนี้ให้จบ + ทำ checkpoint ทุกบท → 1-2 เดือน
ระดับ Mid-level — ปูพื้นให้แน่น
- DDIA อ่านตั้งแต่ต้นจนจบ → ประมาณ 2 เดือน
- Raft paper + Lamport time paper → 1 สัปดาห์ (สองอันรวมกัน)
- ลงมือทำ: สร้าง event-driven app ของตัวเอง
ระดับ Senior — เจาะลึก
- Database Internals → ประมาณ 2 เดือน
- Spanner paper + Dynamo paper → 1 สัปดาห์
- ฝึกออกแบบและ operate distributed system จริงใน production
- อ่าน engineering blog ของบริษัทที่ scale ใหญ่ ๆ เรื่อย ๆ
ทำไมต้องอ่าน paper ด้วย?
หลายคนคิดว่าอ่านแค่หนังสือพอแล้ว — แต่ paper สำคัญเพราะเหตุผลเหล่านี้:
- Code บอกแค่ "ทำอะไร" — paper อธิบาย "ทำไม" เห็นเหตุผลและ trade-off ที่ทีมต้นทางเจอ
- มี trade-off และ decision จริง ที่บางทีหายไประหว่างทางในหนังสือ
- ฝึก critical thinking — paper บางอันก็มีข้อบกพร่อง อ่านแล้วต้องคิดวิเคราะห์
- Senior+ คาดหวังให้อ่าน paper เป็น — เป็นส่วนหนึ่งของ skill
- Interview ระดับ principal มักถามเรื่องที่อยู่ใน paper เช่น Spanner, Dynamo
เคล็ดลับการอ่าน paper สำหรับมือใหม่
- อ่าน abstract กับ introduction ก่อน (ได้สรุปใน 1 หน้า)
- ข้ามส่วน math/proof ถ้าไม่จำเป็น ไม่ต้องเข้าใจหมดในรอบแรก
- ดู diagram และ example เป็นหลัก
- กลับมาอ่านลึกอีกครั้งตอนต้องใช้งานจริง
เริ่มจาก Raft paper กับ Lamport time paper ก่อนครับ — สองอันนี้เป็นจุดเริ่มต้นที่ดีที่สุดสำหรับมือใหม่
21. Common Tools — รู้จักไว้ก่อน เจอบ่อย
ในวงการ distributed system มี tool ที่ "ทุกคนต้องรู้จัก" — เพราะ trade-off แต่ละตัวต่างกัน เลือกใช้ผิด = ปวดหัวยาว
1. Coordination Tools — "ทำให้ N nodes agree กัน"
| Tool | ภาษา | Algorithm | จุดเด่น | จุดด้อย |
|---|---|---|---|---|
| etcd | Go | Raft | API gRPC, ทันสมัย, K8s ใช้ | ต้อง operate เอง (หรือ managed) |
| ZooKeeper | Java | ZAB | เก่า มีคนใช้เยอะ (Kafka, Hadoop) | Operate ยาก, Java memory hungry |
| Consul | Go | Raft | Service discovery + KV + DNS + UI | Feature เยอะอาจ overkill |
เมื่อไหร่ใช้:
- K8s control plane → etcd (built-in)
- Service discovery + config → Consul
- Hadoop/old Kafka stack → ZooKeeper (legacy)
2. Streaming / Event Log — "Log ที่ scale ได้"
| Tool | จุดเด่น | จุดด้อย |
|---|---|---|
| Kafka | Standard de facto, throughput สูง, ecosystem ใหญ่ | Operate ยาก, ZooKeeper dependency (เก่า) / KRaft (ใหม่) |
| Pulsar | Multi-tenancy, geo-replication built-in | คนใช้น้อยกว่า, ซับซ้อน (broker + bookkeeper) |
| Redpanda | Kafka API ทั้งหมด, ไม่ต้อง ZK, เร็วกว่า (C++) | ใหม่กว่า, community เล็กกว่า |
| NATS JetStream | Lightweight, multi-language SDK ดี | Throughput น้อยกว่า Kafka |
| AWS Kinesis | Managed, ผูกกับ AWS | Vendor lock-in, retention default 24h |
Heuristic: ถ้าไม่มีเหตุผลพิเศษ — เริ่มที่ Kafka (ecosystem ดีสุด)
3. Distributed Databases — "DB ที่ replicate + scale ได้"
แบ่งตามประเภท:
Distributed SQL (strong consistency + SQL):
- CockroachDB — open source, Postgres compatible, HLC
- TiDB — HTAP (OLTP + analytics)
- YugabyteDB — Postgres + Cassandra API
- Google Spanner — managed, TrueTime, global strong consistency
Wide-column / NoSQL (high throughput, eventual):
- Cassandra — Java, classic, ecosystem ใหญ่
- ScyllaDB — C++, Cassandra-compatible แต่เร็วกว่า 3-5x
- DynamoDB — managed AWS, key-value + secondary index
Document:
- MongoDB — เป็นที่นิยม ใช้งานง่าย แต่ระวัง consistency
- CouchDB — multi-master sync (offline-first)
Key-Value:
- Redis Cluster — in-memory, cache + KV
- DynamoDB — managed
- etcd — KV เล็ก, สำหรับ config
4. Service Mesh — "Network-layer for microservices"
| Tool | ภาษา proxy | จุดเด่น | จุดด้อย |
|---|---|---|---|
| Istio | Envoy (C++) | Feature ครบที่สุด | Learning curve ชัน, resource เยอะ |
| Linkerd | Linkerd2-proxy (Rust) | เบา, ตั้งค่าง่าย | Feature น้อยกว่า Istio |
| Consul Connect | Envoy | Integration กับ Consul/Vault | Niche |
| Cilium Service Mesh | eBPF | ไม่ต้อง sidecar (sidecarless) | ใหม่, K8s only |
เมื่อไหร่ใช้: มี microservices > 20 ตัว และต้องการ mTLS / observability / retry ที่ centralize
5. Stream Processing — "ทำ analytics real-time"
| Tool | จุดเด่น | จุดด้อย |
|---|---|---|
| Apache Flink | Stateful streaming, exactly-once, low latency | Operate ซับซ้อน |
| Kafka Streams | Library (ไม่ต้อง cluster), Java/Scala | ผูกกับ Kafka, JVM only |
| Apache Spark Streaming | DataFrame API, batch + stream | Latency สูงกว่า Flink |
| ksqlDB | SQL-based, ง่าย | Feature จำกัด |
เมื่อไหร่ใช้:
- Real-time aggregation, anomaly detection → Flink
- ETL จาก Kafka topic → Kafka Streams
- SQL analyst ที่ใช้ stream → ksqlDB
Decision Cheat Sheet — เลือก tool ตามความต้องการ
text
ต้องการ "Strong consistency + SQL" → Postgres (single) หรือ CockroachDB (distributed)
ต้องการ "High write throughput" → Cassandra / ScyllaDB
ต้องการ "Low latency cache" → Redis
ต้องการ "Event log + replay" → Kafka
ต้องการ "Service discovery" → Consul / etcd / K8s Service
ต้องการ "Coordination (lock, leader election)" → etcd / ZooKeeper
ต้องการ "Document DB" → MongoDB / Couchbase
ต้องการ "Real-time stream" → Flink / Kafka Streams
ต้องการ "Microservices mTLS + observability" → Service Mesh (Istio/Linkerd)→ จะ deep dive แต่ละ tool ในบทถัดไป
22. ทำไมต้องรู้ distributed system
หลายคนถามว่า "ผมเขียน CRUD app อย่างเดียว ทำไมต้องเรียน distributed?" — คำตอบสั้น ๆ คือ เพราะปัจจุบันแทบไม่มีระบบไหนที่ไม่ distributed ครับ ลองดูตัวอย่าง: app ที่เรียก API หลายตัว, ใช้ DB ที่มี replica, deploy บน K8s — ทุกอย่างนี้คือ distributed system แล้ว
บทบาทงานที่ต้องรู้เรื่องนี้
| บทบาท | ทำไมต้องรู้ |
|---|---|
| Backend engineer (microservices) | ต้องออกแบบการสื่อสารระหว่าง service และจัดการ failure |
| DevOps / SRE | ดูแล production system ที่มีหลาย node ต้องเข้าใจ failure mode |
| Data engineer | ใช้ Kafka, Spark, Flink — เป็น distributed framework ทั้งหมด |
| Platform engineer | Build platform บน K8s, service mesh, observability stack |
| Senior+ ในตำแหน่งใด ๆ | ระดับนี้คาดหวังให้เข้าใจ trade-off ระดับระบบ |
ในการสัมภาษณ์งาน (interview)
ถ้าสมัครงานระดับ mid-level ขึ้นไป จะเจอคำถามแบบนี้แน่นอน:
- System design question — เช่น "ออกแบบ URL shortener ที่รองรับ 1B request/วัน" หรือ "ออกแบบระบบ chat แบบ WhatsApp"
- Distributed system question — เช่น "อธิบาย CAP theorem" หรือ "Raft กับ Paxos ต่างกันยังไง"
- Real-world architecture discussion — เจาะลึกระบบที่คุณเคยทำ ถามว่ารับมือกับ failure ยังไง
ในการทำงานจริง (real work)
เรื่องที่จะเจอแน่นอน ๆ ในงานประจำ:
- Debug production issue ข้าม service — bug อยู่ที่ service A แต่ root cause อาจอยู่ที่ B ที่เรียก C ที่ timeout
- ออกแบบ service ใหม่ — ต้องตัดสินใจเรื่อง consistency, retry, failure handling
- วางแผน migration — ย้ายจาก monolith เป็น microservices, หรือเปลี่ยน database
- Review architecture decisions — ดู PR/RFC ของทีม และเสนอแนะ trade-off
สรุปสั้น ๆ: distributed system ไม่ใช่ "ทักษะพิเศษ" อีกต่อไป — เป็น พื้นฐาน ที่ทุก engineer ระดับ mid-level ขึ้นไปต้องมีครับ
23. ตัวอย่างจริง — ระบบ distributed ที่เราใช้กันอยู่
ทฤษฎีในเล่มนี้ไม่ใช่แค่แนวคิดในกระดาษ — มันอยู่ใน infrastructure ที่เราใช้ทุกวันครับ ลองมาดู 3 ตัวอย่างใหญ่ ๆ ว่าเอา concept ที่เราเรียนไปใช้ยังไง
ตัวอย่างที่ 1 — Cassandra รับมือกับ node ตายยังไง
Cassandra เป็น NoSQL database ที่ออกแบบมาเพื่อ "always available" — ต้องไม่ล่มแม้ node บางตัวจะตาย ใช้แนวคิด replication factor กับ quorum ที่เราเรียนในบทนี้แหละครับ
สมมติว่า replication factor = 3 (หมายความว่าข้อมูลแต่ละชิ้นจะถูกเก็บไว้ใน 3 node)
ตอน client เขียน data:
- Coordinator node รับ request ก่อน (Cassandra ไม่มี master — ทุก node เป็น peer)
- Coordinator ส่ง write request ไปยัง 3 node ที่ถือ replica ของข้อมูลนี้
- ถ้า 2 ใน 3 node ตอบ ACK → return success ทันที (quorum write)
- node ที่ 3 อาจช้าหรือ down อยู่ — Cassandra ยอมรับได้ เป็น eventual consistency
ถ้า 1 node ตายลง:
- อีก 2 node ยังทำงานต่อได้ — quorum = 2/3 ยังครบ
- Read ก็ยังทำงาน — อ่านจาก 2 node ที่เหลือ แล้วเปรียบเทียบ version
- เมื่อ node ใหม่เข้ามาแทน → ข้อมูลของ node ที่ตายจะถูก redistribute (โอนใส่ node ใหม่)
บทเรียน: Cassandra เลือกเป็น AP system (CAP theorem) — เน้น Availability และ Partition tolerance ยอมแลก consistency เป็น eventual
ตัวอย่างที่ 2 — Kafka รับมือกับ broker ตายยังไง
Kafka เป็น distributed event log — ใช้กันแพร่หลายในระบบ event-driven หลักการของมันใช้ทั้ง leader-follower replication และ automatic failover ที่เราเรียน
โครงสร้างของ Kafka:
- 1 topic ถูกแบ่งเป็นหลาย partition
- แต่ละ partition มี 1 leader + 2 follower (ถ้า replication factor = 3)
- Client เขียน/อ่านได้กับ leader เท่านั้น
ตอน producer เขียน message:
- Producer ส่ง message ไปที่ leader ของ partition นั้น
- Leader replicate ไปยัง follower แบบ async (หรือ sync ถ้าตั้ง
acks=all)
ถ้า leader ตายลง:
- ZooKeeper หรือ Kafka Controller (ตัว coordinator) ตรวจจับว่า leader หาย (heartbeat timeout)
- เลือก 1 follower ขึ้นมาเป็น leader ใหม่ (มี protocol ภายในจัดการ)
- แจ้ง client ทุกตัวว่า partition นี้มี leader ใหม่แล้ว
- Client retry request ไปหา leader ใหม่
บทเรียน: Kafka ทำ auto-failover ในไม่กี่วินาที — ไม่ต้องรอ admin มาแก้ไขเอง นี่คือ pattern ที่ระบบ production ต้องมี
ตัวอย่างที่ 3 — Kubernetes รับมือกับ node ตายยังไง
Kubernetes (K8s) เป็น container orchestrator ที่ใช้กันทุกที่ — ออกแบบให้รับ node failure ได้ทั้งฝั่ง control plane และ worker
Multi-master architecture:
- มี control plane 3 node (etcd, API server, scheduler, controller manager)
- ใช้ Raft consensus (ผ่าน etcd) — ต้องการ quorum = 2/3
ถ้า 1 master ตายลง:
- เหลือ 2 master ทำงาน — quorum ยังครบ → cluster ยังทำงานปกติ
- ถ้าตาย 2 master พร้อมกัน → จะเริ่มมีปัญหา (quorum หาย เขียนไม่ได้)
ฝั่ง worker node:
- ทุก worker ส่ง heartbeat ไปยัง control plane
- ถ้า heartbeat หาย → control plane mark ว่า "unhealthy"
- Pod ที่เคยอยู่บน node นั้นจะถูก reschedule ไปยัง node อื่น
- ข้อมูลใน container ที่ไม่มี persistent volume = หาย (ถ้าไม่ใช่ stateless)
บทเรียน: K8s ใช้ pattern replication + quorum + auto-failover เหมือนกับ Cassandra และ Kafka — เพราะ pattern นี้คือคำตอบมาตรฐานสำหรับ distributed system
Pattern ร่วมที่เห็นจาก 3 ระบบ
ทั้ง Cassandra, Kafka, K8s ใช้ pattern เดียวกัน — replicate → quorum → failover — ต่างกันแค่รายละเอียดและจุดเน้น (Cassandra เน้น availability, Kafka เน้น ordering, K8s เน้น scheduling) ครับ
24. ต้นทุนของ Distributed System — จริง ๆ แล้วแพงกว่าที่คิด
ก่อนที่จะกระโดดไปทำ distributed system — ต้องเข้าใจก่อนว่า ทุกอย่างมีต้นทุน ครับ Distributed system ไม่ใช่ทางลัด มันเป็นการแลก "scale + reliability" กับ "ความซับซ้อน + ค่าใช้จ่าย"
หลายทีมพลาดเพราะเลือกใช้ microservices ตั้งแต่ระบบยังเล็ก สุดท้ายเสียเวลาแก้ปัญหา distributed มากกว่าเขียน feature ใหม่
ต้นทุนเทียบกับ monolith (ระบบเดี่ยว)
| ด้าน | Monolith | Distributed | เพิ่มขึ้นกี่เท่า |
|---|---|---|---|
| Infrastructure | 1 server พอ | ต้องมี service + DB + cache + message queue + LB | 3-5 เท่า |
| Debugging | อ่าน log ไฟล์เดียว | ตามรอย request ข้าม service หลายตัว ต้องใช้ tracing | 5-10 เท่า |
| Monitoring | watch CPU/memory/log พอ | ต้องดู service mesh, metrics ข้าม service, distributed trace | 5+ เท่า |
| Network | ไม่มี (in-process call) | RPC ทุกครั้ง = latency + failure point ใหม่ | สูง |
ต้นทุนการ operate (operational cost)
ระบบใหญ่ขึ้นมาแล้ว ค่าใช้จ่ายซ้อนหายเข้ามาตามมา:
- On-call 24/7 — ต้องมีคน standby หลายทีม (ทีมหนึ่งต่อ 1-2 service)
- Monitoring + alerting infrastructure — Prometheus, Grafana, ELK, Datadog — ค่าใช้จ่ายเดือนละหลายหมื่นบาทขึ้นไป
- Service mesh (Istio, Linkerd) — ช่วย operate ดีขึ้น แต่ก็มี learning curve กับ overhead
- Cross-region traffic — ส่งข้อมูลข้ามภูมิภาคของ cloud (เช่น Singapore → US) แพงมาก ทั้งค่า bandwidth และ latency
- ค่า engineer ที่เก่งพอจะ operate — distributed engineer หายาก เงินเดือนสูง
เมื่อไหร่ควร "go distributed"?
หลักการง่าย ๆ คือ ทำ distributed ก็ต่อเมื่อประโยชน์ที่ได้ > ต้นทุนที่เพิ่มขึ้น เท่านั้น
ควรเป็น distributed เมื่อ:
- ระบบใหญ่จน 1 server เอาไม่อยู่จริง ๆ (vertical scale ไม่ไหวแล้ว)
- ต้องการ high availability — ระบบล่มไม่ได้ (เช่น แบงค์, e-commerce ใหญ่)
- มีหลายทีมที่ต้อง deploy independent (microservices ช่วย team autonomy)
- ต้องการ geo-distribution — บริการผู้ใช้หลายภูมิภาค latency ต่ำ
อย่ารีบทำ distributed ถ้า:
- ระบบยังเล็ก ผู้ใช้ไม่กี่พันคน → monolith ดีกว่า
- ทีมขนาดเล็ก (< 10 คน) → ดูแล distributed ลำบาก
- ยังไม่มี observability stack → operate ไม่ได้
คำเตือน: หลายทีมโดน "microservices hype" ทำตอนระบบยังเล็ก สุดท้ายต้อง rollback กลับเป็น monolith เพราะ operate ไม่ไหว — รู้จัก trade-off ก่อนเริ่มดีกว่ามาแก้ทีหลัง
25. Checkpoint — แบบฝึกหัดทบทวน
ทำแบบฝึกหัดเหล่านี้ก่อนไปบทถัดไป — ถ้าทำได้ครบแสดงว่าเข้าใจ mindset ของ distributed system แล้วครับ
🛠️ Checkpoint 0.1 — หา Fallacy ในโค้ดของตัวเอง
เปิดโปรเจกต์ที่ทำอยู่ แล้วลองหา 3 ตัวอย่างที่ตัวเอง (หรือเพื่อนในทีม) "เคยเชื่อใน fallacy" โดยไม่รู้ตัว เช่น:
- โค้ดที่เรียก API แต่ ไม่ได้ตั้ง timeout (เชื่อใน "network เร็วเสมอ")
- การ retry แบบ infinite loop (เชื่อใน "network reliable")
- การส่งข้อมูลใหญ่โดยไม่คิดเรื่อง bandwidth (เชื่อใน "bandwidth infinite")
เป้าหมาย: หาให้เจอ 3 จุด และเขียนว่าจะแก้ยังไง
🛠️ Checkpoint 0.2 — Trace Failure Mode
เลือกระบบที่คุณทำอยู่หนึ่งระบบ แล้วลิสต์:
- 5 วิธีที่ระบบนี้พังได้ (เช่น DB down, network partition, deploy พลาด, traffic spike, dependency ล่ม)
- สำหรับแต่ละกรณี:
- ตรวจจับยังไง? (alert? metric? log?)
- กู้คืนยังไง? (failover อัตโนมัติ? manual? rollback?)
เป้าหมาย: ฝึกคิดแบบ "อะไรพังได้บ้าง" — เป็นพื้นฐานของ resilient engineer
🛠️ Checkpoint 0.3 — ประมาณ Latency
ลองประมาณ latency ของสถานการณ์เหล่านี้ (ทบทวน Section 12):
- เรียก API จากผู้ใช้ที่กรุงเทพ ไปยัง server ที่ AWS us-east-1
- Query DB ที่มี replica อยู่ใน region เดียวกัน
- Upload รูปขนาด 1 GB ขึ้น S3
- WebSocket roundtrip ใน data center เดียวกัน
เป้าหมาย: รู้จัก order of magnitude — distributed engineer ต้อง "เดาตัวเลขให้ใกล้เคียงได้"
🛠️ Checkpoint 0.4 — อ่าน DDIA บทที่ 8
อ่าน DDIA บทที่ 8 ชื่อ "The Trouble with Distributed Systems" — เป็นบทที่อ่านง่ายและสำคัญมาก
- อ่านจบแล้วเขียน summary 1 หน้า
- จดสิ่งที่ทำให้ "อึ้ง" หรือ "ไม่คิดว่ามันยากขนาดนี้" 3 ข้อ
เป้าหมาย: เริ่มฝึกอ่าน DDIA ตั้งแต่เนิ่น ๆ — จะได้ไม่ต้องอ่านทีเดียวทั้งเล่มตอน senior
🛠️ Checkpoint 0.5 — อ่าน Postmortem จริง
ไปอ่าน postmortem สาธารณะของบริษัทใหญ่ (เลือก 1 อันก็ได้):
- Cloudflare blog — มี postmortem หลายอัน
- AWS Status History — เคส outage ใหญ่ ๆ
- GitHub Status — incident report
- Slack Engineering
วิเคราะห์:
- สาเหตุที่แท้จริง (root cause) คืออะไร?
- เป็น failure ประเภทไหน? (network, hardware, software bug, human error)
- ป้องกันยังไงได้บ้าง? (ตามที่ทีมต้นทางบอก หรือคุณคิดเอง)
เป้าหมาย: เรียนจาก "ความล้มเหลวของคนอื่น" — ถูกกว่าและเร็วกว่ารอให้พังเองเยอะ
26. สรุปบท
จบบทแรกแล้วครับ — เราได้ปูพื้น mindset ของ distributed system ครบทุกมิติที่จำเป็น สรุปสิ่งที่ต้องจำติดตัวไปบทถัดไป:
✅ Distributed system = computer หลายเครื่อง ทำงานผ่าน network ให้ดูเหมือนเป็นเครื่องเดียว ✅ 8 Fallacies of Distributed Computing — ทุกข้อสมมุติ "ตรงข้าม" กับสามัญสำนึก ✅ Network ไม่น่าเชื่อถือ — ช้า, แพง, ไม่ปลอดภัย ต้อง assume สิ่งเหล่านี้เสมอ ✅ Topology เปลี่ยนตลอด — ใช้ service discovery แทน hard-code IP ✅ CAP theorem — เลือก 2 จาก 3: Consistency, Availability, Partition tolerance ✅ PACELC — ส่วนต่อขยายของ CAP: ตอนไม่มี partition ก็ต้องเลือก latency vs consistency ✅ Failure mode — มีหลายแบบ: crash-stop, partial, partition, Byzantine ✅ Two Generals Problem — ตกลงกัน 100% ผ่าน network ที่ไม่ reliable เป็นไปไม่ได้ ✅ Eventual consistency เป็น default ของระบบ internet-scale ส่วนใหญ่ ✅ Common pattern — replication, sharding, consensus, quorum, leader election ✅ Common issue — split brain, cascade failure, thundering herd, cache stampede ✅ อย่าเขียน distributed algorithm เอง — ใช้ของที่ผ่านการทดสอบแล้ว (etcd, Kafka, Cassandra) ✅ อ่าน DDIA + paper ที่แนะนำ — เพื่อต่อยอดเป็น senior+
27. Mini Glossary — ศัพท์สำคัญในบทนี้ (สำหรับ Thai reader)
สำหรับ glossary เต็มของทั้งเล่ม ดูที่ glossary บท 11 หรือ Glossary หลัก
| ศัพท์ | คำอ่าน | ความหมายแบบเข้าใจง่าย |
|---|---|---|
| Latency | เล-เทน-ซี่ | ความหน่วง เวลาที่ต้องรอ |
| Throughput | ทรู-พุท | ปริมาณงานต่อหน่วยเวลา |
| Idempotent | ไอ-เด็ม-โพ-เทนต์ | ทำซ้ำกี่ครั้งผลก็เหมือนเดิม (ป้องกัน double-charge เมื่อ retry) |
| Quorum | คอ-รัม | จำนวนเสียงข้างมาก (> N/2) ที่ต้องการเพื่อตัดสินใจ |
| Linearizability | ลิ-เนีย-ไรซ์-อะ-บิ-ลิ-ตี้ | ทุกคนเห็น state ทันทีแบบเรียงลำดับเดียวกัน (เหมือน single machine) |
| Consensus | คอน-เซน-ซัส | การที่หลาย node ตกลงค่าเดียวกัน |
| Anti-entropy | แอน-ติ-เอน-โทร-ปี้ | กระบวนการ sync ข้อมูลให้ตรงกัน (กัน drift) |
| Partition | พา-ทิ-ชั่น | เครือข่ายแบ่งเป็นส่วน ๆ ที่คุยกันไม่ได้ |
| Replica | เร็พ-พลิ-ก้า | สำเนาข้อมูล |
| Jitter | จิต-เตอร์ | การสุ่มเวลานิดหน่อยให้ไม่ตรงกัน |
| Heartbeat | ฮาร์ท-บีท | สัญญาณ "ผมยังอยู่" ที่ส่งเป็นจังหวะ |
| Fencing token | เฟ้น-ซิ่ง โท-เคน | ตัวเลขที่เพิ่มขึ้นเรื่อย ๆ ใช้ปฏิเสธ leader เก่าที่ฟื้นกลับมา |
| Phi accrual | ไฟ แอค-ครู-อัล | failure detector แบบ probabilistic (ตอบเป็น % แทน yes/no) |
| Wall clock | วอลล์ คล็อก | นาฬิกาแขวนผนัง = เวลาปกติที่อาจเดินถอยหลังได้ |
| Monotonic clock | โม-โน-ทอ-นิค คล็อก | นาฬิกาที่เดินหน้าทางเดียว — เหมาะวัด duration |
| Tail latency | เทล เล-เทน-ซี่ | ความหน่วงที่ส่วนหางของกราฟ (p99, p999 — กลุ่มที่ช้าสุด) |
| Split brain | สปลิต เบรน | สถานการณ์ที่มี leader 2 ตัวพร้อมกันเพราะ network partition |
| Cascade failure | แคส-เคด แฟล-เยอร์ | ปัญหาเล็ก ๆ ลามทั้งระบบ (เหมือน domino) |
| Noisy neighbor | นอย-ซี่ เนเบอร์ | เพื่อนบ้านเสียงดัง — VM อื่นบนเครื่องเดียวกันที่แย่ง CPU เรา |
| Thundering herd | ธัน-เดอร์-ริ่ง เฮิร์ด | ฝูงม้าวิ่งพร้อมกัน — ทุก client ยิง request พร้อมกัน |
| Backoff | แบ็ค-ออฟ | รอนานขึ้นก่อน retry ครั้งถัดไป |
| CAP | แคป | Consistency / Availability / Partition tolerance — เลือก 2 จาก 3 |
| PACELC | แพค-เอลซี่ | ส่วนต่อจาก CAP: Partition → A vs C, Else → Latency vs Consistency |
| ACID | แอซ-ซิด | Atomicity / Consistency / Isolation / Durability ของ traditional DB |
| BASE | เบส | Basically Available / Soft state / Eventually consistent ของ NoSQL |
| RTT | อาร์-ที-ที | Round-Trip Time = เวลาวิ่งไป-กลับ 1 รอบ |
| FLP | เอฟ-แอล-พี | Fischer, Lynch, Paterson — ผลทางคณิตศาสตร์ที่ว่า consensus ใน async network แม่น + เร็ว 100% พร้อมกันไม่ได้ |
🔤 Glossary · 📋 Style guide · 📅 last_verified: 2026-06-03