ข้าม​ไป​ยัง​เนื้อหา

ยอด​ที่​รอด​มา​ถึง​ปลาย​ทาง

เจ็ด​บท​ที่​ผ่าน​มา​วัด​ค่า​ที​ละ​จุด บท​นี้​เอา​จุด​เหล่า​นั้น​มา​ต่อ​กัน​เป็น​ท่อ​เดียว แล้ว​ส่ง​ออ​เด​อร์​ใบ​หนึ่ง เข้าไป​จริง

ออ​เด​อร์​ใบ​นั้น​มี​ยอด​รวม 10 มี​ยอด​คืน​เงิน​ที่​ตาม​มา​ทีหลัง​เป็น 25 และ​มี​รหัส​ลูกค้า​ติดลบ ส่วน orderId ของ​มัน​ยาว​เกิน​ช่วง​ที่​ทศนิยม 64 bit เก็บ​ได้​ตรง​ตัว

body ที่​ท่อ​ทั้ง​สอง​เส้น​รับ
{"orderId": 9007199254740993, "total": 10, "refunded": 25, "customerId": -7}

คำถาม​ของ​บท​นี้​ไม่ใช่​ว่า​ท่อ​เส้น​ไหน​เขียน​สวย​กว่า แต่​เป็น​ว่า ค่าที่​ออก​จาก​ปลาย​ทาง​ของ​สอง​เส้น​นี้ ต่าง​กัน​ตรง​ไหน​บ้าง เมื่อ​อินพุต​เป็น​ใบ​เดียวกัน คำ​ตอบ​วัด​ออก​มา​ได้​ครบ​ทั้ง​แปด​จุด​ที่​วัด

📦 orderctl — ของ​ชิ้น​เดียวกัน​กับ​คอร์ส​ภาษา​อื่น​ใน​เสา​นี้

orderctl คือ​เครื่อง​มือ​ประมวล​ผล​ออ​เด​อร์​ที่​คอร์ส​ภาษา​ใน​เสา​นี้​สร้าง​ซ้ำ​ที​ละ​ภาษา repo ตัวอย่าง กำลัง​จัด​ทำ ระหว่าง​นี้​ทุก​ตัวเลข​ใน​บท​นี้​รัน​เอง​ได้​ด้วย npm run verify:integer-semantics ซึ่ง​เรียก python3 scripts/integer-semantics/probe.py · ไม่​ต้อง​มี Docker และ​ไม่​ต้อง​ต่อ​เน็ต

ท่อ​ทั้ง​สอง​เส้น​อยู่​ใน​โปรแกรม​เดียวกัน และ​อ่าน body จาก​ตัวแปร​ตัว​เดียวกัน

การ​เทียบ​ท่อ​สอง​เส้น​จะ​ไม่มี​ความ​หมาย​เลย ถ้า​ใคร​สัก​คน​เถียง​ได้​ว่า​สอง​เส้น​นั้น​รับ​อินพุต​คนละ​ใบ เงื่อนไข​นี้​จึง​ถูก​บังคับ​ไว้​ที่​ตัว​โปรแกรม ไม่​ได้​ฝาก​ไว้​กับ​วินัย​ของ​คน​รัน

scripts/integer-semantics/runners/pipeline/main.go
body := []byte(os.Args[1])
out := map[string]string{"shardCount": fmt.Sprintf("%d", shardCount)}
naive(body, out)
guarded(body, out)

body มา​จาก​อาร์กิวเมนต์​เดียว​ที่​ส่ง​เข้า​มา​ทาง argv แล้ว​ฟังก์ชัน​ทั้ง​สอง​ตัว​รับ​ตัวแปร​ตัว​เดียวกัน​นั้น ไป​ใช้ ไม่มี​การ​อ่าน​อินพุต​ซ้ำ และ​ไม่มี​บรรทัด​ไหน​ประกอบ​อินพุต​ขึ้น​ใหม่​ก่อน​ถึง​สอง​ฟังก์ชัน​นั้น

