Skip to content

บทที่ 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 บาท" → ระบบเรียก APITIMEOUT

คำถาม: เงินถูกโอนไปแล้วหรือยัง?

  • อาจจะโอนสำเร็จ (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

  1. เครือข่ายเชื่อถือได้ (The network is reliable) — network เชื่อถือได้
  2. ส่งข้อมูลไม่ใช้เวลา (Latency is zero) — ส่งข้อมูลไม่ใช้เวลา
  3. Bandwidth ไม่จำกัด (Bandwidth is infinite) — ส่งข้อมูลได้ไม่จำกัด
  4. เครือข่ายปลอดภัย (The network is secure) — network ปลอดภัย
  5. โครงสร้างเครือข่ายไม่เปลี่ยน (Topology doesn't change) — โครงสร้าง network ไม่เปลี่ยน
  6. มี admin คนเดียวคุม (There is one administrator) — มี admin คนเดียวคุม
  7. ส่งข้อมูลฟรี (Transport cost is zero) — ส่งข้อมูลฟรี
  8. เครือข่ายเหมือนกันหมด (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 msmodern DC backbone
Same region (เช่น Singapore → Singapore)1-5 msLAN-ish
US East ↔ Western Europe70-100 ms (best, MAREA/Dunant cable) ถึง ~150 ms (suboptimal route)ขึ้นกับ submarine cable + routing
US ↔ Asia100-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 มาถึง)
  • บทเรียน: ไม่มี "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 ครั้ง:

PercentileLatencyเทียบกับ p50
p50 (median)10 msbaseline
p99100 ms10x ช้ากว่า
p999500 ms50x ช้ากว่า!
max5000 msnetwork hiccup

สาเหตุที่แกว่ง:

  • GC pauseGC (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ใช้แก้ปัญหาอะไรบทไหน
Replicationdata ปลอดภัย + scale readบท 1
Shardingscale write + storageบท 1
ConsensusN nodes agreeบท 2
Leader Election1 node coordinatesบท 2
Heartbeatdetect failureบท 0
Gossipspread info ใน clusterบท 6
Quorummajority decidesบท 1
WAL (Write-Ahead Log)crash recoveryบท 1, 2
2PCatomic across DBsบท 3
Sagadistributed transactionบท 3
Event Sourcingevents not stateบท 3, 5
Outboxatomic DB + eventบท 3, 5
Lamport / Vector Clocklogical orderingบท 4
CRDTconflict-free replicationบท 1, 4, 6
Circuit Breakerprevent 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 overload

Solutions:

  • 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:

  1. ให้ retry เป็นจำนวนครั้งที่เคย retry ไปแล้ว (0, 1, 2, ...)
  2. คำนวณ base = 1000 × 2^retry (ms) — แบบ exponential แต่ cap ไว้ที่ 60,000 ms (60 วินาที)
  3. คืนค่า random ในช่วง [0, base] — กระจาย retry ของแต่ละ client ให้ไม่ตรงกัน
  4. ตัวอย่าง: 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 ค้าง → cascade

Solutions:

  • 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 waves

Analogy: ส่งจดหมาย

  • L7 (Application): เนื้อจดหมายภาษาไทย
  • L6 (TLS): เอาจดหมายใส่ซองล็อค
  • L4 (TCP): เซ็นรับรองว่าส่งครบ ถ้าหายส่งซ้ำ
  • L3 (IP): ที่อยู่ผู้รับ
  • L1 (Physical): รถส่งของ

ทำไมต้องรู้? Debug network issue → "layer ไหนพัง?"

TCP vs UDP — เลือกตาม use case

TCPUDP
ความน่าเชื่อถือ✅ Retransmit lost packets❌ ส่งแล้วลืม
Ordering✅ Ordered❌ มาถึงตามลำดับใดก็ได้
Connection3-way handshakeConnectionless
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 start

HTTP/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 + กระจายโหลด LB

HTTP/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 layer

BFF (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 repo

Mitigation:

  • ✅ 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 interval1-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 = compromise

→ ลึกใน บทที่ 1 + 2

5. 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

#ProblemHardest partบทไหน
1Time + OrderingClock driftบท 4
2ConsistencyCAP trade-offบท 1
3ConsensusFLP impossibilityบท 2
4ReplicationSync vs asyncบท 1, 2
5PartitioningHot keysบท 1, 10
6TransactionsCross-system atomicityบท 3
7Failure DetectionSlow vs deadบท 0, 9
8MembershipGossip/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 Locketcd lease / Redis Redlock / ZooKeeper ephemeral node
Leader Electionetcd / ZooKeeper / Kubernetes Lease
Distributed TransactionSaga via Temporal / Step Functions / 2PC via XA
Event Log + OrderingKafka / Pulsar / Redpanda
Distributed CacheRedis Cluster / Hazelcast / Memcached
Distributed DBCockroachDB, Spanner, Cassandra, DynamoDB
HA PostgresPatroni (uses etcd) / pg_auto_failover
Service DiscoveryK8s Service / Consul / etcd
Distributed TracingOpenTelemetry / 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):

  1. 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"
  2. ZooKeeper ephemeral nodes — similar guarantees, older but mature
  3. K8s Lease object — built-in coordination primitive (uses etcd underneath)
  4. 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 ก่อน production

Source: 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) ต้อง monitor

Source: 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 plane

Source: 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-intensive

Source: 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

AspectSingle-Machine Mental ModelDistributed Mental Model
Function callReturns or throws (2 outcomes)Returns, throws, or timeout (3 outcomes)
SequenceLinear, deterministicEvents at multiple nodes, partial ordering
StateSingle source of truthMultiple views, eventual agreement
StorageDisk = safeNeed replication + backup
Time1 clock, accurateMultiple clocks, drift
FailureException is rareFailure is normal
Latencyns-μs (predictable)ms-sec (variable, tail latency)
TestingUnit tests sufficientNeed 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 — เริ่มต้น

  1. SRE book (ฟรี เน้นภาคปฏิบัติ) → ประมาณ 1 เดือน
  2. Building Microservices → ประมาณ 2 สัปดาห์
  3. อ่านเล่มนี้ให้จบ + ทำ checkpoint ทุกบท → 1-2 เดือน

ระดับ Mid-level — ปูพื้นให้แน่น

  1. DDIA อ่านตั้งแต่ต้นจนจบ → ประมาณ 2 เดือน
  2. Raft paper + Lamport time paper → 1 สัปดาห์ (สองอันรวมกัน)
  3. ลงมือทำ: สร้าง event-driven app ของตัวเอง

ระดับ Senior — เจาะลึก

  1. Database Internals → ประมาณ 2 เดือน
  2. Spanner paper + Dynamo paper → 1 สัปดาห์
  3. ฝึกออกแบบและ operate distributed system จริงใน production
  4. อ่าน engineering blog ของบริษัทที่ scale ใหญ่ ๆ เรื่อย ๆ

ทำไมต้องอ่าน paper ด้วย?

หลายคนคิดว่าอ่านแค่หนังสือพอแล้ว — แต่ paper สำคัญเพราะเหตุผลเหล่านี้:

  1. Code บอกแค่ "ทำอะไร" — paper อธิบาย "ทำไม" เห็นเหตุผลและ trade-off ที่ทีมต้นทางเจอ
  2. มี trade-off และ decision จริง ที่บางทีหายไประหว่างทางในหนังสือ
  3. ฝึก critical thinking — paper บางอันก็มีข้อบกพร่อง อ่านแล้วต้องคิดวิเคราะห์
  4. Senior+ คาดหวังให้อ่าน paper เป็น — เป็นส่วนหนึ่งของ skill
  5. 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จุดเด่นจุดด้อย
etcdGoRaftAPI gRPC, ทันสมัย, K8s ใช้ต้อง operate เอง (หรือ managed)
ZooKeeperJavaZABเก่า มีคนใช้เยอะ (Kafka, Hadoop)Operate ยาก, Java memory hungry
ConsulGoRaftService discovery + KV + DNS + UIFeature เยอะอาจ overkill

เมื่อไหร่ใช้:

  • K8s control plane → etcd (built-in)
  • Service discovery + config → Consul
  • Hadoop/old Kafka stack → ZooKeeper (legacy)

2. Streaming / Event Log — "Log ที่ scale ได้"

Toolจุดเด่นจุดด้อย
KafkaStandard de facto, throughput สูง, ecosystem ใหญ่Operate ยาก, ZooKeeper dependency (เก่า) / KRaft (ใหม่)
PulsarMulti-tenancy, geo-replication built-inคนใช้น้อยกว่า, ซับซ้อน (broker + bookkeeper)
RedpandaKafka API ทั้งหมด, ไม่ต้อง ZK, เร็วกว่า (C++)ใหม่กว่า, community เล็กกว่า
NATS JetStreamLightweight, multi-language SDK ดีThroughput น้อยกว่า Kafka
AWS KinesisManaged, ผูกกับ AWSVendor 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จุดเด่นจุดด้อย
IstioEnvoy (C++)Feature ครบที่สุดLearning curve ชัน, resource เยอะ
LinkerdLinkerd2-proxy (Rust)เบา, ตั้งค่าง่ายFeature น้อยกว่า Istio
Consul ConnectEnvoyIntegration กับ Consul/VaultNiche
Cilium Service MesheBPFไม่ต้อง sidecar (sidecarless)ใหม่, K8s only

เมื่อไหร่ใช้: มี microservices > 20 ตัว และต้องการ mTLS / observability / retry ที่ centralize

5. Stream Processing — "ทำ analytics real-time"

Toolจุดเด่นจุดด้อย
Apache FlinkStateful streaming, exactly-once, low latencyOperate ซับซ้อน
Kafka StreamsLibrary (ไม่ต้อง cluster), Java/Scalaผูกกับ Kafka, JVM only
Apache Spark StreamingDataFrame API, batch + streamLatency สูงกว่า Flink
ksqlDBSQL-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 engineerBuild 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:

  1. Coordinator node รับ request ก่อน (Cassandra ไม่มี master — ทุก node เป็น peer)
  2. Coordinator ส่ง write request ไปยัง 3 node ที่ถือ replica ของข้อมูลนี้
  3. ถ้า 2 ใน 3 node ตอบ ACK → return success ทันที (quorum write)
  4. 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 ตายลง:

  1. ZooKeeper หรือ Kafka Controller (ตัว coordinator) ตรวจจับว่า leader หาย (heartbeat timeout)
  2. เลือก 1 follower ขึ้นมาเป็น leader ใหม่ (มี protocol ภายในจัดการ)
  3. แจ้ง client ทุกตัวว่า partition นี้มี leader ใหม่แล้ว
  4. 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 (ระบบเดี่ยว)

ด้านMonolithDistributedเพิ่มขึ้นกี่เท่า
Infrastructure1 server พอต้องมี service + DB + cache + message queue + LB3-5 เท่า
Debuggingอ่าน log ไฟล์เดียวตามรอย request ข้าม service หลายตัว ต้องใช้ tracing5-10 เท่า
Monitoringwatch CPU/memory/log พอต้องดู service mesh, metrics ข้าม service, distributed trace5+ เท่า
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 อันก็ได้):

วิเคราะห์:

  • สาเหตุที่แท้จริง (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% พร้อมกันไม่ได้

บทถัดไป → Consistency + CAP


🔤 Glossary · 📋 Style guide · 📅 last_verified: 2026-06-03