Time & Global States
ย้อนกลับไปบทที่ 1: ระบบกระจายไม่มีนาฬิกากลาง — แล้วจะรู้ได้อย่างไรว่าเหตุการณ์ A เกิดก่อนเหตุการณ์ B? บทนี้ตอบคำถามนี้ด้วยแนวคิดที่เปลี่ยนมุมมองต่อ "เวลา" ไปตลอดกาลสำหรับวิศวกรระบบกระจาย
1Physical Clock: ทำไมนาฬิกาเครื่องจริงเชื่อถือไม่ได้เต็มร้อย
| วิธี Synchronize | แนวคิด |
|---|---|
| Cristian's Algorithm | Client ขอเวลาจาก time server ที่เชื่อถือได้ (เช่นอ้างอิง atomic clock) แล้วปรับตามค่าประมาณ round-trip time — ใช้ได้ในเครือข่ายที่ latency ค่อนข้างสมมาตรและคาดเดาได้ |
| Berkeley Algorithm | ไม่มี time server ที่ "ถูกต้องแน่นอน" — master รวบรวมเวลาจากทุกเครื่องในกลุ่ม เฉลี่ยแล้วบอกทุกเครื่องให้ปรับเข้าหาค่าเฉลี่ยนั้น เหมาะกับกลุ่มเครื่องที่ไม่มีการเชื่อมต่อไปยัง external time source |
| NTP (Network Time Protocol) | โครงสร้างลำดับชั้น (stratum) ตั้งแต่นาฬิกาอะตอมมิก/GPS (stratum 0) ลงมาจนถึงเครื่องผู้ใช้ทั่วไป ใช้กันอย่างแพร่หลายในอินเทอร์เน็ตจริง |
2Logical Time: Lamport Clock
| กรณี | สรุปว่า → เป็นจริง |
|---|---|
| เหตุการณ์ในกระบวนการ (process) เดียวกัน | ถ้า a เกิดก่อน b ในลำดับการทำงานของ process เดียวกัน a → b |
| การส่งและรับข้อความ | ถ้า a คือการส่งข้อความ และ b คือการรับข้อความเดียวกันนั้น a → b เสมอ |
| Transitivity | ถ้า a → b และ b → c แล้ว a → c |
เพื่อให้ได้ total ordering (ทุกเหตุการณ์เทียบกันได้หมดแม้ concurrent) มักเสริมด้วยการเทียบ process ID เป็น tie-breaker เมื่อค่า Lamport timestamp เท่ากัน
3Vector Clock: แก้ข้อจำกัดของ Lamport Clock
เหตุการณ์ a → b (จริง ๆ) ก็ต่อเมื่อทุกตำแหน่งของเวกเตอร์ a ≤ เวกเตอร์ b และมีอย่างน้อยหนึ่งตำแหน่งที่น้อยกว่าจริง — ถ้าเทียบกันแล้วไม่มีทิศทางไหนน้อยกว่าล้วน ๆ แปลว่าสอง event นั้น concurrent จริง ๆ (ไม่ใช่แค่ดูเหมือน concurrent แบบที่ Lamport clock บอกไม่ได้)
4Global State และ Snapshot Algorithm
Chandy-Lamport Snapshot Algorithm
1. Process ใดก็ได้เริ่มต้น: บันทึก state ของตัวเอง แล้วส่ง marker message
ออกไปยังทุก outgoing channel
2. เมื่อ process อื่นได้รับ marker เป็นครั้งแรก:
- บันทึก state ของตัวเองทันที
- บันทึกว่า channel ที่ marker มาถึงนั้น "ว่างเปล่า" (ไม่มี message ค้าง)
- ส่ง marker ต่อไปยังทุก outgoing channel ของตัวเอง
3. เมื่อได้รับ marker ซ้ำ (จาก channel อื่น) หลังจากบันทึก state แล้ว:
- บันทึก message ทั้งหมดที่มาถึงบน channel นั้นก่อน marker มาถึง
(คือ message ที่ "ค้างอยู่ระหว่างทาง" ตอนที่ snapshot เริ่ม)
4. เมื่อทุก process บันทึก state และทุก channel บันทึก message ค้างครบแล้ว
-> รวมกันได้ global snapshot ที่ consistent
5ทันสมัย: จาก Vector Clock สู่ Distributed Tracing และ Hybrid Logical Clock
| เทคโนโลยี | ความสัมพันธ์กับทฤษฎีในบทนี้ |
|---|---|
| Correlation ID (Network Computing หัวข้อที่ 3) | เป็นรูปแบบที่เรียบง่ายกว่า Lamport/Vector Clock มาก — ใส่ ID เดียวกันตลอดสายของ request แทนที่จะพยายามเรียงลำดับ event ทั้งระบบ พอเพียงสำหรับการ "ค้นหา log ที่เกี่ยวข้องกัน" แต่ไม่ได้บอก causality ที่ละเอียดเท่า vector clock |
| Distributed Tracing (Network Computing หัวข้อที่ 4) | เก็บ parent-child relationship ระหว่าง span (แต่ละ span คือหนึ่ง operation ในหนึ่ง service) ซึ่งจริง ๆ คือการสร้าง happened-before graph บางส่วนโดยตรง — เพียงแต่ใช้เพื่อ debug/monitor แทนการพิสูจน์ทฤษฎี |
| Hybrid Logical Clock (HLC) | ผสม physical clock (สำหรับให้ค่าใกล้เคียงเวลาจริงที่มนุษย์เข้าใจได้) เข้ากับ logical clock (สำหรับรับประกัน causality ที่ถูกต้อง) — ใช้จริงในฐานข้อมูลกระจายอย่าง CockroachDB และ MongoDB |
| Google Spanner TrueTime | แนวทางที่ต่างออกไป — ลงทุนฮาร์ดแวร์ (GPS + atomic clock) ในทุก datacenter เพื่อให้ physical clock มีขอบเขตความคลาดเคลื่อนที่รู้แน่ชัด (bounded uncertainty) แทนที่จะหลีกเลี่ยงการพึ่งพา physical clock ไปเลยแบบ Lamport — เป็นการแก้ปัญหาเดิมจากอีกมุมหนึ่งโดยสิ้นเชิง |
- อธิบาย happened-before relation พร้อมยกตัวอย่างสถานการณ์ของตัวเองที่แสดง 3 กรณี (process เดียวกัน, ส่ง-รับข้อความ, transitivity)
- อธิบายว่าทำไม Lamport(a) < Lamport(b) จึงไม่ได้แปลว่า a happened-before b เสมอไป และ vector clock แก้ข้อจำกัดนี้อย่างไร
- อธิบายขั้นตอนของ Chandy-Lamport snapshot algorithm และเหตุผลที่มันรับประกัน consistent global state ได้โดยไม่ต้องหยุดระบบ
- เปรียบเทียบแนวทางของ Vector Clock กับ Google Spanner TrueTime ว่าแก้ปัญหา "ไม่มีนาฬิกากลาง" จากคนละมุมกันอย่างไร
- อธิบายว่า Correlation ID ที่ใช้ใน microservices จริงเป็นรูปแบบที่ "เรียบง่ายกว่า" Vector Clock อย่างไร และยอมสูญเสียความสามารถอะไรไปเพื่อแลกกับความเรียบง่ายนั้น
6เวลาในระบบกระจายมีอย่างน้อยสามความหมาย
คำว่าเวลาในโปรแกรมมักถูกใช้รวมกัน ทั้งเวลาบนปฏิทิน ลำดับเหตุการณ์ และระยะเวลาที่ใช้ ทั้งสามเกี่ยวข้องกันแต่แทนกันไม่ได้ Wall-clock time ใช้ตอบว่าเกิดเมื่อวันใด Monotonic time ใช้วัดระยะเวลาโดยไม่ควรถอยหลังเมื่อปรับนาฬิกา ส่วน Logical time ใช้อธิบายลำดับ/เหตุและผลโดยไม่พยายามบอกเวลาโลกจริง
| ชนิด | ตอบคำถาม | ตัวอย่าง API/แนวคิด | ข้อควรระวัง |
|---|---|---|---|
| Wall clock | เหตุการณ์เกิดเวลาใดตามปฏิทิน | UTC timestamp, NTP | กระโดด/ถอยได้จาก clock correction |
| Monotonic clock | ผ่านไปนานเท่าใดในเครื่องเดียว | monotonic timer | เทียบข้ามเครื่องโดยตรงไม่ได้ |
| Logical clock | เหตุการณ์ใดอาจมีเหตุเป็นผลต่อกัน | Lamport, vector clock | ไม่บอกวินาทีจริง |
| Hybrid clock | เก็บความใกล้ physical time พร้อม causal order | HLC | ยังต้องกำหนด skew/error bound |
ทำไมควรใช้ UTC แต่ UTC ก็ยังไม่พอ
UTC ลดความสับสน timezone และ daylight saving แต่ไม่ได้ทำให้นาฬิกาทุกเครื่องตรงกัน NTP/PTP ลด offset ภายใต้ข้อจำกัด แต่มี error bound และช่วงปรับเวลา การเก็บ timestamp ควรระบุ timezone/precision และงานด้าน ordering ต้องไม่สมมุติว่า timestamp ต่าง 1 ms แปลว่าเหตุการณ์เกิดตามลำดับนั้นแน่นอน
Leap Second และ Clock Adjustment
นาฬิกาอาจ step กระโดดทันทีหรือ slew ปรับอัตราทีละน้อย ระบบบางแห่งใช้ leap smear กระจายวินาทีเพิ่ม ถ้าเอา wall clock คำนวณ duration อาจได้ค่าติดลบหรือยาวผิด ควรใช้ monotonic clock สำหรับ timeout/latency และ wall clock สำหรับ audit/display พร้อมเก็บ uncertainty เมื่อจำเป็น
7Clock Synchronization: ตรงแค่ไหนจึงเรียกว่าตรง
นาฬิกา hardware มี drift เพราะ oscillator ต่างกัน ถ้าไม่ sync offset จะโตตามเวลา Protocol แลก timestamp เพื่อประมาณ offset แต่ network delay ไป/กลับไม่เท่ากัน ทำให้แยกไม่ได้สมบูรณ์ว่าเวลาต่างเพราะนาฬิกาหรือข้อความช้า
| วิธี | บริบท | ระดับโดยคร่าว | ข้อจำกัด |
|---|---|---|---|
| NTP | เครือข่ายทั่วไป/อินเทอร์เน็ต | ms ขึ้นกับ network | asymmetric delay และ server hierarchy |
| PTP | LAN/อุตสาหกรรมพร้อม hardware support | µs หรือต่ำกว่า | ต้องควบคุม network/hardware |
| GPS/atomic source | ระบบเฉพาะทาง | แม่นสูง | ราคา สัญญาณ และ trust/spoofing |
External กับ Internal Synchronization
External synchronization ถามว่านาฬิกาแต่ละเครื่องใกล้แหล่ง UTC เท่าใด Internal synchronization ถามว่านาฬิกาในกลุ่มต่างกันเองเท่าใด ระบบบางงานไม่สนใจเวลาจริงแต่ต้องให้สมาชิกใกล้กัน ขณะที่ audit ทางกฎหมายอาจต้อง external accuracy ด้วย
TrueTime และ Interval Time
แทนที่จะอ้างเวลาจุดเดียว ระบบอย่าง Spanner ใช้ช่วง [earliest, latest] ที่มั่นใจว่าเวลาจริงอยู่ภายใน แล้วรอ uncertainty ผ่านก่อนประกาศ commit order บทเรียนไม่ใช่ทุกระบบควรสร้าง atomic clock แต่คือความซื่อสัตย์ต่อ uncertainty: ถ้ารู้เวลาได้เพียงช่วง ควร model เป็นช่วง ไม่แกล้งใช้เลขจุดเดียวแล้วรับประกันเกินหลักฐาน
8Happened-Before: ลำดับที่มาจากข้อมูลไหล
Lamport นิยาม a → b ถ้า a และ b อยู่ process เดียวและ a มาก่อน b, หรือ a เป็น send และ b เป็น receive ของข้อความเดียวกัน, หรือเชื่อมต่อแบบ transitive หากไม่มี a→b หรือ b→a เหตุการณ์ concurrent กัน หมายถึงไม่มี causal evidence ระหว่างกัน ไม่ได้แปลว่าเกิดเวลาเดียวกัน
P1: a ---- send(m) ---------------- c
\
P2: receive(m) ---- d
a → send(m) → receive(m) → d
Causality มาจากความเป็นไปได้ที่ข้อมูลจากเหตุการณ์หนึ่งมีอิทธิพลต่ออีกเหตุการณ์ ถ้าสอง process ไม่แลกข้อความ ไม่มีทางที่เหตุการณ์ล่าสุดของฝ่ายหนึ่งจะรู้เหตุการณ์อีกฝ่ายในขณะนั้น ต่อให้ wall timestamp ใกล้กันก็ไม่สร้าง causal relation
Causality ไม่เท่ากับ Business Causation
Happened-before บอก potential information flow ไม่ได้พิสูจน์สาเหตุทางธุรกิจ ถ้า request A มาก่อน B ใน process เดียว เราได้ order แต่ไม่ได้แปลว่า A ทำให้ B เกิด ในทางกลับกัน business relation อาจถูกส่งผ่านช่องทางที่ tracing ไม่เห็น ต้องระวังใช้คำ causal เกินความหมายทางเทคนิค
9Lamport Clock แบบทำทีละเหตุการณ์
- แต่ละ process มี counter เริ่ม 0
- ก่อน local/send event เพิ่ม counter
- แนบ counter กับ message
- เมื่อรับ timestamp T ตั้ง clock = max(local,T)+1
กฎทำให้ถ้า a→b แล้ว L(a)<L(b) แต่ทิศกลับไม่จริง: L(a)<L(b) ไม่พิสูจน์ a→b เหตุการณ์ concurrent อาจได้เลขเรียงกันเพราะ counter คนละ process การใช้ Lamport timestamp ร่วม process ID สร้าง total order ได้เพื่อ deterministic tie-break แต่ total order ที่สร้างเพิ่มไม่ใช่ causal fact ทั้งหมด
| สิ่งที่ Lamport clock ให้ | สิ่งที่ไม่ให้ |
|---|---|
| รักษา happened-before direction | ตรวจ concurrency จากเลขสองตัวไม่ได้ |
| สร้าง consistent total order เมื่อ tie-break | ไม่บอกเวลาจริงหรือระยะเวลา |
| ใช้ mutual exclusion/event ordering | ไม่รู้ว่ามี process ใดเห็น event แล้วบ้าง |
Lamport Mutual Exclusion
Process multicast request พร้อม timestamp ทุกคนจัด queue ตาม (timestamp, process ID) และตอบ acknowledgment ผู้ขอเข้า critical section เมื่อ request ตนอยู่หัว queue และได้รับ reply ครบ วิธีนี้แสดงว่า logical order ใช้สร้าง mutual exclusion ได้ แต่มี message cost และสมาชิกใดล้มอาจ block หากไม่มี membership/failure handling
10Vector Clock: เก็บว่าแต่ละ Process รู้ถึงไหน
Vector clock มีหนึ่ง component ต่อ process Local event เพิ่มช่องตน Send แนบ vector Receive ทำ component-wise max แล้วเพิ่มช่องตน ถ้า V(a)<V(b) ทุก component ≤ และอย่างน้อยหนึ่ง < จึงสรุป a→b หาก vector เปรียบเทียบไม่ได้ เหตุการณ์ concurrent
P1 vector [2,0,0] ส่ง m P2 ก่อนรับ [0,3,0] P2 รับ: max([0,3,0],[2,0,0]) = [2,3,0], แล้วเพิ่ม → [2,4,0]
| ความสัมพันธ์ | เงื่อนไข | ความหมาย |
|---|---|---|
| V(a) < V(b) | ทุกช่องไม่เกิน และมีช่องน้อยกว่า | a happened-before b |
| V(a) || V(b) | บางช่องมาก บางช่องน้อย | concurrent |
| เท่ากัน | ทุก component เท่ากัน | knowledge frontier เดียวกันใน model |
ต้นทุนเมื่อ Membership ใหญ่/เปลี่ยน
Vector ยาวตามจำนวน process ถ้าสมาชิกนับล้านหรือ churn สูง metadata แพง จึงมี version vector ต่อ replica, dotted version vector, interval tree clock หรือ tracking เฉพาะ actor สำคัญ ระบบจริงอาจยอม causal information บางส่วนเพื่อขนาดเล็กลง
Conflict ใน Dynamo-style Store
ถ้า version vector สองค่า concurrent ระบบรู้ว่าไม่มีอันใดสืบทอดอีกอัน จึงเก็บ siblings แล้ว merge ด้วย application เช่น shopping cart union การใช้ last-write-wins จาก wall clock ง่ายกว่าแต่เสี่ยงทิ้ง update จาก clock skew ส่วน CRDT ออกแบบ operation/state ให้ merge ตามคณิตศาสตร์โดยไม่ต้องเลือกผู้ชนะ แต่จำกัดชนิด semantics
11Total Order, Causal Order และ Delivery Order
ระบบอาจต้องการ ordering ต่างระดับ Chat comment/reply ต้องให้ reply ไม่มาก่อนต้นเหตุ (causal) แต่ comment อิสระสองรายการไม่จำเป็นต้องเรียงเหมือนกันทั่วโลก Replicated state machine ต้อง total order ของ command เพื่อ replica ให้ state เหมือนกัน การใช้ guarantee เข้มเกินงานเพิ่ม coordination โดยไม่จำเป็น
| Order | รับประกัน | ต้นทุน/ข้อใช้ |
|---|---|---|
| Per-sender FIFO | ข้อความจากผู้ส่งเดียวไม่สลับ | ง่ายแต่หลาย sender ยังต่างกัน |
| Causal | เหตุและผลถึงตามลำดับ | metadata/dependency tracking |
| Total | ทุกสมาชิกเห็นลำดับเดียว | sequencer/consensus และ latency |
| Real-time/linearizable | operation หลังอีกอันจบต้องอยู่หลัง | ต้อง coordination/clock assumption เพิ่ม |
12Global State: ภาพรวมที่ถ่ายพร้อมกันไม่ได้
Global state ประกอบด้วย local state ของทุก process และ state ของ channel (ข้อความที่ส่งแล้วแต่ยังไม่รับ) การอ่านทุก process ทีละตัวอาจได้ inconsistent cut เช่นบันทึกฝั่งรับหลังได้เงิน แต่บันทึกฝั่งส่งก่อนหักเงิน ทำให้ยอดรวมเหมือนเงินเกิดขึ้น หรือกลับกันหายไป
Consistent Cut
Cut สอดคล้องถ้ารวม event e แล้วต้องรวม causal predecessor ของ e ทั้งหมด ห้ามมี receive โดยไม่มี send อยู่ในอดีตของ snapshot แต่มี send ที่ receive ยังไม่เกิดได้ ซึ่งข้อความนั้นอยู่ใน channel state
13Chandy–Lamport Snapshot แบบเข้าใจ Marker
สมมุติ reliable FIFO channel Process เริ่ม snapshot โดยบันทึก local state แล้วส่ง marker ทุก outgoing channel เมื่อ process ได้ marker แรก บันทึก local state ส่ง marker ต่อ และถือข้อความที่มาถึง channel อื่นตั้งแต่บันทึก stateจนพบ marker ของ channel นั้นเป็น channel state
- Initiator บันทึก local state
- ส่ง marker ก่อนข้อความปกติถัดไปทุก outgoing channel
- ผู้รับ marker แรกบันทึก local state และ channel ที่ marker มาถึงเป็นว่าง
- บันทึกข้อความบน channel อื่นจน marker ของ channel นั้นมาถึง
- รวม local + channel state เป็น snapshot
FIFO สำคัญเพราะ marker แยกข้อความก่อน/หลัง snapshot บน channel หาก channel ไม่ FIFO ต้องเพิ่ม sequence/color หรือ algorithm อื่น Snapshot ไม่หยุด application และไม่ได้เป็น state ณ wall-clock เดียว แต่มันเป็น consistent global state ที่อาจเกิดขึ้นได้ตาม execution
Snapshot ใช้ทำอะไร
| งาน | สิ่งที่ snapshot ช่วย |
|---|---|
| Checkpoint | restart จาก state ที่สอดคล้อง ไม่สร้าง orphan message |
| Deadlock detection | ดู wait-for relation ระดับระบบ |
| Termination detection | ทุก process idle และไม่มี message in transit |
| Invariant/debug | ตรวจ property ของ global state |
| Stream processing | consistent checkpoint ของ operator/channel |
14Distributed Tracing: เวลาเพื่ออธิบาย Request หนึ่งสาย
Tracing สร้าง trace ID และ span ID ต่อ operation บันทึก parent/child, start/end, attribute และ status ทำให้ตาม request ข้าม service ได้ มันตอบว่าเส้นทางและ latency อยู่ที่ไหน แต่ timestamp skew ทำให้ span จากคนละเครื่องดูซ้อนหรือกลับลำดับ จึงควรใช้ causal parent relation และ monotonic duration ร่วม ไม่ดู timeline ด้วยตาอย่างเดียว
| ข้อมูล | ตอบอะไร | ข้อจำกัด |
|---|---|---|
| Trace/span IDs | operation ใดเป็นเรื่องเดียวกัน | context อาจขาดที่ queue/async boundary |
| Parent/link | causal/fan-out relation | batch มีหลาย parent ต้องใช้ links |
| Timestamp/duration | ใช้เวลาที่จุดใด | clock skew และ sampling |
| Attributes/events | endpoint, retry, state สำคัญ | privacy/cardinality และ overhead |
Async Messaging กับ Trace Context
Producer span จบก่อน consumer เริ่มนานมาก การใช้ parent-child อาจทำให้ trace ยาวและ ownership สับสน OpenTelemetry ใช้ span links เชื่อม event จาก producer กับ consumer โดยไม่แกล้งว่าเป็น call stack เดียว Message ต้องส่ง trace context แต่ต้องกำหนด policy เมื่อ replay event เก่าหรือ fan-in หลาย message
15Hybrid Logical Clock และฐานข้อมูลกระจาย
HLC เก็บ physical component ร่วม logical counter หาก wall time เดินหน้าใช้ค่าใหม่ หากรับ timestamp มากกว่าหรือเวลาไม่เดิน counter ช่วยรักษา causality จึงมี timestamp ใกล้เวลาโลกจริงและไม่ถอยตาม causal order เหมาะกับ version/order ใน distributed database บางชนิด
HLC ไม่ทำ clock sync หายไป Physical component ยังมี skew และงานที่ต้อง external time accuracy ต้องใช้กลไกอื่น แต่ช่วยลด metadata เทียบ vector clock และให้ sort/debug ใกล้เวลาจริงกว่า Lamport ล้วน แลกกับตรวจ concurrency ไม่สมบูรณ์แบบ vector
16เวลาที่เกี่ยวกับ Lease, Lock และ Expiry
Lease ให้สิทธิ์ช่วงเวลาจำกัด ใช้ leader, cache validity หรือ resource ownership แต่ถ้าผู้ถือกับผู้ให้เช่าเข้าใจเวลาต่างกัน อาจมีผู้ถือสองคน ต้องมี clock bound หรือ fencing token ที่เพิ่มทุก lease ผู้รับ resource ปฏิเสธ token เก่าแม้ process เก่ายังคิดว่า lease ไม่หมด
Lease #41 → fencing token 41 Lease #42 → fencing token 42 Storage accepts write only if token >= last_seen
TTL expiry ก็ไม่ควรใช้ลบ business fact โดยไม่คิด เช่น session หมดอายุใช้ได้ แต่ order ไม่ควรหายเพราะนาฬิกากระโดด งาน scheduler ต้องออกแบบ duplicate/missed execution เมื่อ leader failover และ clock skew
17ข้อผิดพลาดเรื่องเวลาที่พบในระบบจริง
- ใช้ wall clock วัด duration แล้วได้ค่าติดลบหลัง NTP step
- ใช้ timestamp เป็น unique ID แล้วชนเมื่อหลายเครื่อง/clock rollback
- ใช้ last-write-wins โดยไม่ยอมรับว่า clock skew ทำ update ใหม่หาย
- เรียง log จาก timestamp อย่างเดียวแล้วสรุป causal chain ผิด
- ตั้ง token expiry แต่ไม่เผื่อ skew ระหว่าง issuer/verifier
- ใช้ local timezone ใน storage ทำ DST ซ้ำ/ขาด
- ตั้ง cron บนหลาย replica แล้วงานเกิดซ้ำโดยไม่มี leader/idempotency
- ถือว่า timeout คือหลักฐานว่า remote operation ไม่เกิด
- ใช้ timestamp จาก client ที่ไม่น่าเชื่อถือเป็นลำดับธุรกรรม
18Workshop: สร้าง Timeline ที่โกหก แล้วแก้ด้วย Causality
- รัน process สามตัว แต่ละตัวมี clock offset ต่างกันและแลก message
- เก็บ wall timestamp แล้วเรียง log แสดงลำดับที่ผิด causal relation
- เพิ่ม Lamport clock ตรวจว่า send<receive เสมอ
- เพิ่ม vector clock สร้างเหตุการณ์ concurrent แล้วพิสูจน์ว่าเปรียบเทียบไม่ได้
- จำลอง request fan-out/fan-in สร้าง trace spans และ links
- ทำ snapshot algorithm บน channel FIFO บันทึก message in transit
- เปลี่ยน channel ให้ reorder แล้วอธิบาย assumption ที่พัง
- ใช้ monotonic กับ wall clock วัด duration เปรียบเทียบเมื่อปรับเวลา
19ทบทวนแกนสำคัญก่อนกรณีศึกษา
ไม่มีนาฬิกากลางไม่ได้หมายความว่าเราใช้เวลาไม่ได้ แต่ต้องเลือกเวลาให้ตรงคำถาม Wall clock ใช้ปฏิทิน Monotonic ใช้วัดระยะ Logical clock ใช้ ordering/causality และ hybrid clock ประนีประนอมบางด้าน การ sync ลด error แต่ไม่ทำให้ timestamp เป็นความจริงสมบูรณ์
Lamport clock รักษาทิศ happened-before แต่ตรวจ concurrency ไม่ได้ Vector clock ตรวจ causality ได้ละเอียดขึ้นแต่ metadata โต Global snapshot ไม่หยุดเวลาทั้งระบบ แต่สร้าง consistent cut ที่รวม message in transit ขณะที่ tracing เป็นการประยุกต์ context/causal structureเพื่อ debug request จริง
ขั้นตอนถัดไปคือ Distributed File Systems ซึ่งต้องใช้ naming, caching, replication และเวลา/version ร่วมกัน เมื่อ client หลายเครื่องเปิดชื่อไฟล์เดียว เราต้องกำหนดว่าเห็นข้อมูลเมื่อใด cache เก่าได้นานเท่าใด และ server fail/restart แล้ว file handle กับ write จะมีความหมายอย่างไร
20กรณีศึกษา: เรียง Log ของเหตุขัดข้องข้าม Service
สมมุติ Gateway เวลาเร็วจริง 800 ms, Order ตรง และ Payment ช้า 500 ms ผู้ใช้ส่ง request ผ่าน Gateway→Order→Payment แล้ว Payment timeout การเรียง log ตาม timestamp อาจแสดง Payment เริ่มก่อน Gateway รับ ทั้งที่เป็นไปไม่ได้จาก message flow หากทีมเชื่อ timestamp อย่างเดียวจะสร้าง root cause timeline ผิด
| หลักฐาน | ใช้จัดลำดับอย่างไร | ความเชื่อมั่น |
|---|---|---|
| Trace parent/child | child context มาจาก parent จึงมี causal relation | สูงเมื่อ context ไม่ขาด |
| Send/receive log + request ID | send เกิดก่อน receive ของ message | สูงถ้า ID จับคู่ถูก |
| Local monotonic sequence | event ใน process เดียวตามลำดับ | สูงภายใน process incarnation |
| Wall timestamp | ประมาณเวลาโลกจริง | ขึ้นกับ sync/uncertainty |
| User report | ขอบเขตโดยคร่าว | มี delay/ความจำ |
Incident timeline ควรแยก observed timestamp, causal order และ inference เช่น “Payment span เป็นลูก Order จึงเกิดหลัง Order ส่ง call; timestamp ที่ดูย้อนเป็น clock offset” ไม่ควรแก้ timestamp ใน log ให้สวยโดยไม่เก็บ raw value และ correction method
Clock Offset Estimation จาก Trace
เราประมาณ offset บางส่วนจาก constraint ว่า client send ≤ server receive ≤ server send ≤ client receive แต่ network delay ไม่รู้แน่ จึงได้ช่วงความเป็นไปได้มากกว่าค่าจุดเดียว Tool อาจปรับ visualization แต่ไม่ควรใช้ผลประมาณเป็นหลักฐานทางบัญชีโดยไม่มี clock service ที่รับประกัน
21กรณีศึกษา: Shopping Cart ที่แก้แบบ Offline
โทรศัพท์ A และ B แก้ cart เดียวกันขณะ offline A เพิ่มหนังสือ B เพิ่มปากกา เมื่อ sync ทั้งสอง update มี vector clock concurrent ถ้าใช้ last-write-wins จะทิ้งรายการหนึ่งตาม timestamp หาก cart semantics เป็น set/multiset เรา merge การเพิ่มได้ แต่ remove กับ add item เดียวกันต้องกำหนด policy เช่น observed-remove set
| Conflict | นโยบายที่เป็นไปได้ | ผลทางธุรกิจ |
|---|---|---|
| Add คนละ item | union | รักษาทั้งสองเจตนา |
| Add กับ remove item เดียว | add-wins/remove-wins ตาม type | ต้องเลือกว่าพลาดซื้อหรือพลาดลบอันตรายกว่า |
| เปลี่ยน quantity พร้อมกัน | counter CRDT หรือ manual rule | ค่าบวก/ตั้งค่าใหม่ semantics ต่าง |
| Checkout กับ edit | state/version boundary | ห้าม merge เข้า order ที่ commit แล้วเงียบ ๆ |
Vector clock ตรวจว่า update concurrent แต่ไม่ได้บอกวิธี merge การแก้ conflict เป็น business semantics CRDT ช่วยเมื่อ operation มี algebra ที่ merge ได้ แต่ payment/order invariant ยังต้อง coordination บางจุด
22กรณีศึกษา: Leader Lease และ Process ที่ฟื้นจาก Pause
Leader A ได้ lease ถึงเวลา T แล้วถูก GC pause ขณะเดียวกัน lease หมดและ B ได้ lease ใหม่ A กลับมาทำงานโดย local clock/ความเชื่อเดิมและเขียน storage หาก storageรับทั้งคู่เกิด split-brain Fencing token แก้โดย B ได้ token 42 หลัง A token 41 Storage ปฏิเสธ write 41 แม้ A ยังมีชีวิต
บทเรียนคือการหมดเวลาเปลี่ยนสิทธิ์ในระบบหนึ่ง แต่ไม่ได้หยุด process เก่าทางกายภาพ ต้องมี downstream enforcement ไม่ใช่หวังว่า old leaderจะรับ cancellation ทัน เวลาและ ordering จึงเชื่อมกับ authorization ต่อ resource
23Snapshot ใน Stream Processing
Pipeline มี source, operator state และ channel records ถ้า checkpoint operator แต่ไม่ประสาน offset/message in transit หลัง restart อาจทำข้อมูลหายหรือซ้ำ Barrier snapshot คล้าย marker ไหลตาม stream Operator บันทึก state เมื่อ barrier ครบ input และจัดการ record ระหว่าง barrier ตาม aligned/unaligned checkpoint
| แบบ | พฤติกรรม | ข้อแลกเปลี่ยน |
|---|---|---|
| Aligned | หยุด input ที่ barrier ถึง รอ input อื่น | state เล็กกว่าแต่ backpressure ทำ checkpoint ช้า |
| Unaligned | รวม in-flight buffer ใน checkpoint | checkpoint เร็วช่วง backpressureแต่ใหญ่ |
Exactly-once state ใน engine ยังไม่แปลว่า external sink effect exactly-once ถ้าเขียน database/API ต้องใช้ transactional sink, idempotency หรือ 2PC/outbox ตามขอบเขต นี่เชื่อมบท Time/Global State กลับ Distributed Transactions โดยตรง
24เวลาใน Database: MVCC, Commit Timestamp และ Snapshot
MVCC ใช้ transaction ID/timestamp เลือก version ที่มองเห็น Snapshot timestamp ไม่จำเป็นต้องเป็น wall time และ commit order อาจต่างจาก start order ฐานข้อมูลกระจายต้องกำหนด timestamp oracle, HLC หรือ consensus order เพื่อให้ replica/shard ตีความเหมือนกัน
| Guarantee | ความสัมพันธ์เวลา |
|---|---|
| Snapshot Isolation | อ่านภาพ ณ logical snapshot; concurrent write conflict บางชนิด |
| Serializability | มี serial order ที่ผลเทียบเท่า |
| Strict Serializability | serial order เคารพ real-time completion |
| External Consistency | commit timestamp/order สอดคล้อง observation ภายนอกตาม contract |
คำว่า “ล่าสุด” ต้องถามว่าล่าสุดตาม replica, commit order, wall time หรือ session ของ client Read-your-writes อาจพอสำหรับ user profile แม้ไม่ linearizable ทั่วโลก การรับประกันให้เข้มสุดทุก read เพิ่ม latency/availability cost
25Bounded Staleness และเวลาใน Consistency Model
ระบบอาจรับประกัน replica ตามหลังไม่เกิน K version หรือ Δ เวลา แต่ guarantee ต้องมี mechanism วัด/บังคับ ถ้า clock uncertaintyสูง “ไม่เกิน 5 วินาที” ต้องนิยามจาก commit timestamp/replication watermark ไม่ใช่เวลาเครื่องอ่านอย่างเดียว
Client ที่ต้องข้อมูลสดอาจรอ safe time/watermark ส่วนงาน analytics ยอม stale เพื่ออ่าน replica ใกล้ การเปิด consistency เป็น option ให้ clientช่วย trade-off แต่ API ต้องอธิบายผล ไม่ใช่ใช้ชื่อ eventual/strong กว้าง ๆ
26Time-based ID: สะดวกแต่มีสมมติฐาน
Snowflake-style ID รวม timestamp, worker ID และ sequence เพื่อ sortable/unique โดยไม่ใช้ coordinatorทุกครั้ง แต่ต้องจัด worker ID, รับมือ clock rollback และ sequence overflow หากนาฬิกาถอย generator อาจรอ ใช้ logical bump หรือ fail ไม่ควรสร้าง ID ซ้ำ
| คุณสมบัติ | ประโยชน์ | ข้อควรระวัง |
|---|---|---|
| Time sortable | index locality/ประมาณเวลา | ไม่ใช่ causal/commit order สมบูรณ์ |
| Decentralized | สร้างที่หลาย worker | worker ID uniqueness และ lifecycle |
| Timestamp embedded | debug/partition | privacy และ clock rollback |
27แบบฝึกคำนวณ Logical Clock
กำหนด P1, P2, P3 มีเหตุการณ์ local และ message ต่อไปนี้ ให้นักศึกษาคำนวณ Lamport/vector clock ทุกจุด แล้วตอบว่าเหตุการณ์คู่ใด concurrent
P1: a --send m1---------------- d --recv m3
\ /
P2: recv m1--b--send m2----/
\
P3: c-------------recv m2--send m3
- เริ่ม vector [0,0,0] เพิ่มช่องตนทุก event
- คำนวณ timestamp ของ a,b,c,d และ send/receive
- ตรวจ a กับ c, b กับ c, d กับ receive m2
- สร้าง Lamport total order ด้วย process ID แล้วระบุคู่ที่ order นี้สร้างเพิ่ม
- ถ้า m2 มาถึงช้าหลัง local event เพิ่มหนึ่งจุด vector เปลี่ยนอย่างไร
28Checklist การใช้เวลาในระบบจริง
- ใช้ monotonic clock สำหรับ duration, retry delay และ timeout
- เก็บ wall timestamp เป็น UTC พร้อม precision/แหล่งเวลา
- ไม่ใช้ client wall clock เป็น authority ของ ordering/security โดยไม่ตรวจ
- ระบุ maximum clock skew/uncertainty ถ้า algorithm ต้องพึ่ง
- แยก event time, processing time และ ingestion time ใน pipeline
- กำหนด behavior เมื่อเวลาถอย/กระโดดและทดสอบจริง
- ส่ง trace context/causal metadata ข้าม queue ไม่เฉพาะ HTTP
- ใช้ fencing token กับ lease ที่ควบคุม resource สำคัญ
- ไม่สรุปเหตุการณ์ข้ามเครื่องจาก log timestamp อย่างเดียว
- กำหนด retention/late-arrival window สำหรับ event ที่มาช้า
- monitor NTP offset, sync state และ clock anomaly
- เขียน consistency contract ของคำว่า latest, stale และ snapshot
29คำถามทบทวนเชิงเหตุผล
- เหตุใด clock ที่ตรงระดับ millisecond ยังพิสูจน์ causal order ของ event ห่าง 2 ms ไม่ได้เสมอ
- Lamport clock ให้ implication ทิศใด และเหตุใด converse ไม่จริง
- Vector clock บอก conflict ได้ แต่เหตุใดแก้ conflict แทน business logic ไม่ได้
- Snapshot ที่ไม่ได้ถ่าย wall time เดียวกันยังถือว่าสอดคล้องได้อย่างไร
- Marker ใน Chandy–Lamport ใช้ FIFO assumption ตรงไหน
- Tracing timeline ผิดจาก skew ได้ แต่ parent relation ยังช่วยอะไร
- Lease หมดแล้วเหตุใด process เก่ายังอันตราย และ fencing แก้อย่างไร
- HLC แลกข้อดี/ข้อจำกัดอะไรเทียบ vector clock
- event time กับ processing time ต่างกันอย่างไรเมื่อข้อมูลมาช้า
- strict serializability เพิ่มเงื่อนไขใดเหนือ serializability
30Event Time, Processing Time และ Watermark
ใน stream system เหตุการณ์อาจเกิดที่อุปกรณ์เวลา 10:00 เดินทางออฟไลน์แล้วถึงระบบ 10:30 Event time คือเวลาที่เหตุการณ์เกิดตาม domain Ingestion time คือเวลาเข้าระบบ และ Processing time คือเวลาที่ operator ประมวลผล ถ้า aggregate หน้าต่างตาม processing time ข้อมูลช้าจะไปอยู่คนละช่วงกับเหตุการณ์จริง
Watermark เป็นข้อสันนิษฐานว่าระบบไม่น่าจะได้รับ event time ต่ำกว่าค่านี้อีก ช่วยปิด window แต่ไม่ใช่ความจริงสมบูรณ์ Late event ยังมาได้ ต้องมี allowed lateness และ policy: update result, ส่ง correction, side output หรือทิ้งพร้อม metric
| แนวคิด | ใช้ตอบ | ความเสี่ยง |
|---|---|---|
| Event time | โลกธุรกิจเกิดเมื่อใด | source clock ผิด/ไม่น่าเชื่อถือ |
| Processing time | ระบบเห็น/ทำงานเมื่อใด | ผลเปลี่ยนตาม load/replay |
| Watermark | ปิด window เมื่อใด | เร็วเกินทำ late มาก ช้าเกิน result latency สูง |
| Allowed lateness | แก้ผลย้อนหลังนานเท่าใด | state retention/correction complexity |
31Causal Consistency ใน Application
ผู้ใช้โพสต์ข้อความแล้วเปิดหน้าใหม่ควรเห็นโพสต์ตน (read-your-writes) ผู้ใช้อ่านโพสต์แล้วตอบ reply คนอื่นควรไม่เห็น reply ก่อนต้นเรื่อง (writes-follow-reads/causal) Guarantee แบบ session/causal อาจให้ประสบการณ์สมเหตุผลโดยไม่ต้อง linearizable ทุก operation ทั่วโลก
| Session guarantee | ความหมาย |
|---|---|
| Read-your-writes | หลังเขียน session เดิมไม่ย้อนเห็นรุ่นก่อน |
| Monotonic reads | การอ่านถัดไปไม่ถอยไป version เก่ากว่าที่เคยเห็น |
| Monotonic writes | write จาก session ถูกนำไปใช้ตามลำดับ |
| Writes-follow-reads | write ที่เกิดหลังอ่านถูกวางหลัง version ที่อ่าน |
ระบบทำได้ด้วย session token/version vector/sticky region หรือรอ replica catch up การ route ไป replica ใกล้ที่สุดอย่างเดียวอาจละเมิด monotonic read หาก replica ใหม่ตามหลัง
32Global Predicate และ Stable Property
บาง property เป็น stable: เมื่อจริงแล้วจะจริงต่อ เช่น computation terminated ใน model ที่ไม่มีงานใหม่ บาง property transient เช่น token อยู่ที่ process ใด Snapshot/detection algorithm ต้องรู้ชนิด property เพราะ snapshot หนึ่งไม่พบ transient state ไม่ได้พิสูจน์ว่าไม่เคยเกิด
Distributed deadlock detection ต้องรวม wait relation และ message in transit หาก snapshot ไม่ consistent อาจเห็นวงจรปลอมหรือพลาดวงจรจริง การตรวจ invariant ระดับระบบจึงพึ่ง global state construction ไม่ใช่ query dashboard ณ timestamp ใกล้กันอย่างเดียว
33คำศัพท์และประโยคที่ควรใช้ให้แม่น
| คำ | ความหมาย |
|---|---|
| Concurrent events | ไม่มี happened-before ทั้งสองทิศ ไม่ได้แปลว่า timestamp เท่ากัน |
| Clock skew | ความต่างของนาฬิกาสองเรือน ณ เวลาใด |
| Clock drift | อัตราที่นาฬิกาเดินต่างจาก reference/กันเอง |
| Consistent cut | ถ้ารวม event ต้องรวม causal past ของมัน |
| Channel state | message ส่งแล้วแต่ยังไม่รับ ณ snapshot |
| Total order | ทุกคู่เปรียบเทียบได้ อาจเป็น order ที่สร้างเพิ่ม ไม่ใช่ causality |
| Uncertainty interval | ช่วงที่เวลาแท้จริงน่าจะอยู่ แทนความมั่นใจเกินจริงของเลขจุดเดียว |
สถานการณ์สรุป
ระบบขายบัตรมี event Reserve, Pay และ Confirm ข้ามสาม service ถ้า Confirm มี wall timestamp ก่อน Pay เพราะ skew แต่ trace แสดง Confirm ได้ context หลัง Pay เราควรรักษา causal order ไม่แก้ business stateตาม timestamp หากสอง Reserve ของที่นั่งเดียว concurrent vector clock ช่วยบอก conflict แต่การเลือกผู้ได้ที่นั่งยังต้อง serializable/consensus policy ไม่ใช่ clock merge อย่างเดียว
นี่คือขอบเขตสำคัญของบท: เวลาและ causality ช่วยอธิบาย/จัดลำดับข้อมูล แต่ไม่ได้แทน concurrency control หรือ agreement เมื่อ invariant ต้องมีผู้ชนะเพียงคนเดียว
34Design Review: ทุกครั้งที่เห็น Timestamp ให้ถามหน้าที่ของมัน
Timestamp field หนึ่งอาจถูกใช้แสดงเวลา เรียงข้อมูล หมดอายุ เลือกผู้ชนะ และสร้าง ID พร้อมกัน ซึ่งแต่ละหน้าที่มี requirement ต่างกัน Review ควรทำ inventory ว่า field มาจาก clock ใคร เชื่อถือได้แค่ไหน ถอยได้หรือไม่ และอะไรเกิดเมื่อสองค่าเท่ากัน
| ใช้เพื่อ | สิ่งที่ควรใช้/กำหนด | กับดัก |
|---|---|---|
| แสดงผู้ใช้ | UTC storage + timezone presentation | DST/local ambiguity |
| วัด latency | monotonic duration ต่อ process/span | ลบ wall clock ข้ามเครื่อง |
| เรียง causal event | logical/trace relation | wall clock skew |
| หมดอายุ token | trusted time + skew allowance | client clock เป็น authority |
| เลือก version | commit/logical order ตาม consistency | last-write-wins ทำ update หาย |
| สร้าง unique ID | worker+sequence+rollback policy | timestamp ล้วนชน/ถอย |
ระบบต้องมี behavior เมื่อ time service เสื่อม เช่นหยุดออก lease ใหม่ ลด guarantee หรือใช้ logical counterต่อ ไม่ควรปล่อยให้นาฬิกาผิดแล้วสร้างผลเงียบ ๆ Alert clock offset เป็นเพียงจุดเริ่ม ต้องรู้ว่า component ใดพึ่ง bound และผลกระทบต่อ safety หรือเพียงความสวยของ log
สำหรับ snapshot/tracing ให้เก็บ causal metadata เป็น first-class ไม่หวังแก้ทุกอย่างภายหลังด้วยการ sync clock ที่แม่นขึ้น เพราะแม้นาฬิกาตรง การเห็น event A ก่อน B ทางเวลาก็ยังไม่พิสูจน์ว่า A ส่งผลต่อ B หากไม่มีช่องข้อมูลเชื่อมกัน
เกณฑ์ว่าการใช้เวลาปลอดภัยหรือยัง
ต้องทดสอบ clock ถอย กระโดด และ skew เกิน bound พร้อมตรวจ component ที่ใช้ lease, token expiry, scheduler, ID และ ordering แยกกัน การแก้ log ให้เรียงสวยไม่เท่ากับแก้ algorithm ที่พึ่งเวลาผิด ต้องมี fallback เมื่อ time source unhealthy และ alert ที่บอกผลกระทบต่อ guarantee
Trace/query tooling ควรค้นด้วย ID และ causal relationได้แม้ timestamp คลาด ถ้าการสืบ incident ทำได้เพียงเรียงข้อความทั้งหมดตาม wall time ระบบยังขาดข้อมูลสำคัญ Message/event ต้องพา context ข้าม asynchronous boundary และบันทึก process incarnation เพื่อไม่ให้ sequence หลัง restartปนกับก่อน restart
เมื่อออกแบบ global snapshot ให้เขียน channel assumption, membership และสิ่งที่เกิดกับ message ระหว่าง checkpoint ชัด หากเปลี่ยนจาก FIFO เป็น broker partition/retry semantics ต้องทบทวน algorithm ไม่ถือว่าชื่อ queue เหมือนกันแล้ว assumption เหมือนเดิม
ท้ายที่สุด ความแม่นของนาฬิกาไม่ควรถูกใช้แทนความชัดของ semantics หากงานต้องมีผู้ชนะหนึ่งคนให้ใช้ coordination หากต้องรักษาเหตุและผลให้ส่ง causal metadata และหากเพียงวัดเวลาให้ใช้ monotonic clock เครื่องมือแต่ละชนิดตอบคำถามต่างกัน การเลือกให้ตรงคำถามช่วยลดทั้ง bug และการรับประกันเกินจริง
ในรายงานการทดลองควรระบุ clock source, offset ที่สังเกต, logical timestamp และ message relation แยกกัน เมื่อค่าขัดแย้งให้ถือ causal evidence สำหรับ ordering ที่เกี่ยวกับ execution และเก็บ wall timeไว้เป็นข้อมูลประมาณโลกจริง วิธีนี้ไม่ทำให้เวลาสมบูรณ์ แต่ทำให้ข้อสรุปซื่อสัตย์ต่อสิ่งที่ระบบรู้ได้
35สรุปและขั้นตอนถัดไป
เวลาในระบบกระจายต้องแยก wall clock, monotonic duration และ logical order ออกจากกัน Clock synchronization ลด offset แต่ไม่ลบ uncertainty Lamport clock รักษาทิศ happened-before ส่วน vector clock ตรวจ concurrency ได้ด้วยต้นทุน metadata ที่สูงขึ้น
Global snapshot ไม่ได้หยุดทุกเครื่องในขณะเดียวกัน แต่เลือก consistent cut ที่รวม causal past และ message in transit Distributed tracing นำแนวคิด context/causalityมาใช้ติดตาม request จริง ขณะที่ lease, fencing และ database timestamp แสดงว่าเวลาเชื่อมกับสิทธิ์และ consistency โดยตรง
ขั้นตอนถัดไปคือ Distributed File Systems ซึ่งต้องใช้ naming เพื่อหาไฟล์ ใช้เวลา/version เพื่อจัดการ cache และใช้ snapshot/replication เพื่อกู้ข้อมูล เมื่อหลาย client อ่านเขียน object เดียวกัน คำถามสำคัญจะเปลี่ยนจาก “เหตุการณ์ใดเกิดก่อน” เป็น “write ใดมองเห็นและ durable เมื่อใด”