runner ไม่​พิมพ์​อินพุต​ของ​ตัว​เอง​ออก​มา​สัก​ใบ body ที่​ยกไว้​ข้าง​บน​จึง​มา​จาก​ช่อง orderBody ของ measured.json ซึ่ง​เป็น​ค่าที่ probe ส่ง​เข้าไป ส่วน body สอง​ใบ​ที่​บท​นี้​ยก​มา​ทีหลัง คือ​ผล​ของ​ขั้น​ที่​สอง ของ​แต่ละ​เส้น ไม่ใช่​อินพุต

ปลาย​ทาง​ของ​ทั้ง​สอง​เส้น​ก็​เป็น​ชุด​เดียวกัน คือ shard จำนวน 8 ใบ ชื่อ shard-0 ถึง shard-7 ซึ่ง​จำนวน​ใบ​นี้​ก็​ออก​มา​เป็น​ค่าที่​วัด​ได้​ด้วย ไม่​ได้​อยู่​แต่​ใน source

สี่​ขั้น​ที่​ทั้ง​สอง​เส้น​เดิน​เหมือน​กัน​คือ decode body · marshal ใหม่​แล้ว​ส่ง​ต่อ · ลบ​ส่วน​คืน​เงิน ออก​จาก​ยอด · แล้ว​เลือก shard จาก customerId ต่าง​กัน​ที่​วิธี​เขียน​แต่ละ​ขั้น​เท่านั้น

สี่​ขั้น​ให้​แปด​แถว เพราะ​บาง​ขั้น​รายงาน​ทั้ง​ค่าที่​ได้ และ​ความ​ล้ม​เหลว​ที่​มัน​พบ · แต่ละ​แถว​ให้​สอง​ค่า

จุด​ที่​วัดท่อ​เปล่าท่อ​ที่​มี​ด่าน
ชนิด​ของ orderId ที่ decode ได้float64json.Number
orderId ที่​อ่าน​ออก​มา9.007199254740992e+159007199254740993
body ที่​ส่ง​ต่อ{"customerId":-7,"orderId":9007199254740992,"refunded":25,"total":10}{"customerId":-7,"orderId":"9007199254740993","refunded":25,"total":10}
ผล​ของ total - refunded4294967281ไม่มี​ค่า​คืน​ออก​มา
ความ​ล้ม​เหลว​ของ​การ​ลบไม่​รายงานrefunded 25 exceeds total 10
index ของ shard-71
shard ที่​ลง​จริงไม่​ได้​ลง​ใบ​ไหน​เลยshard-1
ความ​ล้ม​เหลว​ตอน​อ่าน arrayruntime error: index out of range [-7]ไม่​รายงาน

สอง​แถว​แรก​มา​จาก​ขั้น​ที่​หนึ่ง แถว​ที่​สาม​มา​จาก​ขั้น​ที่​สอง แถว​ที่​สี่​กับ​ห้า​มา​จาก​ขั้น​ที่​สาม และ​สาม แถว​สุดท้าย​มา​จาก​ขั้น​ที่​สี่

ทั้ง​สอง​เส้น​ตอบ​ไม่​ตรง​กัน​ครบ​ทั้ง​สี่​ขั้น ไม่ใช่​ขั้น​ใด​ขั้น​หนึ่ง และ​ไม่มี​ขั้น​ไหน​ที่​ค่า​ของ​สอง​เส้น บังเอิญ​มา​เจอ​กัน​กลาง​ทาง

ท่อ​เปล่า · สี่​ขั้น กับ​เสียง​ที่​ดัง​ครั้ง​เดียว

หัวข้อ​ที่​มีชื่อ​ว่า “ท่อ​เปล่า · สี่​ขั้น กับ​เสียง​ที่​ดัง​ครั้ง​เดียว”

ขั้น​ที่​หนึ่ง​ถึง​สาม​คืน​ค่าที่​หน้าตา​ใช้​งาน​ได้ แล้ว​ขั้น​ที่​สี่​เป็น​ขั้น​เดียว​ที่​รายงาน​ความ​ล้ม​เหลว

json.Unmarshal ที่​ปลาย​ทาง​เป็น map[string]any ไม่​คืน error ออก​มา​เลย และ​ชนิด​ที่ orderId ได้​คือ float64 ตาม​ที่​เอกสาร​ของ encoding/json ระบุ​ไว้​สำหรับ​ตัวเลข​ของ JSON

