งานอัตโนมัติที่รันสองรอบ — บั๊กที่อาการไม่คงที่และหาสาเหตุยากที่สุด
บั๊กที่ไล่หาสาเหตุยากที่สุดไม่ใช่บั๊กที่ทำให้ระบบพัง แต่เป็นบั๊กที่เกิดบ้างไม่เกิดบ้าง
เราเจอบั๊กแบบนั้นในระบบงานอัตโนมัติ และการไล่ปิดมันให้ขาดกลายเป็นงานที่กินเวลาหลายรีลีสติดกัน
อาการ
รายงานที่เข้ามาไม่เหมือนกันเลยสักเรื่อง
- บางครั้งลูกค้าได้รับการแจ้งเตือนเรื่องเดียวกันสองครั้ง
- บางครั้งบิลตามรอบถูกสร้างซ้ำ
- บางครั้งรายการเดิมถูกประมวลผลสองรอบ
ทั้งหมดนี้เกิดเป็นครั้งคราว ทำซ้ำตามไม่ได้ และเมื่อไปดูบันทึกเหตุการณ์ย้อนหลังก็เห็นว่างานรันสำเร็จตามปกติทุกรอบ
สาเหตุ
ระบบของเราถูกออกแบบให้บริการทั้งหมดรวมกันอยู่ในกระบวนการเดียวเพื่อประหยัดหน่วยความจำ ระหว่างการเปลี่ยนผ่านนั้น ตัวจัดการงานอัตโนมัติถูกลงทะเบียนไว้สองที่ — ทั้งในกระบวนการรวมและในกระบวนการแยกที่ยังเหลืออยู่
ผลคือมีตัวจัดการงานสองตัวรันพร้อมกัน ต่างคนต่างหยิบงานเดียวกันไปทำ
ทำไมถึงร้ายแรงกว่าที่ฟัง
งานอัตโนมัติเกือบทั้งหมดถูกเขียนขึ้นภายใต้สมมติฐานเงียบ ๆ ว่ามีตัวเองรันอยู่ตัวเดียว สมมติฐานนี้ไม่เคยถูกเขียนไว้ที่ไหน เพราะตอนที่เขียนโค้ดมันเป็นจริงเสมอ
เมื่อสมมติฐานพัง ปัญหาเกิดสองรูปแบบ
1. ผลซ้ำ
งานที่ส่งอีเมล สร้างเอกสาร หรือหักยอด จะทำสิ่งนั้นสองครั้ง
2. แย่งกันทำงาน
รูปแบบนี้อันตรายกว่า — งานส่วนใหญ่เริ่มด้วยการเช็คก่อนว่ามีใครทำรายการนี้ไปแล้วหรือยัง ถ้าทั้งสองตัวเช็คพร้อมกัน ทั้งคู่จะเห็นว่ายังไม่มีใครทำ แล้วต่างคนต่างทำ
การเช็คแบบนี้ไม่ได้ผิด มันแค่ไม่ปลอดภัยเมื่อมีคนแข่งกัน และช่องว่างระหว่าง “เช็ค” กับ “ทำ” กว้างแค่เสี้ยววินาที ซึ่งเป็นเหตุผลที่บั๊กเกิดเป็นครั้งคราวเท่านั้น
สิ่งที่เราทำ
ขั้นที่ 1 — แก้ต้นเหตุ
ให้เหลือที่เดียวที่รับผิดชอบการรันงานเบื้องหลัง
ขั้นที่ 2 — ไล่ตรวจทั้งระบบ
เราไม่หยุดที่การแก้จุดที่เจอ แต่ไล่ตรวจทุกส่วนของระบบว่ายังมีที่ไหนอีกที่อาจรันซ้อนกันได้ พบอีก 2 จุดและแก้ไปพร้อมกัน
ขั้นตอนนี้สำคัญกว่าขั้นแรก เพราะบั๊กที่อาการไม่คงที่ ถ้าเหลือไว้แม้จุดเดียวจะกลับมาในเวลาที่คาดไม่ถึง และคราวหน้าจะไม่มีใครนึกถึงสาเหตุนี้แล้วเพราะ “แก้ไปแล้ว”
ขั้นที่ 3 — เปลี่ยนจาก “เช็คแล้วทำ” เป็น “จองแล้วทำ”
สำหรับงานที่กระทบเงินโดยตรงอย่างการสร้างบิลตามรอบ เราไม่พึ่งการที่มีตัวจัดการงานตัวเดียว
เปลี่ยนวิธีทำงานเป็นการจองรายการก่อนลงมือ — ตัวที่จองได้เท่านั้นที่จะสร้างบิล ตัวที่จองไม่ทันจะข้ามไปเอง
ความต่างคือการจองเป็นการกระทำเดียวที่แบ่งครึ่งไม่ได้ ไม่มีช่องว่างให้แทรก ไม่ว่าจะมีตัวจัดการงานกี่ตัวรันพร้อมกัน บิลก็ถูกสร้างครั้งเดียว
นี่คือความต่างระหว่าง “แก้ให้ไม่เกิด” กับ “ทำให้เกิดไม่ได้”
สิ่งที่เจอเพิ่มระหว่างทาง
การไล่ตรวจรอบนี้ทำให้เจอปัญหาอื่นในระบบงานอัตโนมัติที่ซ่อนอยู่ด้วยกัน
- งานที่ถูกหยุดกลางคันทิ้งสถานะ “กำลังรัน” ค้างไว้ ทำให้ระบบเข้าใจว่ายังมีตัวรันอยู่แล้วไม่รันอีกเลย — พบว่ามีงานค้างมาตั้งแต่สามเดือนก่อน ตอนนี้ระบบล้างสถานะค้างให้ตอนเริ่มทำงาน
- งานที่เรียกไปยังระบบภายนอกโดยไม่จำกัดเวลารอ ถ้าปลายทางค้าง งานก็ค้างตาม และเพราะทุกงานใช้คิวร่วมกัน มันจึงบล็อกงานอื่นทั้งหมด — อาการที่เห็นคือระบบเหมือนหยุดทำงานโดยไม่มีข้อผิดพลาดใด ๆ
- งานที่ล็อกตารางสินค้าทั้งตารางในรายการเดียว ระหว่างนั้นคนที่กำลังบันทึกงานขายจะถูกกันไว้ ยิ่งข้อมูลเยอะยิ่งกันนาน
ทั้งสามเรื่องนี้ไม่เกี่ยวกับบั๊กเดิมโดยตรง แต่เจอเพราะการไล่ตรวจแบบเดียวกัน
ทำไมเราถึงเล่าเรื่องนี้
เพราะเราคิดว่าวิธีจัดการกับบั๊กบอกอะไรเกี่ยวกับทีมได้มากกว่าตัวบั๊กเอง
บั๊กนี้แก้ให้หายอาการได้ในบรรทัดเดียว เราเลือกไม่หยุดตรงนั้น แต่ไล่ตรวจทั้งระบบ แล้วเปลี่ยนวิธีทำงานของส่วนที่กระทบเงินให้ผิดพลาดไม่ได้ตั้งแต่แรก
สำหรับระบบที่ลูกค้าใช้จัดการบัญชีและสต๊อกจริง เราคิดว่านั่นคือมาตรฐานขั้นต่ำที่ควรเป็น
บริหารจัดการข้อมูลในองค์กร ครบในที่เดียว