2020: What does not kill me makes me stronger

ปีนี้รู้สึกว่าไม่ค่อยมีอะไรหวือวาในชีวิต นอกจากว่าจะมี COVID-19 แล้วงานยังเยอะขึ้นมากๆ จนครึ่งปีหลังแทบจะไม่ได้สนใจอะไรเลย

Planned for 2020

ปีนี้ตอนแรกแพลนจะไปญี่ปุ่นอีก อยากไปกินราเมง !!

ว่ากันตามตรงคือไม่ได้ตามไอดอลแล้ว โดยเฉพาะเมื่อเคยากิกลายเป็นซากุระซากะก็ออกจากวงการนี้อย่างเป็นทางการได้แล้ว อาจจะยังตามผลงานยุยปงอยู่บ้างแค่นั้น

ปัญหาที่ตามมาคือมันเลยไม่รู้ว่าไปเที่ยวทำไม แต่ช่วงปลายๆ ปีที่แล้วก็เลยเริ่มคิดว่าไปกินราเมงก็ดีนะ อยากกินแล้วก็อยากรู้ว่าถ้ากินราเมงทุกมื้อจะเบื่อเมื่อไร

ตอนนี้ไม่ต้องไปพอจะเดาได้ว่าไม่เกิน 3 มื้อน่าจะเบื่อ โดยเฉพาะว่าเราไม่ได้ชอบราเมงเกือบทุกประเภท มีแค่บางประเภทเท่านั้นแหละที่ชอบมากๆ แต่ช่วงกลางปีก็เจอว่าน่าจะลองพวก AirBNB experience ซึ่งเดี๋ยวนี้มีขายกันหลายเจ้ามาก ไอเดียคือเหมือนทัวร์แต่ไม่ต้องมาเป็นแพคเกจ

นอกจากญี่ปุ่นอยากลองไปยุโรปบ้าง ยังหาเหตุผลอยู่แต่ก็ไม่แน่ใจ ทั้งว่าไม่เคยไป มันน่าจะแพง แล้วก็ยังไม่มีแรงบันดาลใจพอ

Work

ปีนี้บริษัทมีข่าวใหญ่ๆ หลายข่าว งานที่ทำก็เริ่มเปลี่ยน priority มากขึ้น

อย่างแรกคือเค้าให้เป็น Senior แล้วซึ่งกลายเป็น level รองสุดท้ายในทีมแล้ว ก็ยังมองไม่ออกว่า career path จะขึ้นไปต่อยังไงแต่รู้สึกว่าตอนนี้ก็อยู่ที่ตำแหน่งที่เข้าใกล้ Peter Principle แล้ว คือถ้า promote ไปอีกก็คงทำงานได้ไม่ดีเท่าไร ประมาณนี้แหละกำลังดี

ช่วงครึ่งหลังของปีทีมใหญ่ขึ้นเป็นเท่าตัว priority ของงานเลยเปลี่ยนไป ความรับผิดชอบใหม่ที่มาคือตรวจงานคนอื่นๆ ด้วย ก็ต้องเจียดเวลาจากที่นั่งทำงานตัวเองไปดูคนอื่นบ้างแต่ก็ยังรู้สึกว่างานพวกตรวจต่างๆ ไม่ว่า design หรือ code ยังทำได้ไม่ค่อยดีเท่าไร

อีกอย่างที่ตามมากับขนาดของบริษัทที่ใหญ่ขึ้นคือบริษัทเป็น enterprise แล้ว และก็ตามมาด้วย policy เยอะแยะ ตอนทำงานมาปีกว่าๆ คนถามเยอะว่าทำไมยังไม่ย้ายไปไหน ตอนนั้นบอกว่าอยากเห็นว่า team size ใหญ่ขึ้นเรื่อยๆ แบบนี้แต่เราใช้วิธีทำงานแบบ startup แล้วมันจะไปจบตอนไหนแล้วจบยังไง ตอนนั้นก็ยังมองไม่เห็นตอนจบหรอกว่าจะมายังไง แต่ปรากฏว่าปีนี้ตอนจบมันมาแล้ว มันเร็วมาก และมันไม่สนุกเลย

