Sort & Find to Max-Min Aggregate in a MongoDB query
- Sathit Jittanupat
- 22 ก.พ.
- ยาว 2 นาที
อัปเดตเมื่อ 23 ก.พ.

ไม่นานมานี้โปรแกรมเริ่มมีปัญหาเล็กๆ เกี่ยวการใช้งาน พบว่ามี query error เกิดขึ้นผิดสังเกตเฉพาะกับชุดข้อมูลแห่งหนึ่ง ขณะที่ที่ชุดข้อมูลอื่นไม่มี หลังจากจัดการแก้ไขปัญหาเสร็จแล้ว กลับคิดถึงความเป็นไปได้ในมุมมองที่ลึกซึ้งจากเหตุการณ์นี้
โปรแกรมมีหัวข้อ "สร้างข้อมูลสำหรับรายงาน" เป็นการประมวลผลข้อมูลเพื่อเตรียมใช้สำหรับรายงานเฉพาะ ในกรณีนี้คือ คำนวณต้นทุนสินค้า จุดที่เกิดปัญหาอยู่ตรงที่จอแสดงสถานะข้อมูล
บังเอิญว่าชุดข้อมูลที่เกิดปัญหานั้นเป็นชุดที่ใช้งานต่อเนื่องมานานกว่า 6 ปี และมีความเคลื่อนไหวของข้อมูลค่อนข้างมาก ผู้ใช้ประมาณ 100 บัญชีต่อวัน มีเอกสารเพิ่มเข้าในระบบเดือนละประมาณ 4–5 หมื่นบิล
ครั้งหนึ่งตอนขึ้นระบบใหม่องค์กรแห่งหนึ่ง เคยมีเรื่องเล่าจากคนในองค์กรถึงโปรแกรมเก่าของเขาว่า วันไหนที่ต้องเรียกรายงานให้ผู้บริหารก็จะประกาศเสียงตามสายให้ทุกคนหยุดใช้โปรแกรม ทำให้ผมนึกสยดสยองว่า วันนี้โปรแกรมเราเริ่มมีสัญญาณเช่นนั้น
Programmer
เมื่อไล่ไปถึงจุดที่เกิด query error พบว่าเป็น time-out error หมายความว่าใช้เวลานานเกินไปในการหาผลลัพธ์ จนต้องตัดการติดต่อกับ database server ในทางเทคนิคจึงต้องเข้าไปตรวจสอบ query ดังกล่าว
มุมมองแรกในฐานะโปรแกรมเมอร์ เมื่อ query มีปัญหาก็หาทางแก้ด้วยโปรแกรม เช่น อ้อมหลบ หากจุดปัญหานั้นใช้เพียงแค่แสดง information ไม่มีผลกับการประมวลผล หรือ เผชิญหน้าตรงๆ ดูว่า query นั้นสามารถปรับปรุงได้หรือไม่
"หาเอกสารลงวันที่เก่าสุด ที่เปลี่ยนแปลงหลังจากสั่งประมวลผลปิดงวดครั้งที่แล้ว"
เริ่มจาก query แรกหา timestamp "เวลาปิดงวด" หลังสุด จากข้อมูลที่เกิดจากคำสั่งประมวลผล
หลังจากใช้ query ที่สอง หาเอกสารโดยเลือกใช้ index ให้เรียงตาม "วันที่" จากน้อยไปมาก จนกว่าจะเจอข้อมูลแรกที่มี "เวลาเปลี่ยนแปลง" ของเอกสารมากกว่า "เวลาปิดงวด"
ตัวอย่าง query MongoDB (แปลงเป็น pipeline เพื่อให้เข้าใจง่าย)
[
{$sort: {docdate: 1} },
{$match: {$gt: [ '$modified', time_xxx]} },
{$limit: 1 },
]
สำหรับข้อมูลที่มีเอกสารเก่าระดับ 6–7 ปี ผมนึกออกทันทีว่า query ที่สองน่าจะมีปัญหา เพราะช่วงของวันที่เอกสารเก่าที่ไม่เปลี่ยนแปลงยาวนานขึ้นเรื่อยๆ ทำให้ต้องใช้เวลามากขึ้นกว่าจะได้คำตอบ จนกระทั่งถึงจุดที่กลายเป็น time-out error
ในทางเทคนิค เราสามารถปรับ query โดยอาศัยความเข้าใจธรรมชาติของข้อมูลของเอกสารที่เปลี่ยนแปลงด้วย โดยทั่วไปช่วงเวลาของเอกสารที่อาจเปลี่ยนแปลงย้อนหลัง มักจะยาวหรือสั้นขึ้นอยู่กับรอบระยะเวลาเครดิตของธุรกิจประเภทนั้นๆ
ดังนั้น เราสามารถเปลี่ยน query ใหม่ โดยเลือกใช้มุมมอง "เวลาเปลี่ยนแปลง" แทน สแกนหาเอกสารทั้งหมดที่มี "เวลาเปลี่ยนแปลง" มากกว่า "เวลาปิดงวด" แล้วสะกัดหาวันที่เอกสารที่เก่าสุด (น้อยสุดแทน)
ตัวอย่าง query MongoDB (aggregate)
[
{$match: {$gt: [ '$modified', time_xxx]} },
{$group: {
_id: null,
maxdate: {$max: '$docdate'},
mindate: {$min: '$docdate'},
}
},
]
ข้อแตกต่างระหว่าง query ทั้งสอง อยู่ที่ปริมาณข้อมูลที่ต้องสแกน จะเห็นว่าวิธีแรกมีโอกาสขยายมากขึ้นเรื่อยๆ เพราะช่วงเวลา active ที่เอกสารเปลี่ยนแปลงมักอยู่แถวช่วงวันที่ปัจจุบัน ขณะที่วิธีที่สองปริมาณค่อนข้างคงที่ตามรอบของการปิดงวด
Junior to Senior
คำถามหนึ่งที่ติดอยู่ในใจผม จำเป็นไหมที่จะต้องใช้ query แบบที่สอง ตั้งแต่วันแรกที่ทำโปรแกรม ไม่ว่าจะคิดทบทวนกี่ครั้งคำตอบที่ได้ก็ยังเหมือนเดิม "ไม่จำเป็น"
ย้อนไปสมัยนั้นตอนที่ผมเริ่มใช้ MongoDB ยังไม่กล้าใช้คำสั่ง $group, $max, $min เรียกว่าเป็นท่ายากสำหรับมือใหม่ ได้แต่คิดวิธี query ตรงไปตรงมา โจทย์ถามอะไรก็ค้นหาตามนั้น ใช้ sort ด้วย index ตามท่าพื้นฐานของดาต้าเบสทั่วไป
ยากมากที่จะบอกว่า query แบบไหนไม่เหมาะสม อาจจะพอแยกได้ว่า query ที่ "ใช้ได้" กับ "ใช้ไม่ได้" จากผลลัพธ์ที่ต่างกัน แต่ถ้าหากได้ผลลัพธ์ออกมาเหมือนกัน ก็หมายความว่าใช้ได้ โดยเฉพาะช่วงเวลาที่ข้อมูลสะสมยังไม่มากจนปัญหายังไม่ผุดขึ้นมา แม้กระทั่งกำหนดเป็นสเปคโปรแกรมก็ยังยาก
"อะไรที่ใช้งานมาได้ 6 ปี จะนับว่าผิดพลาดได้ไหม"
สำหรับโปรแกรมเมอร์ที่มีประสบการณ์ อาจพอนับว่าเป็น Technical Debt อย่างหนึ่ง แต่ก็เป็นความยากที่จะอธิบายหรือถ่ายทอดบทเรียนนี้กับรุ่นน้อง และยิ่งยากขึ้นไปอีกหากวิธีพัฒนาเป็นแบบโปรเจกท์เบส แบ่งความรับผิดชอบตามขอบเขตงาน ส่งมอบเสร็จแล้วทีมก็แยกย้ายไปทำอย่างอื่น ทีมเช่นนี้แทบไม่มีโอกาสได้สะสมเรื่องทำนองนี้จากประสบการณ์จริง
"ปัญหาของวันพรุ่งนี้ ให้ตัวฉันในวันพรุ่งนี้จัดการ" ไซตามะ
"ตื่นตระหนกต่อปัญหาก่อนที่มันจะเกิด คือทักษะจำเป็นในยุคปั่นป่วน" Nassim Taleb
เรามักพบเจอคนที่มีทัศนะคติรับมือกับปัญหาของวันพรุ่งนี้แตกต่างกัน
ยกตัวอย่าง ภาวะโลกร้อน แต่บางครั้งทัศนะคติที่แตกต่างอาจอยู่ในคนคนเดียวกัน หลายครั้งขึ้นอยู่กับการตระหนักรู้ขนาดของปัญหาเมื่อเวลาผ่านไป หรือเมื่อหมกมุ่นค้นคว้า หรือเมื่อสะสม "จุด" มากพอจนเข้าใจ อุณหภูมิที่สูงขึ้น 6 องศาทำให้ก๊าซมีเทนในชั้นน้ำแข็งระเหยแล้วเกิดปฏิกิริยาลูกโซ่
มันเข้าใจยากพอๆ กับบอกว่าเมื่อดาต้าเบสสะสม 6 ปีจะมีข้อมูลหลายล้านเร็คคอร์ด ทำให้ query ไม่ระบุขอบเขตที่รัดกุม หรือไม่อิงตาม index ใช้งานไม่ได้ คุณจะอธิบายให้เด็กน้อยเข้าใจ "ความร้อนของไฟ" อย่างไร หากไม่ให้เขาลองสัมผัสมัน
นับว่าเป็นโชคดีที่มีของจริงให้พวกเขาได้เจอ เอาไว้สัมผัสว่า query ที่ดี ต่างจาก query ที่ใช้ได้อย่างไร
ยิ่งระบบที่ใช้มายาวนานเท่าไหร่ ก็กลายเป็นเรื่องกลืนไม่เข้าคายไม่ออก ไม่สามารถสางรื้อถึงต้นตอจนเข้าใจปัญหา ทำให้ไม่รู้ว่าจะรับมือหรือเริ่มแก้อย่างไรจึงไม่พังพินาศ นอกจากต่อเติมปะผุประคองหรืออ้อมหลบกันไปตามเหตุปัจจัย ดังที่จะเห็นตัวอย่างของบรรดา Legacy Code ทั้งหลาย
สักวันเราก็ต้องเป็น Legacy Code เช่นกัน
Owner
ซอฟต์แวร์คอมพิวเตอร์เพิ่งวิวัฒน์ขึ้นมาเมื่อปลายศตวรรษ 1900 แค่ชั่วรุ่นเดียวของมนุษย์เอง สิ่งหนึ่งที่คนรุ่นแรกเข้าใจผิดคือ คิดว่าซอฟต์แวร์เป็นเหมือนหนังสือที่สามารถครอบครองและควบคุมได้ เช่นเดียวกับคนในยุคที่การรู้หนังสือเป็นอภิสิทธิ์เฉพาะชนชั้นสูง
จุดเปลี่ยนสำคัญของหนังสือคือ เครื่องพิมพ์ของกูเตนเบิร์ก ทำให้เริ่มเข้าใจว่าเราต้องการทักษะรู้หนังสือมาแปรเปลี่ยนเป็นความรู้ และเมื่อฮาร์ดแวร์คอมพิวเตอร์เป็นมาตรฐานเดียวกัน ทักษะการใช้ซอฟต์แวร์มาแปรเปลี่ยนเป็นผลิตภาพต่างหากที่สำคัญ
"ความเชื่อว่าเป็นเจ้าของครอบครองเพื่อควบคุม ยังคงแรงกล้าอยู่อีกไหม"
ช่วงที่ผมเป็นวัยรุ่นเราไม่มีทางเลือกมากนัก หากต้องการฟังเพลงที่ชื่นชอบในเวลาที่ต้องการก็ได้แต่ซื้อเทปคาสเซ็ตมาครอบครอง เราจึงเชื่อมโยงการครอบครองวัตถุกับประโยชน์ใช้งาน
ซอฟต์แวร์หรือโปรแกรมบัญชีรุ่นแรก ถูกนำเสนอในรูปแบบวัตถุที่สามารถครอบครองได้ แผ่นดิสก์และหนังสือคู่มือ ทั้งที่หัวใจสำคัญอยู่ที่โค้ดที่ติดตั้งลงในคอมพิวเตอร์ สมัยนั้นซอร์สโค้ดเป็นเหมือนสูตรลับทางการค้าอันศักดิ์สิทธิ์ ไม่ได้มีไว้เพื่อแบ่งปัน
ใช่ครับ โลกสมัยนั้นยังคืบไปไม่เร็วนัก เขียนโปรแกรมขึ้นมาแล้วใช้งานอย่างเดิมนับสิบปี โดยไม่ต้องปรับปรุง โดยมากอุปกรณ์ฮาร์ดแวร์หมดอายุก่อนซอฟต์แวร์ด้วยซ้ำ สมัยนั้นเรายังพอเห็นใช้วิธีพัฒนาระบบแบบเดิม วิเคราะห์จนถี่ถ้วนก่อน ร่างสเปคละเอียดแล้วค่อยทำออกมาตามนั้นเป๊ะ
แล้วเราก็มาถึงวันที่ซอฟต์แวร์ต้องยืดหยุ่นเปลี่ยนเร็วกว่าฮาร์ดแวร์ ในแวดวง Software Architecture เราเถียงกันตั้งแต่ Monolith ไปถึง Microservice แล้วกำลังย้อนกลับไปหาสมดุลระหว่างสองขั้วของมันอีกรอบ ในแวดวงธุรกิจ ก็มีค่านิยม Fail Fast ล้มแล้วลุกได้ สเกลโตได้ เปลี่ยนแปลงเร็ว ทดลองทำสิ่งใหม่ แสวงหาโอกาสใหม่ไปเรื่อยๆ
ซอฟต์แวร์คอมพิวเตอร์ยุคใหม่ โดยเฉพาะเมื่อถูกใช้เป็นแกนกลางที่ขับเคลื่อนระบบงานประจำวัน ผมสังเกตว่าระยะหลังงานวางระบบไม่ถูกถามถึงแผนสำรองกรณีระบบคอมพิวเตอร์ใช้งานไม่ได้ แปลว่าเราผ่าน point of no return มาแล้ว กิจการหลายแห่งวางเดิมพันไว้กับระบบคอมพิวเตอร์ เมื่อไฟฟ้าดับ อินเตอร์เน็ตล่ม กิจกรรมทุกอย่างได้แต่หยุด รอจนกว่าจะระบบกลับมาใช้งานได้
เช่นเดียวกับสาธารณูปโภค ไฟฟ้า น้ำประปา สื่อสาร (อินเตอร์เน็ต) หากไปไม่ถึง Economy Of Scale หรือผ่านจุด Peak มาแล้ว ถ้ามีใครสักคนขอซอร์สโค้ดเพื่อศึกษาหรือปรับปรุงให้ดีขึ้น ถือว่าเป็นโชควาสนาที่มีผู้สืบทอด
เจ้าของกับทาสเป็นสภาวะที่เกิดขึ้นพร้อมกัน ทาสแมวว่าไว้ การครอบครองซอฟต์แวร์มาพร้อมกับภาระที่ต้องดูแลให้ใช้งานได้ทันกับเทคโนโลยีที่เปลี่ยนแปลง ซึ่งมีรอบเวลาที่สั้นลงทุกที บางทีมันควรเป็นโมเดลป่าชุมชน ฟิตเนสในคอนโด สระว่ายน้ำในหมู่บ้าน การถูกครอบครองมีเจ้าของเบ็ดเสร็จผู้เดียวยากที่จะยืนระยะดูแล
การลงทุนซื้อซอฟต์แวร์ แล้วคาดหวังว่าใช้งานได้ยาวนาน โดยคำนวณว่ามีแต่ค่าดูแลรักษาให้ใช้งานได้ตามสัญญา Maintenance ในอัตราต่ำอาจใช้ไม่ได้แล้ว เพราะความคาดหวังว่า "ใช้งานได้" เท่าเดิมไปเรื่อยๆ อาจสะท้อนว่าล้าหลัง ยึดติด จนเสียโอกาสก็ได้ เรื่องนี้รวมไปถึงคำถามต่อหลายสิ่งหลายอย่าง ว่าจำเป็นต้องครอบครองหรือไม่ อาคารสำนักงาน รวมถึงบุคคลากรมืออาชีพบางตำแหน่ง
ความเชื่อเรื่องประโยชน์สูงสุดของตัวเอง เชื่อเรื่องการครอบครองเบ็ดเสร็จ มองโลกแยกส่วนไม่สนใจองคาพยพที่ค้ำจุนกัน ถึงเวลาที่เราได้มาทบทวนกัน
Comments