- บทความเตือนถึงข้อผิดพลาดของงาน cron ที่เกิดขึ้นระหว่างการเปลี่ยนเวลา เวลาออมแสง (DST) บนเซิร์ฟเวอร์ Linux
- ปีละสองครั้ง จะมีการเปลี่ยนเขตเวลาใน เช้ามืดวันอาทิตย์เวลา 2:00 หรือ 3:00 น. ทำให้งาน cron อาจถูกรันซ้ำหรือข้ามการรันไป
- มีกรณีจริงในสภาพแวดล้อม vixie-cron ที่งานช่วง 3:00~3:01 ถูก รันซ้ำ 60 ครั้งทุก ๆ 1 วินาที จนเกิดอีเมลถล่ม
- แนวทางแก้ไขคือ ตั้งค่าเขตเวลาเป็น UTC หรือ หลีกเลี่ยงการตั้งงานในช่วงเวลาดังกล่าว และเสนอให้พัฒนา ตัวจัดตารางงานโอเพนซอร์ซ ที่ดีกว่าเดิม
- เป็นกรณีที่ย้ำเตือนผู้ดูแลเซิร์ฟเวอร์และวิศวกร DevOps ถึง ความสำคัญของการจัดการความเสี่ยงจากการเปลี่ยนเขตเวลา
การชนกันระหว่างเวลาออมแสงกับงาน cron
- หากตั้งงาน cron ไว้ในช่วงเช้ามืดวันอาทิตย์เวลา 2:00 หรือ 3:00 น. อาจเกิดข้อผิดพลาดในการรันโดยไม่คาดคิดเมื่อชนกับช่วงเปลี่ยน เวลาออมแสง (DST)
- ตอนเริ่ม DST นาฬิกาจะเดินไปข้างหน้าหนึ่งชั่วโมง และตอนสิ้นสุดจะถอยหลังหนึ่งชั่วโมง ทำให้เกิด เวลาซ้ำหรือเวลาหายไป
- ผลคือ งานในช่วงเวลานั้นอาจ ถูกรันสองครั้งหรือไม่ถูกรันเลย
- โดยเฉพาะงานที่รันทุกสัปดาห์ในเช้ามืดวันอาทิตย์จะได้รับผลกระทบใน ช่วงเปลี่ยน DST ปีละ 2 ครั้ง
- โดยปกติจะทำงานได้ไม่มีปัญหา แต่ในวันที่มีการเปลี่ยน DST อาจเกิด การรันซ้ำแบบผิดปกติ
กรณีจริง: ปัญหาการรันซ้ำของ vixie-cron
- มีรายงานกรณีใน vixie-cron บน Linux ว่าเมื่อเริ่ม DST งานช่วง 3:00~3:01 ถูก รันประมาณ 60 ครั้งในช่วงห่างกัน 1 วินาที
- แต่ละงานชนกันเองและก่อให้เกิดความวุ่นวาย เช่น อีเมลแจ้งเตือนถล่ม
- โชคดีที่งานดังกล่าวไม่ใช่งานวิกฤต จึงไม่ทำให้ระบบเสียหาย
- ปัญหานี้เกิดจาก โครงสร้างการจัดตารางแบบอิงเวลาอย่างเรียบง่ายของ cron
- cron ไม่รับรู้การเปลี่ยนเขตเวลาหรือการสลับ DST แต่จะรันตามเวลาที่กำหนดไว้อย่างตรงไปตรงมา
แนวทางแก้ไขและทางเลือก
- วิธีที่ง่ายที่สุดคือ อย่าตั้งงานในช่วงเช้ามืดวันอาทิตย์เวลา 2:00 และ 3:00 น.
- หากหลีกเลี่ยงช่วงเวลานี้ ก็สามารถเลี่ยงปัญหาการรันซ้ำจาก DST ได้ทั้งหมด
- อีกทางเลือกที่ได้ผลคือ ตั้งเขตเวลาของเซิร์ฟเวอร์เป็น UTC
- UTC ไม่ใช้เวลาออมแสง จึงไม่มีการเปลี่ยนเวลา
- ทางออกที่เป็นรากฐานกว่านั้นคือการ พัฒนาตัวจัดตารางงานที่ฉลาดกว่าเดิม
- จำเป็นต้องมี เครื่องมือทดแทนแบบโอเพนซอร์ซ ที่มีความสามารถอย่างการป้องกันการรันซ้ำ การจำกัดเวลารัน และการรับรู้เขตเวลา
ข้อเสนอระยะยาว: ยกเลิกเวลาออมแสง
- ผู้เขียนเสนอว่า การยกเลิก DST ในระดับนโยบายภาครัฐ คือทางออกที่ดีที่สุด
- การเปลี่ยนเวลาปีละสองครั้งสร้างความซับซ้อนที่ไม่จำเป็นทั้งต่อการดูแลระบบและชีวิตประจำวันของผู้คน
- อย่างไรก็ตาม ตราบใดที่ DST ยังถูกใช้อยู่ ผู้ดูแลระบบและวิศวกร DevOps ก็ต้องมีมาตรการป้องกัน
- โดยเฉพาะกับงานที่อาศัยเวลา เช่น งานแบตช์อัตโนมัติ การสำรองข้อมูล และการหมุนเวียนล็อก ต้องจัดการอย่างระมัดระวัง
สรุป: หลักการตั้งตาราง cron อย่างปลอดภัย
- ควร หลีกเลี่ยงงานในช่วง 2:00~3:00 น. ตอนมีการเปลี่ยน DST
- หากเป็นไปได้ ควร ให้เซิร์ฟเวอร์ทำงานบน UTC เพื่อตัดปัญหาเรื่องเขตเวลา
- ควรตระหนักถึงข้อจำกัดของ cron และพิจารณา ใช้เครื่องมือจัดตารางงานที่แข็งแกร่งกว่า
- ในสภาพแวดล้อม DevOps การ จัดการเขตเวลาและทำให้ระบบอัตโนมัติน่าเชื่อถือ เป็นเรื่องสำคัญอย่างยิ่ง
2 ความคิดเห็น
หลังจากที่งานที่ผมตั้งไว้ตอนตี 2 ก็เกิดปัญหาเหมือนกัน (บางครั้งรันสองรอบ บางครั้งก็ไม่รันเลย) ตั้งแต่นั้นมาผมก็เลี่ยงเวลานั้นอย่างเด็ดขาด
ความคิดเห็นใน Hacker News
คิดว่า DST (เวลาออมแสง) เป็นระบบที่ผิดพลาดโดยสิ้นเชิง
มันไม่ได้แก้ปัญหาอะไรเลย แถมยังสร้างแต่ความไม่สะดวก
แค่ยึด เวลามาตรฐาน ไว้ตลอด แล้วเลื่อนเวลาทำงานให้เร็วขึ้นหนึ่งชั่วโมงแบบช่วงหน้าร้อนก็พอ
ตัวอย่างเช่น เปิดร้าน 6 โมงแทน 7 โมง และปิด 3 ทุ่มแทน 4 ทุ่ม
ยังไงก็ต้องมีช่วงปรับตัวปีละสองครั้งอยู่แล้ว ดังนั้นเปลี่ยนแค่ครั้งเดียวก็พอ
ฉันอยากเห็นแสงแดดหลังเลิกงานมากกว่า โดยเฉพาะในฤดูหนาว และไม่สนว่าแดดจะขึ้นตอนเดินทางไปทำงานหรือไม่
เพราะยังไงก็ต้องถูกขังอยู่ในอาคาร 9 ชั่วโมง เลยอยากเห็นแสงแดดตอนที่ยังมีเวลาส่วนตัว
เช่น การทดลองใช้ DST ตลอดทั้งปีในสหรัฐฯ ช่วงปี 1973~1975
ตอนแรกกระแสสนับสนุนสูงด้วยเหตุผลอย่างการประหยัดพลังงานและอาชญากรรมที่ลดลง
แต่สุดท้ายกระแสตีกลับอย่างรุนแรงเพราะ อุบัติเหตุระหว่างเดินทางไปโรงเรียนในตอนเช้าที่มืด จนต้องยกเลิก
(อ้างอิงจาก Wikipedia)
ฟังดูเหมือนแค่อยากเลื่อนเวลาให้มากขึ้นเท่านั้น
มีแค่เวลามาตรฐานกับเวลาออมแสงเท่านั้น
การพูดว่าอยากใช้ ‘เวลาฤดูหนาว’ ตลอดทั้งปีฟังดูไม่ดึงดูดทางจิตวิทยาเท่าไร
ผู้คนอยากเริ่มวันตอนที่ยังมีแสงอาทิตย์
โปรแกรมเมอร์ต้องลำบากเพราะ DST ก็จริง แต่ผมคิดว่านั่นคือปัญหาที่ต้องจัดการ edge case ให้ดี
แต่ละคนมีรูปแบบการนอนต่างกัน ถ้าเลือกเวลาทำงานที่เหมาะกับตัวเองได้ ความขัดแย้งก็น่าจะลดลง
ตอนเคยเซ็ตอัป เซิร์ฟเวอร์ของ reddit ผมตั้งทั้งหมดเป็น เขตเวลา Arizona
เพราะ Arizona ไม่ใช้ DST เลยต่างจากแคลิฟอร์เนียแค่ 1 ชั่วโมง
เหตุผลที่ไม่ใช้ UTC คือเวลาอ่านล็อกนั้น ‘ลบ 1 ง่ายกว่าลบ 8’
ตอนนี้มีทีมระดับโลกแล้วเลยเปลี่ยนมาใช้ UTC
ทางที่ดีควรตั้งทุกอย่างเป็น UTC ตั้งแต่แรก
เรื่องแบบนี้เกิดขึ้นบ่อยทั่วโลก จนสำหรับนักพัฒนาแอปปฏิทินแล้วถือเป็นงานชวนปวดหัวมาก
ถ้าเลือก UTC แล้ว ก็ควรสมมติว่าผู้ใช้เข้าใจรูปแบบ YYYY-MM-DD แบบ 24 ชั่วโมงได้
แต่ในองค์กรมีคนเปลี่ยนไปใช้ PST กันคนละแบบ จนล็อกแต่ละตัวเวลาคนละชุดและสร้างความสับสน
สุดท้ายหัวหน้าต้องออกมาจัดการให้ ทุกแอปพลิเคชันและฐานข้อมูลใช้ PST เหมือนกันหมด
แต่ตอนนี้ก็เริ่มตีความเวลา UTC ได้แบบอัตโนมัติแล้ว
เคยตั้งเซิร์ฟเวอร์ของบริษัทเป็น BST (เวลาอังกฤษฤดูร้อน) และใช้ cron เยอะมาก
ทำให้เกิด ความสับสนซ้ำ ๆ ปีละสองครั้ง
สุดท้ายก็แก้ไม่ทันจนบริษัทปิดตัวไป
บทเรียนง่าย ๆ คือ — ใช้ UTC ซะ ถ้าไม่มีเหตุผลพิเศษ
ตัวอย่างเช่น การรีเซ็ตโควตาการใช้งาน หรือ งานแบตช์สำหรับการคิดเงิน มักรันตามเวลาท้องถิ่น
เช่น รายงาน 8 โมงเช้าในสหราชอาณาจักร ซึ่งเวลา UTC จะเปลี่ยนไปตาม DST
เพราะฉะนั้น UTC อย่างเดียวไม่พอ และต้อง เก็บข้อมูลเขตเวลาไว้ด้วย
บางประเทศ (Cuba, Egypt, Lebanon) มีการ เปลี่ยน DST ตอนเที่ยงคืน
(ลิงก์ที่เกี่ยวข้อง)
ในบราซิล การเปลี่ยนที่ช่วง 00:00~01:00 หรือ 00:00~23:00 เคยเป็นเรื่องปกติ
มีงานวิจัยที่บอกว่าในวันที่มีการปรับ DST อัตราการเสียชีวิตและการเข้าห้องฉุกเฉินพุ่งสูงขึ้น
(บทความจาก ScienceAlert)
นี่ไม่ใช่แค่ปัญหาเรื่อง cron เท่านั้น แต่ DST ยังเป็น ระบบที่ส่งผลเสียต่อสุขภาพ ด้วย
ปัญหานี้เคยถูกแก้ไว้แล้วตั้งนานใน OpenBSD และ Debian
ในคู่มือ cron(8) ของ Debian มีคำอธิบายลอจิกสำหรับ การจัดการการเลื่อนเวลาที่น้อยกว่า 3 ชั่วโมง ตอนปรับ DST
(ลิงก์แพตช์,
คู่มือ,
รายงานบั๊ก)
คำแนะนำคืออย่าตั้งงานไว้ตอน เที่ยงคืน (00:00)
เพราะวันที่มักทำให้สับสนได้ง่าย ควรใช้เวลาแบบ ก้ำกึ่ง อย่าง 00:01 หรือ 01:45 แทน
เวลาเครื่องมีอาการแปลกในช่วงเวลาหนึ่งจะได้ตามหาสาเหตุได้ง่าย
แต่ในสภาพแวดล้อมที่ใช้เวลารูปแบบ 12 ชั่วโมงก็อาจทำให้สับสนได้
กว่าจะเจอปัญหาเรื่องเขตเวลาก็ไม่เคยรู้มาก่อนว่า
ในโลกนี้มีทั้ง เวลาที่ไม่มีอยู่จริง และ เวลาที่กำกวม
ตัวอย่างเช่น ตอนข้ามจาก 2 โมงไป 3 โมง เวลา 2:30 จะไม่มีอยู่จริง
แต่ตอนหมุนนาฬิกากลับ เวลา 2:30 จะเกิดขึ้นสองครั้ง
แต่ละประเทศก็ปรับ DST คนละเวลา จนมีกรณีอย่าง Chile ที่เที่ยงคืนหายไปเลย
(บล็อกที่เกี่ยวข้อง)
จำได้ว่าตอนเริ่มทำงานที่ Stripe ใหม่ ๆ Java กับ Ruby จัดการสถานการณ์นี้ต่างกัน จนเกิด เหตุขัดข้องติดกัน 3 ครั้ง
งาน cron ควร หลีกเลี่ยงการรันตรงเวลาเป๊ะ และถ้าเป็นไปได้ให้ตั้ง หลังตี 4 หรือก่อนเที่ยงคืน
เพราะบนโครงสร้างพื้นฐานที่ใช้ร่วมกัน มักเกิดการแย่งทรัพยากรหนักในช่วงตรงชั่วโมง
sleep $(( $(od -N1 -tuC -An /dev/urandom) % 60 ))m ;หน้า cron commandเพื่อเพิ่ม ดีเลย์แบบสุ่ม 0~59 นาที ก็เป็นวิธีที่ดี
งานตามกำหนดเวลาที่สำคัญควรออกแบบให้ idempotent (รันซ้ำได้อย่างปลอดภัย) ถ้าเป็นไปได้
โดยเฉพาะเมื่อมีระบบคิวเข้ามาเกี่ยวข้อง การออกแบบแบบนี้คือ หัวใจสำคัญของการป้องกันปัญหา