ปีนี้ครึ่งปีหลังทำ hiring น้อยลง รู้สึกว่าปีนี้มีแต่ bad hire เกือบทั้งหมด นึกถึงโควทของ Steve Jobs ว่า “คนระดับ A จะเลือกคนระดับ A มาทำงาน แต่คนระดับ B จะเลือกคนระดับ C มาทำงาน แล้ว C ก็จ้าง D ไม่นานมันก็ไปถึง Z” คือบางคนอาจจะเคยรู้ว่าทีมเราหาคนเข้ามาหลายปีแล้วได้อยู่คนเดียว เพราะเราเอาแต่ A จริงๆ ซึ่งก็มีคนบอกเยอะมากว่าไม่มีหรอก แต่ครึ่งหลังของปีมาพบว่าเนื่องจากงานเยอะมากๆ และคนขาด บริษัทยอมให้ convert senior software engineer มาช่วยงาน system admin ชั่วคราวแล้วเค้าทำได้ดีมากๆ เรียกว่าดีกว่า candidate SRE ทุกคนที่เคยสัมภาษณ์มาเลยก็ว่าได้ (แต่ก็ให้ได้แค่ชั่วคราว…) ก็เป็นจริงอย่างที่ Google บอกในหนังสือ แต่ว่าคงเอาไปใช้ทำ hiring ไม่ได้

แต่การทำงานในทีมที่เกือบทุกคนเป็น A player นี่เสพติดมากบอกได้เลย มันทำให้เกิด trust ว่าเค้าได้งานไปแล้วเค้าจะจัดการให้มันเสร็จได้เรียบร้อยโดยไม่ต้องตรวจงานเค้าอีก และไม่ต้องคอย mentor ด้วย

ทั้งหมดนี้เลยเป็นเหตุผลว่ายังไม่ค่อยโอเคเวลาใครชวนไปไหน (แต่ปีนี้ค่อนข้างเงียบนะ น่าจะเพราะ COVID แล้วคน freeze hiring ด้วยส่วนหนึ่ง อีกส่วนหนึ่งคือไม่ค่อยได้ออกไปงานไหนด้วย)

Project ใหญ่ๆ ปีนี้ที่ดูคือ PDPA, Infra แล้วก็ Security

PDPA

PDPA นี่รู้สึกว่าดีที่บริษัทยอมเปลี่ยน ลด data ที่มองเห็นให้น้อยลง คือกฎหมายมันออกมาดักพวก UX & marketing ที่อยากเห็นลูกค้าทุกอย่าง (ถ้าไปยืนแอบมองมือถือทุกคนได้ก็คงทำ) ก็กลายเป็นว่าต้อง opt in แทน แต่เอาจริงๆ รู้สึกว่าบริษัทในไทยส่วนมากที่เห็น implement แล้วก็ยังทำไม่ถูกเจตนารมณ์ของกฎหมายเท่าไรนัก (นอกจากกลุ่มธนาคารที่ทำได้ค่อนข้างดี แต่ดัก network request ก็ยังเห็นวิ่งอยู่นะ) เช่น…

  • คนไป focus เรื่อง cookie เหมือนยุโรป จริงๆ กฎหมายไม่ได้พูดถึง cookie เลย อยากเก็บก็เก็บได้ไม่ต้องขออนุญาต
  • จุดที่ focus จริงๆ คือเรื่องการเก็บ data และการเอาไปใช้ ซึ่งในไทยไม่มีใครตีความเลยสักคนว่าอะไรได้ไม่ได้ มีแต่จะไปลอก GDPR มาซึ่งก็คล้ายๆ (เพราะกฎหมายก็ลอกมา) แต่มันไม่เหมือน 100% ปัญหาใหญ่ๆ คือ “anonymous” tracking แบบ Google Analytics คือ ไม่รู้ว่าชื่ออะไร แต่รู้ว่าทำอะไรบ้าง บางคนตีความว่ามันใช้ Cookie ID ทำให้ต้องขอ consent แต่บางคนก็ตีความไปว่าแบบนี้ใช้ได้ ซึ่งผมว่ามันผิดเจตนารมณ์ของกฎหมาย (ad tracking มันก็ใช้ “anonymous” advertising ID นะ)
    • ถ้าไม่ใช้ cookie ID มันจะนับ unique visitor/session ไม่ได้เลย ซึ่งเป็น KPI ใหญ่ๆ ของหลายๆ ที่ บางระบบอาจจะใช้ fingerprinting เข้ามาแทน
  • มีข้อยกเว้นว่าเก็บเพื่อประโยชน์ของบริษัทได้ ซึ่งบริษัทคงจะตีความต่างๆ นาๆ กันได้หมด เช่นบอกว่า unique visitor บริษัทต้องใช้จะเก็บ คิดว่าข้อยกเว้นแบบนี้น่าจะทำเหมือนข้อหารือภาษีมากกว่า คือส่งเรื่องให้สำนักงานที่กำกับดูแลอนุมัติก่อนจึงจะทำได้