ค่าที่​อ่าน​ออก​มา​คือ 9.007199254740992e+15 ซึ่ง​เขียน​เป็น​หลัก​ล้วน​ได้​เป็น 9007199254740992 น้อย​กว่า id ที่​ส่ง​มา​อยู่​หนึ่ง นี่​คือ​จุด​ที่​บท​ที่ 5 วัด​ไว้ ย้าย​มา​อยู่​บน​พรมแดน​ของ​การ parse

ขั้น​ที่​สอง · body ที่​ส่ง​ต่อ​ได้​โดย​ไม่มี​ใคร​ปฏิเสธ

หัวข้อ​ที่​มีชื่อ​ว่า “ขั้น​ที่​สอง · body ที่​ส่ง​ต่อ​ได้​โดย​ไม่มี​ใคร​ปฏิเสธ”

gateway อ่าน body ลง map แล้ว Marshal กลับ​ออก​ไป​ข้าง​หน้า สิ่ง​ที่​ออก​มายัง​เป็น JSON ที่​ถูก​ต้อง ทุก​ประการ ทุก​ช่อง​ยัง​อยู่​ครบ และ service ข้าง​หลัง​รับ​มัน​ได้​ตาม​ปกติ

body ที่​ออก​จาก​ท่อ​เปล่า
{"customerId":-7,"orderId":9007199254740992,"refunded":25,"total":10}

total refunded และ customerId เดิน​ผ่าน​มา​ครบ​ทุก​หลัก เพราะ​ทั้ง​สาม​ค่า​อยู่​ใน​ช่วง​ที่​ทศนิยม 64 bit เก็บ​ได้​ตรง​ตัว ช่อง​เดียว​ที่​เปลี่ยน​ค่า​คือ orderId และ​มัน​เปลี่ยน​ไป​แล้ว​ตั้งแต่​ขั้น​ที่​หนึ่ง

ลำดับ​ของ​ช่อง​ใน​บรรทัด​นี้​เรียง​ตาม​ตัว​อักษร ไม่​ได้​เรียง​ตาม​อินพุต เพราะ​เอกสาร​ของ encoding/json ระบุ​ว่า Marshal เรียง​คีย์​ของ map ก่อน​เขียน​ออก​ไป การ​เรียง​ใหม่​นั้น​ไม่ใช่​ค่าที่​เปลี่ยน และ body ของ​ท่อ​ที่​มี​ด่าน​ก็​เรียง​แบบ​เดียวกัน​ด้วย​เหตุผล​เดียวกัน

ยอด​กับ​ส่วน​คืน​เงิน​ถูก​อ่าน​ออก​มา​เป็น float64 แล้ว​แปลง​เป็น uint32 ก่อน​ลบ​กัน ผล​ที่​ได้​คือ 4294967281 และ​ขั้น​นี้​ไม่​รายงาน​ความ​ล้ม​เหลว​อะไร​เลย ช่อง error ของ​มัน​เป็น​สตริง​ว่าง

สเปก​ของ Go เขียน​กฎ​นี้​ไว้​ตรงๆ ที่​หัวข้อ Integer overflow ว่าการ​บวก​ลบ​คูณ​และ​เลื่อน bit ของ ค่า unsigned คิด​แบบ modulo ของ​สอง​ยก​กำลัง​ความ​กว้าง ผลลัพธ์​จึง​เป็น​ค่าที่​ถูก​ต้อง​ตาม​ชนิด

นี่​คือ​จุด​ที่​บท​ที่ 1 กับ​บท​ที่ 2 วัด​ไว้ เดิน​ทาง​มา​ถึง​ยอด​เงิน​จริง ยอด​ที่​ยัง​ต้อง​เก็บ​จา​กอ​อเด​อร์ ใบ​นี้​กลาย​เป็น​สี่​พัน​ล้าน โดย​ไม่มี​บรรทัด​ไหน​ใน​ท่อ​บอก​ว่า​มี​อะไร​ผิด

ขั้น​ที่​สี่ · index ที่​ติดลบ และ​บรรทัด​เดียว​ที่​ดัง

หัวข้อ​ที่​มีชื่อ​ว่า “ขั้น​ที่​สี่ · index ที่​ติดลบ และ​บรรทัด​เดียว​ที่​ดัง”

