Messages & Fields
กายวิภาคของ message
หัวข้อที่มีชื่อว่า “กายวิภาคของ message”message คือกลุ่มของ field ที่มี type และมีชื่อ แต่ละ field มีสามส่วน: type, ชื่อ และ field number
syntax = "proto3";package user.v1;
message User { int64 id = 1; string name = 2; string email = 3; bool verified = 4;}ชื่อ (name, email) มีไว้ให้คนอ่าน .proto และให้ generated code ใช้ ส่วน number (= 1, = 2) คือสิ่งที่เดินทางบน wire จริง ๆ การแยกสองอย่างนี้คือสิ่งสำคัญที่สุดที่ต้องเข้าใจเกี่ยวกับ protobuf
wire เห็นแค่ number
หัวข้อที่มีชื่อว่า “wire เห็นแค่ number”เวลา protobuf encode message แต่ละ field จะกลายเป็น tag — คือ field number รวมกับ wire type — ตามด้วย value ส่วนชื่อ field ไม่เคยออกไปจาก .proto เลย
flowchart LR field["name = "Ada" (field #2)"] --> tag["tag: field 2, type length-delimited"] tag --> val["value: 3 bytes 'Ada'"] val --> bytes["บน wire: รวมไม่กี่ byte"]
สิ่งนี้ให้ผลตอบแทนใหญ่สองอย่าง:
- กะทัดรัด string
"verified"ไม่เคยเดินทาง มีแต่ number4ที่เดินทาง message เล็กอยู่เสมอไม่ว่าชื่อ field จะยาวแค่ไหน - rename ได้ฟรี เปลี่ยน
nameเป็นfull_nameใน.protoแล้วทุก client ยังคุยกันได้ — เพราะ wire ถือแค่ field2เท่านั้น ชื่อคือ documentation ส่วน number คือ contract
field number ไม่ได้สุ่มเลือก
หัวข้อที่มีชื่อว่า “field number ไม่ได้สุ่มเลือก”คุณเป็นคนเลือก number เอง และการเลือกมีผลทั้งเรื่อง performance และความปลอดภัย
- 1–15 encode เป็น byte เดียว (tag = number + wire type แพ็กรวมกัน) เก็บไว้ให้ field ที่มีอยู่ใน เกือบทุก message
- 16–2047 ใช้สอง byte เหมาะกับ field ที่พบไม่บ่อย
- 19000–19999 ถูก reserve โดยตัว protobuf เอง — ใช้ไม่ได้
- ค่าสูงสุดคือ 536,870,911 (2^29 − 1) ซึ่งคุณไม่มีทางใช้ถึง
ห้าม reuse number — ให้ reserve แทน
หัวข้อที่มีชื่อว่า “ห้าม reuse number — ให้ reserve แทน”กฎหนึ่งข้อที่กัน bug ร้ายแรงที่สุดของ protobuf: เมื่อลบ field ห้ามให้ number ของตัวเองถูก reuse เด็ดขาด ถ้า client เก่ายังส่ง field 3 เป็น string แต่ server ใหม่เอา 3 ไปใช้เป็น int64 bytes จะถูกตีความผิด — เป็น failure ที่เงียบและทำให้ข้อมูลเสีย
protobuf ให้ reserved ไว้ล็อก number ที่ปลดระวางแล้ว (และชื่อเก่าของตัวเองด้วย) เพื่อให้ compiler ปฏิเสธการ reuse:
message User { reserved 3; // field number เดิมของ 'email' — ห้าม reuse reserved "email"; // และชื่อเดิม เพื่อดักความผิดพลาด
int64 id = 1; string name = 2; bool verified = 4;}ตอนนี้ใครก็ตามที่พยายามเพิ่ม field 3 ใหม่จะเจอ compile error แทนที่จะเป็น incident บน production เราจะกลับมาลงลึกเรื่องวินัยนี้ในบท Evolving Schemas — แต่ให้เริ่มนิสัยตั้งแต่ตอนนี้: ลบ field แล้ว reserve number ของตัวเอง