คือถ้าคนยังติด Google Analytics แล้ว default on ได้ ผมว่ากฎหมายมีปัญหาแล้วล่ะ

Infra & Security

Infra ปีนี้ได้โอกาส revamp infra รอบใหญ่ๆ ล้างบาง legacy ไปได้พอสมควรแล้วก็ยังทำให้ infra เรายัง cutting edge อยู่ เสียดายว่าปีนี้งานจัดน้อยเลยไม่ค่อยได้ออกไปเล่าให้ใครฟังมาก แต่ก็น่าจะเขียนบล็อคไปหมดแล้ว

Security เองปีนี้มีข่าวใหญ่ๆ ไปทีนึง ซึ่งเท่าที่สำรวจและอุดไปก็เป็นช่องทางที่เหนือความคาดหมายเหมือนกัน คือช่วงปีสองปีมาเราทำเรื่อง security ให้เพิ่มขึ้นเรื่อยๆ แต่ที่แทบไม่ได้ทำเลยคือกลับไปแก้ของเก่า เพราะถ้าสังเกตจากบล็อคที่เขียน งานที่เราทำคือออก platform ใหม่แล้วให้ทุกคนย้ายไปใช้ platform ใหม่ แต่บางอย่างคือ practice มันทันสมัยอยู่แล้ว (แต่ insecure) เลยไม่ได้มีประเด็นต้องออก version ใหม่แล้วมันเลยถูกปล่อยทิ้งไว้แบบนั้น

ปีหน้าคิดว่าธีมของงานคงจะเป็น security ไปอย่างน้อยครึ่งปี

Open Source

ปีนี้คือลาจาก open source maintainer จริงจัง ใครส่ง PR อะไรมาบอกเลยว่าไม่อ่านแล้วนะ อยากเอาป้าย unmaintained.tech ติดด้วยซ้ำแต่ก็ขี้เกียจ

Project ใหม่ๆ ที่อยาก open source มีอยู่พอสมควร แต่ว่าพอจะไม่ได้ maintain ก็เลยเฉยๆ งานอื่นยุ่งกว่า แล้ว Hacktobefest ก็ไม่ได้ทำมัวแต่เล่นเกมอยู่…