customerId % 8 ใน Go ให้​เศษ​ที่​ตาม​เครื่องหมาย​ของ​ตัว​ตั้ง ค่าที่​ได้​จึง​เป็น -7 ไม่ใช่​ค่าที่ อยู่​ใน​ช่วง 0 ถึง 7 ของ shard แปด​ใบ

สเปก​ของ Go ที่​หัวข้อ Index expressions ระบุ​ว่า index อยู่​ใน​ช่วง​เมื่อ 0 <= x < len(a) นอกจาก​นั้น​คือ​นอก​ช่วง และ​การ​อ่าน array ด้วย index ที่​นอก​ช่วง​ตอน​รัน ทำให้​เกิด panic

ข้อความ​ที่​ขั้น​ที่​สี่​ของ​ท่อ​เปล่า​คืน​ออก​มา
runtime error: index out of range [-7]

ค่าที่​ขั้น​นี้​คืน​จึง​เป็น​สตริง​ว่าง ไม่มี shard ใบ​ไหน​รับ​ออ​เด​อร์​ใบ​นี้ และ​นี่​คือ​จุด​ที่​บท​ที่ 4 วัด​ไว้ มา​จบ​ที่​ปลาย​ทาง​จริง​ของ​มัน

ใน service จริง panic ตัว​นี้​ฆ่า​คำขอ​ที่​กำลัง​ทำ​อยู่ ส่วน​ใน runner ของ​บท​นี้​มัน​ถูก​ดัก​ไว้​ด้วย recover เพื่อ​ให้​ความ​ล้ม​เหลว​กลาย​เป็น​ค่าที่​วัด​ได้ ไม่ใช่​โปรแกรม​ที่​ตาย​ก่อน​บันทึก​ค่า

ท่อ​ที่​มี​ด่าน · body ใบ​เดิม สี่​ขั้น​ที่​เขียน​ใหม่

หัวข้อ​ที่​มีชื่อ​ว่า “ท่อ​ที่​มี​ด่าน · body ใบ​เดิม สี่​ขั้น​ที่​เขียน​ใหม่”

ด่าน​ทั้ง​สี่​ไม่​ได้​แก้​ค่า​ให้​ถูก มัน​เปลี่ยน​สิ่ง​ที่​ขั้น​นั้น​ยอม​ส่ง​ต่อ​ไป​ข้าง​หน้า

Decoder.UseNumber() สั่ง​ให้​ตัวเลข​ของ JSON ถูก decode เป็น json.Number แทน float64 ชนิด​ที่ orderId ได้​จึง​เป็น json.Number และ​ค่าที่​อ่าน​ออก​มา​คือ 9007199254740993

json.Number เก็บ​ตัวเลข​ไว้​เป็น​ข้อความ​ของ literal เดิม การ​อ่าน​ค่า​กลับ​ออก​มา​เป็น​จำนวนเต็ม​จึง เป็นการ​กระทำ​ที่​ผู้​เรียก​สั่ง​เอง ไม่ใช่​การก​ระ​ทำ​ที่​เกิด​ไป​แล้ว​ตั้งแต่​ตอน decode

ก่อน Marshal ท่อ​เส้น​นี้​คัด​ทุก​ช่อง​ของ map ที่ decode มา​ลง​ใน map ใบ​ใหม่​ก่อน แล้ว​จึง​เขียน​ทับ ช่อง orderId ของใบ​ใหม่ด้วย​ข้อความ​ของ json.Number ตัว​เดิม map ที่ decode มา​ไม่​ถูก​แตะ และ​ขั้น​ที่​สาม​กับ​ขั้น​ที่​สี่​ก็​อ่าน​ค่า​จาก​ใบ​เดิม​นั้น ไม่ใช่​จาก​ใบ​ที่​ส่ง​ออก​ไป

สิ่ง​ที่​ออก​ไป​ข้าง​หน้า​จึง​เป็น​สตริง ไม่ใช่​ตัวเลข​ของ JSON

body ที่​ออก​จาก​ท่อ​ที่​มี​ด่าน
{"customerId":-7,"orderId":"9007199254740993","refunded":25,"total":10}