ก็เลยอยากเขียนหน่อยว่าปีขี้เกียจแบบนี้ทำอะไรไปบ้าง…

  • ArgoCD: Add support for ssh-with-port repo url (#2866) อันนี้ critical มากๆ ทำให้เริ่มงาน project Ceylon ได้
  • gziphandler: Refactor out gzipwriter interface อันนี้พอเค้าไม่ได้ merge ก็ไม่ได้ open source ตัว cfzlib ต่อ ลด CPU ได้ 40% เลยนะ
  • Open source Go SafetyNet validator รองรับทั้ง Google Safetynet และ Huawei Device Check เลย
  • Pomerium: docs fix
  • Pomerium: small fix รู้สึกว่าเวลา contribute open source project ใหม่ควรเริ่มเล็กๆ ก่อน จะได้รู้ว่า upstream จะมาไม้ไหน
  • Pomerium: read url query string พอ contribute ไปเยอะขนาดนี้แล้วก็พบว่า Pomerium มันไม่เสถียรมากๆ เลยเลิกใช้แล้วไปเขียนเองแทน (หวังว่าจะได้ open source)
  • Vault cap username length in database ปรากฏว่า upstream อยากทำท่าอื่น เลยกลายเป็นว่าตอนนี้ต้อง fork Vault ไปก่อน…
  • dex: service account-less group info + docs เขียน PR เกือบเสร็จแล้วเจอว่า domain บริษัทไม่มี license ใช้ API นี้….
  • dex: Vault signer อันนี้ใหญ่มาก แต่ก็ยังขี้เกียจทำให้โค้ดมันคุณภาพดีพอจะ merge

อย่างหนึ่งที่สังเกตคือข้างบนเป็น Go ทุก project เลย เราว่า Go มันทำให้โค้ดมันค่อนข้างเขียนเหมือนกันเกือบหมด ไม่ต้องตีกันเรื่อง design แล้วมันไม่มีอะไรซับซ้อนแบบ DI container (ใครใช้ wire ตีตายเลย) หรือแม้แต่ class inheritance โปรเจกท์ที่เราเขียน Go เป็นครั้งแรก maintainer เคยเขียนบล็อคถึงปรากฏการณ์นี้ไว้ค่อนข้างน่าสนใจ

อีกข้อสังเกตคือข้างบนเป็น PR ในงานบริษัทหมดเลย ก็ดีว่าบริษัทให้อิสระทำอะไรก็ได้ (ที่เป็นประโยชน์กับบริษัท) แล้วเวลาเราติดปัญหาเราก็เลยเลือกที่จะ fix ที่โค้ดดูก่อนถ้ามันไม่เหลือบ่ากว่าแรง ในขณะเดียวกันตอนนี้เลิกงานมาก็ขี้เกียจจนเล่นแต่ RuneScape แล้ว…

นอกจาก code contribution ให้ open source แล้ว ปีนี้รู้สึกว่า project open source บางตัว โดยเฉพาะตัวที่ไม่ค่อยได้ใช้ใน corporate หรือมัน niche มากๆ มันควรจะต้องมี funding เหมือนเราซื้อ proprietary software ปีนี้ก็เลยเลือกจะ fund โครงการต่อไปนี้

  • MATE ผ่าน Ubuntu Mate หลังจากทนใช้ GNOME 3 มาหลายปี พอมาใช้ MATE (GNOME 2) แล้วพบว่ามันเวิร์คกว่า GNOME 3 เยอะมากๆ ถึงจะผ่านไปหลายปีแล้วก็เถอะ แต่ MATE มีปัญหาเรื่องตามเทคโนโลยีไม่ทันมากๆ และมันไม่น่าจะมีโอกาสได้ corporate funding แน่ๆ ก็เลยเลือกจะ join Patreon ไป $10/mo
  • KeepassXC Browser ทีมนั่งคุยกันเรื่อง password manager บน browser ว่าตัวไหนใช้งานสะดวกกว่ากัน ก็ยอมรับว่า KeepassXC มันสู้ proprietary software ไม่ได้ ฉะนั้นก็แทนที่จะให้ $3/mo กับ 1Password หรือ LastPass ก็เอาให้ KeepassXC ดีกว่า

RuneScape

ปีที่ผ่านมานอกเวลางานคือเล่นแต่ RuneScape ตอนที่กดซื้อ subscription 1 ปีคือรู้ตัวเลยว่าทั้งปีนี้จะขายวิญญาณให้เกมนี้แน่นอน

แต่ตอนท้ายๆ ปีงานเริ่มตึงขึ้นเรื่อยๆ รู้สึกว่าอยากหนีไปทำอย่างอื่นแต่จะไปเที่ยวก็ไม่ได้ ก็เหลือแต่เล่นเกมนี่แหละ แล้วทั้งปีคือแทบไม่มีความอยากเล่นเกมอื่นเลย มันเลยคุ้มค่ามากๆ (Switch ฝุ่นจับแล้วตอนนี้…)

หลังจากปีที่แล้วได้ Quest Cape มาแล้ว แพลนปีนี้คืออยากได้ Diary Cape กับ Music Cape ซึ่ง Music Cape นี่โชคดีมากๆ เพราะเปิด Master casket อันที่สองได้ Mimic ก็เลยได้ง่ายๆ (แต่กว่าจะเอา mimic ลงก็เหนื่อย)

ส่วน Diary Cape ตอนนี้ยังเหลือแค่ Slayer กับ Thieving ซึ่งน่าเบื่อทั้งคู่ ก็เลยตัดสินใจว่าไปเล่น RuneScape 3 แล้วกัน…

RuneScape 3

ตอนเลิกเล่น RuneScape บอกว่าจะไม่เล่น EoC ซึ่งตอนกลับมาเล่นก็พยายามอยู่มากๆๆๆ ที่จะใช้ legacy mode แต่ก็ต้องยอมเพราะว่า EoC มันมี heal หลังจบ combat และมันใช้ทรัพยากรน้อยกว่า (runes, ammo) ซึ่ง heal นี่มันโกงมากๆ

  • ไปตี Knight Waves แล้วพบว่ามันไม่เหมือน OSRS คือต้องเล่นให้จบในครั้งเดียว เลยตีด้วย legacy mode เพราะได้ DPS มากกว่า แล้วสลับเข้า Revolution mode บางครั้งเพื่อเอาฮีล เล่นจบรู้สึกว่าโกงมากๆ
  • ทำ Fight Caves แล้วปรากฏว่าอาหารเหลือแต่ prayer เกือบหมด เพราะปล่อยฮีลเอาทุก wave แถมแทบจะยืน tank ได้เลยเพราะยังไงมันก็ฮีลอยู่แล้ว

สิ่งที่ได้จาก RS3 คือ keyboard shortcut ที่พอ bind สลับ prayer บนแป้นแล้วรู้สึกว่าเล่นสบายขึ้นเยอะ ตอนกลับไปเล่น OSRS ก็รู้สึกว่าเล่นดีขึ้นเยอะ

ข้อเสียของ RS3 คือมันเป็น pay to win คือเคยมีเอกสารหลุดมาเค้าบอกว่าคนที่เล่นจริงๆ (ไม่ใช่ gold farmer) ใช้บอตเพราะอยากได้ตังหรืออยากได้เลเวล ถ้าอย่างนั้นก็ขายตัง ขายเลเวลไปเลยแล้วกัน หลังๆ ก็เริ่มรู้สึกว่าอยากซื้อเลเวลไปเลยนะถ้าไหนๆ เกมมันจะ legitimatize ให้ทำได้แล้ว แล้วแม้แต่ผู้เล่น OSRS เองก็ให้เหตุผลว่าฟาร์มตังในเกมยังไงก็ไม่เร็วเท่าฟาร์มตังในชีวิตจริงแล้วเติม

คือ economy ของเกมมันเปลี่ยนไปจากสมัย 2012 แล้ว สมัยนั้นคือ grind skill ไปเรื่อยๆ แล้วได้ตัง เดี๋ยวนี้ต้องไปตีบอสถึงจะรวย หรือไม่ก็ merch items แต่เราไม่ชอบทั้งคู่ ในขณะที่ราคาไอเทมมันถูกกำหนดโดย supply และ demand จากคนสองกลุ่มนั้นเลย

2020

ปีที่แล้วเขียนไว้เยอะ เอาจริงไม่ได้ทำอะไรเลยมัวแต่เล่นเกม…

Prediction

ปีนี้ได้เลื่อนขั้น ถือว่าทายไม่ถูก

New Year Resolution

New Blog

ไม่ได้ทำ อย่างที่เห็น

รู้สึกว่าทำงานแล้วเหนื่อยมากขึ้น บวกกับ อยากเล่น RuneScape ปีนี้เลยแทบไม่ได้เขียนโค้ดที่ไม่เกี่ยวกับงานเลย

เลิกพูดแทรกคนอื่น

เริ่มสังเกตบางทีแต่ยังคิดว่าไม่ดีขึ้นเท่าไร

2021

Prediction

ปีนี้ไม่รู้จะทายเรื่องส่วนตัวยังไงดี ดูท่าทีตอนนี้ COVID ยังอาจจะอยู่ไปทั่วโลกอีกปี (เห็นว่า Ebola มัน 2 ปีเต็มเลยนะ นี่ COVID ยังเพิ่งปีเดียวเอง) ยังอยากไปเที่ยวอยู่แต่เดาว่าปีนี้จะไม่ได้ไปไหน

New Year Resolution

ปีนี้อยากลอง resolution ทุก quarter ดู ไม่รู้จะอยากเขียน quarter in review มั้ย

่สวนหนึ่งคือเอาจริงๆ new year resolution ส่วนมากก็ทำจบใน H1 อยู่แล้ว แล้วบางทีกลางปีก็มีอะไรที่อยากทำแต่ก็จะรู้สึกว่าเก็บไปเขียน New Year Resolution ดีกว่าเดี๋ยวไม่มีอะไรเขียน

ของ Q1 2021 ขอไปคิดก่อนนะ…

Things you don’t know about Protocol Buffers

ช่วงนี้นั่งงม Protocol Buffers ลึกๆ แล้วพบว่า documentation มันไม่ค่อยมีเขียนเท่าไร หลายๆ คนก็คงน่าจะเคยใช้อย่างมากก็ gRPC เลยอยากมาเล่าให้ฟังหน่อยว่า Protobuf ทำอะไรได้อีกบ้าง

บทความนี้จะพูดถึงเฉพาะ Protobuf 3 เท่านั้น ส่วน 2 ผมไม่ได้ใช้นานแล้ว

REST API

ลองทำ API แล้วคืนเป็น Protobuf ดู พบว่าทำงานง่ายขึ้นมาก

  • Protobuf มี schema ชัดเจน และ validate มาแล้ว ไม่ต้องนั่งเขียน JSON Schema บน Swagger อีกรอบ (ตรงไม่ตรงก็ไม่รู้อีกต่างหาก)
  • ไม่เสียเวลาเขียน struct หรือ type definition มา parse บน client side ใช้ codegen ออกมาจาก proto definition ได้เลย
  • ยังใช้ Django + Django REST Framework Serializer ได้อยู่

แต่ข้อเสียคือ

  • HTTP Library ส่วนมาก parse JSON จาก response ได้เลย พอเป็น Protobuf แล้วต้องเขียน logic ในการ parse เอง
  • บน JavaScript frontend ถ้าใช้ Protobuf API ต้อง ship parser ไปด้วยทำให้เปลือง bundle size

JSON

จากข้อข้างบน ถ้าอยากจะ ship Protobuf แต่ไม่อยากเปลือง bundle size วิธีหนึ่งที่ทำได้คือ Protobuf จะมี JSON representation เราก็อาจจะให้รับส่งเป็น JSON แทนได้

วิธีนี้คิดว่าได้ข้อดีข้างบนมาหมดทุกข้อเลย

วิธี encode protobuf เป็น JSON อย่า parse ออกมาแล้วโยนใส่ JSON encoder ปกติ แต่ Protobuf จะมี function ให้ เช่น google.protobuf.json_format.MessageToJson ใน Python หรือ google.golang.org/protobuf/encoding/protojson.Marshal ใน Go เวลาถอดรหัสก็ต้องใช้ function ของ protobuf เช่นกัน

JSON encoding ของ Protobuf จะไม่เหมือนกับ data structure ตรงๆ นิดนึงคือ

  • Key จะกลายเป็น lowerCamelCase เสมอ เช่น field ชื่อ user_name จะกลายเป็น userName
  • ถ้าไม่ได้แก้ settings อะไร default value ของแต่ละ field จะหายไป เช่นถ้ามี field string username = 1; อยู่มีค่าเป็น string เปล่า มันจะไม่ออกมาใน JSON (ถ้าอยากให้ parse สะดวกอาจจะต้องแก้ config ให้มันส่งออกมาด้วย)
  • enum จะแสดงเป็นชื่อ enum field แต่จะแก้ settings ให้ส่งเป็นตัวเลขแทนก็ได้ (default value ของ enum คือ member ตัวแรก)
  • bytes กลายเป็น base64 string

Fun fact: เวลาแปลง protobuf เป็น JSON ใน Go ไม่ได้ใช้ encoding/json แต่มันจะ build string ออกมาเลย (แต่ตอนถอดกลับใช้ยัง encoding/json นะ)

Null value

จากข้อข้างบนอาจจะสงสัยว่าทำไม default value ไม่ถูก encode ออกมา

คำตอบคือ Protobuf ไม่มี null value และนี่น่าจะเป็นสิ่งที่ผมพลาดเยอะที่สุด

กรณีที่เราเซต field ใดๆ เป็น null นั้น protobuf จะถือว่าใช้ default value ของ field นั้นๆ ได้แก่

  • string คือ string เปล่า
  • bool คือ false
  • ตัวเลขต่างๆ คือ 0
  • enum คือ สมาชิกตัวแรกของ enum
  • repeated คือ empty list
  • เฉพาะ field ที่มี type เป็น message เท่านั้นจะมี null value ได้

ที่เป็นแบบนี้เพราะ Protobuf จะไม่ encode field ที่มีค่าเป็น default ส่งไป

Well Known Type

Docs Protobuf เอา WKT ไปแอบลึกมากจนอาจจะไม่เคยรู้เลยว่ามีสิ่งนี้ด้วย

Well known type คือ message ต่างๆ ที่ติดมากับ Protobuf ได้แก่

  • google.protobuf.Duration เก็บระยะเวลา
  • google.protobuf.Timestamp เก็บเวลา
  • google.protobuf.Empty ไม่เก็บอะไรเลย

ดังนั้นเวลาจะ encode เวลาควรจะใช้ WKT เสมอ เนื่องจากว่าใน library ภาษาต่างๆ มักจะมี function ที่ถอดรหัสเข้าออกเป็น native value ของภาษานั้นๆ ให้อยู่แล้ว เช่น ToDatetime ใน Python หรือ AsTime ใน Go วิธีนี้ทำให้เราไม่ต้องนั่งจำว่า time บน service นี้ encode เป็นอะไร (ISO8601? POSIX? Custom format?)

เวลาใช้ WKT ต้อง import มาจาก google/protobuf/timestamp.proto (หรือไฟล์อื่นๆ ตามที่จะใช้) ซึ่งไม่มี document ไว้…

Wrapped type

จากข้อข้างบนเราบอกว่า Protobuf ไม่มี null value แต่ message เป็น null ได้ ดังนั้นถ้าอยากจะส่ง nullable เราก็แค่ยก field นั้นไปทำเป็น message ใหม่แล้วเซตเป็น null

ซึ่ง Protobuf ก็คิดมาแล้ว ใน WKT จะมี type ต่างๆ ที่ครอบมาให้แล้ว

  • google.protobuf.BoolValue
  • google.protobuf.BytesValue
  • google.protobuf.DoubleValue
  • google.protobuf.FloatValue
  • google.protobuf.Int32Value
  • google.protobuf.Int64Value
  • google.protobuf.StringValue
  • google.protobuf.UInt32Value
  • google.protobuf.UInt64Value

ทั้งหมดจะมี field เดียวคือ value และเมื่อ encode เป็น JSON แล้วมันจะหายไปกลายเป็นค่าที่เก็บไว้ตรงๆ (นี่แหละที่ถ้าทำ type เองจะทำไม่ได้)

FileDescriptorSet

เวลาใช้งาน protobuf เราจะใช้ protoc เพื่อทำ code generation ซึ่งต้องลง protoc-gen-* ตามภาษาที่ใช้งานด้วย

แล้ว protoc generate อะไรได้มั้ย?

คำตอบคือมัน generate FileDescriptorSet ได้

FileDescriptorSet เป็น well known type อันนึง มันคือการ .proto ให้เป็น protobuf แถมเราสามารถบอกให้ protoc include ไฟล์ทั้งหมดที่เรา include ต่อๆ กันมาได้ด้วย ทำให้ FileDescriptorSet นั้นจบในตัว

FileDescriptorSet นี้ไม่อยู่ใน protobuf documentation ด้วยนะ!!

Any

WKT อีก type หนึ่งที่น่าสนใจคือ Any ซึ่งน่าเสียดายที่ถึงมันจะอยู่ใน documentation แต่ library ต่างๆ ยังไม่พร้อมใช้เท่าไรนัก

Any ใช้เก็บ message อะไรก็ได้ โดยมันจะมี 2 field คือ

  • type_url เก็บ URL ของ type นั้น เช่น type.googleapis.com/com.mycompany.TypeName
  • value เป็น bytes คือข้อมูลที่ต้องการเก็บ encode เป็น protobuf

เวลา decode แล้ว library ที่รองรับจะสามารถอ่าน type ต้นฉบับได้เลยโดยไม่ต้อง decode เอง นอกไปจากนี้จะสังเกตว่า type_url นั้นเป็น URL เวลาเจอ type แปลกประหลาด Protobuf client library จะเข้าไปใน URL นั้นเพื่อโหลด type definition ให้อัตโนมัติ ซึ่งฟีเจอร์นี้โม้ไว้เฉยๆ ยังไม่ได้ทำ

เวลา encode Any เป็น JSON มันจะกลายเป็น {"@type": "type.googleapis.com/com.mycompany.TypeName"} แล้วเอาค่าใน value ที่เก็บไว้เข้ามา merge เลยไม่ได้ทำเป็น field ย่อย ใครที่ใช้ Stackdriver Logging น่าจะเห็น output ที่มี @type field บ่อย

จากด้านบน ถ้าเราทำ message ที่มี Any กับ FileDescriptorSet ฝังรวมกันเราจะได้ Self describing message แต่ปัจจุบันเนื่องจาก library ไม่รองรับกันเท่าไรจึง decode ยากมากๆ บางภาษาอาจจะ decode แล้วพังเลย

Reflection

แล้ว FileDescriptorSet ทำอะไรได้อีก? Protobuf ในบางภาษาจะมี reflection API ทำให้เราสามารถ create Protobuf message ได้ใน runtime (ภาษา dynamic เช่น JavaScript อาจจะไม่ต้องรองรับเพราะไม่ต้องใช้ codegen) เช่น dynamicpb

Lots of potential

เขียนมาทั้งหมดนี้แล้วจะเห็นธีมคล้ายๆ กันว่าถึง Protobuf จะออกมา 12 ปีแล้ว และ Proto3 ออกมา 4 ปีแล้วแต่ Protobuf ยังดูมี potential อีกมากที่ยังพัฒนาไม่เสร็จ