ทุก​หลัก​ของ id ต้นทาง​ยัง​อยู่​ครบ​ใน body ใบ​นี้ ราคา​ของ​มัน​คือ​รูป​ของ​ช่อง​นั้น​เปลี่ยน​ไป ทุก service ข้าง​หลัง​ที่​ประกาศ orderId เป็น​ตัวเลข​จึง​ต้อง​แก้​ตาม

ยอด​กับ​ส่วน​คืน​เงิน​ใน​ท่อ​เส้น​นี้​เดิน​ทาง​มา​เป็น json.Number แล้ว​ถูก​แปลง​ลง uint32 ก่อน​ลบ ทั้ง​สอง​ค่า​จึง​เป็น​จำนวนเต็ม​ตลอด​ทาง ไม่​ได้​ผ่าน​ทศนิยม​สัก​ขั้น จาก​นั้น​การ​ลบ​ถูก​ห่อ​ไว้​ด้วย​การ​ตรวจ​ก่อน​ลบ

runner ไม่​ได้​ประกาศ​สกุล​เงิน​หรือ​หน่วย​ของ​ยอด​ไว้​ที่ไหน​เลย การ​เก็บ​ยอด​เป็น หน่วย​ย่อย​ที่สุด​ของ​สกุล​เงินminor unitsการ​เก็บ​เงิน​เป็น​จำนวนเต็ม​ของ​หน่วย​ย่อย​ที่สุด แทน​การ​เก็บ​เป็น​ทศนิยม · คอร์ส​นี้​เป็น​เจ้าของ​การ​คำนวณ​บน​ค่า​นั้น ส่วน​ชนิด​ที่​ห่อ​มัน​ไว้​เป็น​ของ​คอร์ส payments-ledger-ddd คือ​เหตุผล​ที่​ระบบ​จริง​เลือก​รูป​จำนวนเต็ม​แบบ​นี้ ไม่ใช่​ค่าที่ probe วัด​ได้

การ​ตรวจ​นั้น​ให้​ผล​แบบ​เดียว​กับ checked arithmetic ที่​บท​ที่ 2 วัด​ไว้ ต่าง​กัน​ที่​ด่าน​นี้​เขียน​ขึ้น​เอง ใน​ท่อ ไม่ใช่​การก​ระ​ทำ​ที่​ภาษา​มี​มา​ให้

ความ​ล้ม​เหลว​ที่​ขั้น​ที่​สาม​ของ​ท่อ​ที่​มี​ด่าน​รายงาน
refunded 25 exceeds total 10

ช่อง​ผลลัพธ์​ของ​ขั้น​นี้​ไม่มี​ค่า​คืน​ออก​มา​เลย สิ่ง​ที่​ผู้​เรียก​ได้​ไป​คือ​ความ​ล้ม​เหลว​ที่​ระบุ​ทั้ง​สอง​ค่า ใน​ข้อความ​เดียว และ​ไม่มี​ตัวเลข​ให้​เผลอ​ส่ง​ต่อ

นิพจน์​ที่​ท่อ​เส้น​นี้​ใช้​คือ​การ​ครอบ​เศษ​ให้​เป็น​บวก​ก่อน​ใช้​งาน ซึ่ง​เป็น​รูป​ที่​บท​ที่ 4 ยกไว้​เป็น​ทาง​แก้ ค่าที่​ได้​จาก customerId ที่​เป็น -7 กับ shard แปด​ใบ จึง​เป็น 1

ขั้น​นี้​คืน shard-1 และ​ไม่​รายงาน​ความ​ล้ม​เหลว ความ​ล้ม​เหลว​ขอ​งอ​อเด​อร์​ใบ​นี้​จึง​มา​จาก​ขั้น​ที่​สาม ขั้น​เดียว ไม่ใช่​จาก​การ​เลือก​ปลาย​ทาง

flowchart TD
  B["body ใบเดียว · orderId 9007199254740993 · total 10 · refunded 25 · customerId -7"]
  B --> N["ท่อเปล่า"]
  B --> G["ท่อที่มีด่าน"]
  N --> N1["ขั้นที่ 1 · float64 · อ่านได้ 9.007199254740992e+15"]
  N1 --> N2["ขั้นที่ 2 · ส่งต่อ orderId เป็นตัวเลข 9007199254740992"]
  N2 --> N3["ขั้นที่ 3 · ลบแล้วได้ 4294967281 · ไม่รายงานความล้มเหลว"]
  N3 --> N4["ขั้นที่ 4 · index -7 · ได้ runtime error แทนที่จะได้ shard"]
  G --> G1["ขั้นที่ 1 · json.Number · อ่านได้ 9007199254740993"]
  G1 --> G2["ขั้นที่ 2 · ส่งต่อ orderId เป็นสตริง 9007199254740993"]
  G2 --> G3["ขั้นที่ 3 · ไม่คืนค่า · รายงานว่า refunded 25 exceeds total 10"]
  G3 --> G4["ขั้นที่ 4 · index 1 · ลงที่ shard-1"]

คำ​บรรยาย​ภาพ: สอง​เส้น​เริ่ม​จาก​กล่อง​เดียวกัน และ​แยก​กัน​ตั้งแต่​ขั้น​แรก · กล่อง​สี​เดียวกัน​ทั้ง​ภาพ เพราะ​ทุก​กล่อง​คือ​ค่าที่​วัด​ได้ ไม่ใช่​ความ​เห็น​ว่า​เส้น​ไหน​ดี​กว่า

สาม​ใน​สี่​ค่า​ของ​ท่อ​เปล่า หน้าตา​เหมือน​ข้อมูล​ปกติ

หัวข้อ​ที่​มีชื่อ​ว่า “สาม​ใน​สี่​ค่า​ของ​ท่อ​เปล่า หน้าตา​เหมือน​ข้อมูล​ปกติ”

เส้น​ที่​มี​ด่าน​ไม่​ได้​ชนะ​เพราะ​ตอบ​ถูก มัน​ชนะ​เพราะ​ไม่​ยอม​ส่ง​คำ​ตอบ​ผิด​ออก​ไป​เงียบๆ

ไล่​ค่าที่​ท่อ​เปล่า​คืน​ออก​มา​ที​ละ​ขั้น แล้ว​ถาม​ว่า​ขั้น​ไหน​บ้าง​ที่​ระบบ​เฝ้า​ระวัง​จะ​เห็น

  • ขั้น​ที่​หนึ่ง คืนตัว​เลข​หนึ่ง​ค่า Unmarshal ไม่​คืน error และ​ชนิด​ที่​ได้​ก็​เป็น​ชนิด​ปกติ
  • ขั้น​ที่​สอง คืน body ที่​เป็น JSON ถูก​ต้อง ทุก​ช่อง​อยู่​ครบ ส่ง​ต่อ​ได้​ทันที
  • ขั้น​ที่​สาม คืน​จำนวนเต็ม​บวก​หนึ่ง​ค่า และ​ช่อง​ความ​ล้ม​เหลว​เป็น​สตริง​ว่าง
  • ขั้น​ที่​สี่ ไม่​คืน​ค่า และ​คืน​ข้อความ​ที่​มี​คำ​ว่า runtime error อยู่​ใน​นั้น

สาม​ขั้น​แรก​ไม่มี​อะไร​ให้ alert ไป​จับ​เลย​สัก​อย่าง ค่าที่​มัน​คืน​คือ​ตัวเลข body และ​ตัวเลข ซึ่ง​เป็น รูป​เดียว​กับ​ที่​ระบบ​รับ​ไว้​เป็น​ปกติ​อยู่​แล้ว

ขั้น​ที่​สี่​เป็น​ขั้น​เดียว​ที่​ใคร​ก็ตาม​จะ​รู้ตัว และ​มัน​เป็น​ขั้น​ที่​ความ​เสีย​หาย​น้อย​ที่สุด​ใน​สี่​ขั้น เพราะ ไม่มี shard ใบ​ไหน​รับ​ออ​เด​อร์​ใบ​นี้​ไว้

ความ​เสีย​หาย​จริง​อยู่​ที่​สาม​ขั้น​แรก คือ id ที่​เปลี่ยน​ไป​หนึ่ง body ที่​พา id ใหม่​นั้น​ต่อ​ไป​ทั้ง​ระบบ และ​ยอด​สี่​พัน​ล้าน​ที่​ถูก​ส่ง​ไป​เป็น​ยอด​ที่​ยัง​ต้อง​เก็บ

ราคา​ของ​ท่อ​ที่​มี​ด่าน และ​สิ่ง​ที่​บท​นี้​ไม่​ได้​วัด

ด่าน​ทั้ง​สี่​ไม่​ได้​ทำให้​ยอด​ขอ​งอ​อเด​อร์​ใบ​นี้​คำนวณ​ออก​มา​ได้ ขั้น​ที่​สาม​ไม่มี​ค่า​คืน​ออก​มา​เลย เพราะ ส่วน​คืน​เงิน​มากกว่า​ยอด​จริงๆ สิ่ง​ที่​ด่าน​เปลี่ยน​คือ​ความ​ล้ม​เหลว​นั้น​กลาย​เป็น​ข้อความ​ที่​อ่าน​ได้ แทนที่​จะ​เป็น​ตัวเลข

ราคา​ที่​วัด​ได้​ใน​บท​นี้​มี​สอง​ข้อ ทั้ง​สอง​ข้อ​อยู่​ใน​ตาราง​แปด​แถว​ข้าง​บน

  • รูป​ของ orderId ใน body ที่​ส่ง​ต่อ​เปลี่ยน​จาก​ตัวเลข​เป็น​สตริง ทุก service ที่​รับ body ใบ​นี้​ต้อง​แก้​ตาม และ​การ​แก้​นั้น​ต้อง​ลง​พร้อม​กัน​ทั้ง​ระบบ
  • ขั้น​ที่​สาม​ไม่มี​ตัวเลข​ให้​ถือ​ต่อ ผู้​เรียก​ต้อง​เขียน​ทาง​เดิน​ของ​ความ​ล้ม​เหลว​เอง ไม่ใช่​รับ​ค่า มา​แล้วไป​ต่อ​เหมือน​เดิม

probe ของ​คอร์ส​นี้​ไม่​ได้​วัด​เวลา ไม่​ได้​วัด​ปริมาณ​งาน​ต่อ​วินาที และ​ไม่​ได้​วัด​ต้นทุน​ของ​ด่าน​เหล่า​นี้ สัก​ข้อ บท​นี้​จึง​ไม่​บอก​ว่า​ด่าน​ถูก​หรือ​แพง มัน​บอก​เฉพาะ​ค่าที่​ออก​จาก​ปลาย​ทาง​ของ​สอง​เส้น

บท​นี้​ไม่​ได้​เป็น​เจ้าของ​จุด​ไหน มัน​คือ​ที่​ที่​จุด​ทั้ง​ห้า​ถูก​วาง​ต่อ​กัน​เป็น​ท่อ​เดียว

ธี​สิ​ส​ของ​คอร์ส​นี้​เป็น​ประโยค​เดียว คือ ค่า​จำนวนเต็ม​ค่า​หนึ่ง​เปลี่ยน​ได้​ห้า​จุด​ระหว่าง​ทาง​ผ่าน​ระบบ และ​ทุก​จุด​ใน​ห้า​จุด​นั้น​เงียบ เจ็ด​บท​ก่อน​หน้า​วัด​ครบ​ทั้ง​ห้า​จุด​แล้ว

จุดเรื่องบท
1ความ​กว้าง​หมดบท​ที่ 1 และ​บท​ที่ 2
2ค่าสัมบูรณ์​กับ​ความ​ไม่​สมมาตร​ของ two’s complementบท​ที่ 3
3การ​หาร​กับ​เศษ​ของ​จำนวน​ติดลบบท​ที่ 4
4ทศนิยม​กลืน​จำนวนเต็มบท​ที่ 5
5พรมแดน​ของ​การ parseบท​ที่ 6 และ​บท​ที่ 7

ท่อ​ใน​บท​นี้​เดิน​ผ่าน​สี่​จุด​ใน​ห้า​จุด​นั้น และ​แต่ละ​จุด​โผล่​ที่​ขั้น​ของ​มัน​เอง

  • ขั้น​ที่​หนึ่ง คือ​จุด​ที่ 5 กับ​จุด​ที่ 4 มา​เจอ​กัน​บรรทัด​เดียว ชนิด​ปลาย​ทาง​ที่ parse ได้​เป็น float64 คือ​สิ่ง​ที่​ทำให้​ทศนิยม​กลืน id ไป​หนึ่ง
  • ขั้น​ที่​สาม คือ​จุด​ที่ 1 ความ​กว้าง​ของ uint32 หมด​ลง แล้ว​ค่าว​นก​ลับ​เป็น 4294967281
  • ขั้น​ที่​สี่ คือ​จุด​ที่ 3 เศษ​ของ​จำนวน​ติดลบ​ตาม​เครื่องหมาย​ของ​ตัว​ตั้ง แล้ว index ออก​นอก​ช่วง

จุด​ที่ 2 ไม่มี​ที่​นั่ง​ใน​ท่อ​นี้ เพราะ​ท่อ​นี้​ไม่​ได้​หา​ค่าสัมบูรณ์​ของ​อะไร​เลย​สัก​ขั้น การ​เขียน​ว่า​มัน​โผล่ ที่​นี่​ด้วย​จะ​เป็นการ​อ้าง​ค่าที่ probe ไม่​ได้​วัด

สิ่ง​ที่​ทั้ง​แปดบท​ชี้​ไป​ที่​เดียวกัน​คือ ตัว​ตัดสิน​ว่า​ค่า​จะ​เปลี่ยน​หรือ​ไม่ ไม่ใช่​ชื่อ​ภาษา แต่​เป็น​ชนิด ปลาย​ทาง​ที่​คุณ​ประกาศ กับ​ด่าน​ที่​คุณ​เขียน​หรือ​ไม่​ได้​เขียน​ไว้​ตรง​นั้น


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้
  • Go package documentation — encoding/json (เข้า​ถึง 2026-09-13) — Unmarshal ระบุ​ว่า​เมื่อ​ปลาย​ทาง​เป็น interface value มัน​เก็บ float64, for JSON numbers ลงไป · Decoder.UseNumber ระบุ​ว่า​มัน​ทำให้ decoder unmarshal a number into an interface value as a Number instead of as a float64 · Number ระบุ​ไว้​ว่า A Number represents a JSON number literal · และ​หน้า​เดียวกัน​ระบุ​เรื่อง การ​เขียน map กลับ​ออก​ไป​ว่า The map keys are sorted and used as JSON object keys

  • The Go Programming Language Specification — Index expressions (เข้า​ถึง 2026-09-13) — ระบุ​ว่า index x อยู่​ใน​ช่วง​เมื่อ 0 <= x < len(a) นอกจาก​นั้น คือ​นอก​ช่วง และ​สำหรับ array ระบุ​ว่า if x is out of range at run time, a run-time panic occurs ซึ่ง​เป็น​ที่มา​ของ​ข้อความ​ที่​ขั้น​ที่​สี่​ของ​ท่อ​เปล่า​คืน​ออก​มา

  • The Go Programming Language Specification — Integer overflow (เข้า​ถึง 2026-09-13) — ระบุ​ว่า​สำหรับ​ค่า unsigned การก​ระ​ทำ + - * และ << are computed modulo 2n, where n is the bit width of the unsigned integer’s type ซึ่ง​เป็น​เหตุผล​ที่ 10 - 25 แบบ uint32 ให้ 4294967281 โดย​ไม่มี​ความ​ล้ม​เหลว​ใด​ออก​มา

  • ตัวเลข​ทุก​ตัว​ใน​บท​นี้​มา​จาก scripts/integer-semantics/measured.json ช่อง pipeline ซึ่ง scripts/integer-semantics/probe.py เขียน​ขึ้น​หลัง​ยิง​ทั้ง​ชุด​สาม​รอบ​ต่อ​การ​เรียก​หนึ่ง​ครั้ง · source ของ​ท่อ​ทั้ง​สอง​เส้น​อยู่​ที่ scripts/integer-semantics/runners/pipeline/main.go · ช่อง pipeline กับ runner ของ​มัน​ลง repo ใน​คอม​มิต​เดียวกัน วัด​บน go1.27.0 บน darwin/arm64 เมื่อ 2026-09-13

เช็กความเข้าใจ — บทที่ 8

ข้อ 1 / 3

ท่อเปล่าเดินครบทั้งสี่ขั้นบน body ใบนี้ แล้วมีกี่ขั้นที่รายงานความล้มเหลวออกมา