เขียนไฟล์ JSON ทับที่เดิมอันตรายไหม: เทคนิคเขียนโค้ดไม่ให้พัง
เจาะลึกปัญหาเขียนไฟล์ JSON แบบเปิดช่องให้คนอ่านเจอข้อมูลครึ่งๆ กลางๆ พร้อมวิธีแก้ด้วย temp file, os.replace และ os.fsync ใน Python

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
- การเขียนไฟล์ JSON ทับที่เดิมตรงๆ เสี่ยงทำให้โปรแกรมอ่านเจอไฟล์ว่างหรือพัง
- การใช้ temp file ร่วมกับ os.replace ช่วยให้อ่านไฟล์ได้สมบูรณ์แบบ atomic operation
- ต้องสร้างไฟล์ชั่วคราวในโฟลเดอร์เดียวกันเพื่อป้องกันปัญหาคนละ filesystem
- อย่าลืมซิงค์ข้อมูลลงดิสก์ด้วย os.fsync เพื่อป้องกันการสูญหายยามระบบล่ม
นักพัฒนาหลายคนอาจคุ้นเคยกับการเขียนไฟล์ข้อมูลด้วยคำสั่งเปิดไฟล์ธรรมดา เช่น with open(PATH, 'w') เพื่อบันทึกข้อมูลใหม่ลงไปทับไฟล์เดิมทันที แต่รู้หรือไม่ว่าวิธีนี้อาจซ่อนปัญหาใหญ่เอาไว้ หากมีกระบวนการอื่นพยายามเข้ามาอ่านไฟล์นั้นในจังหวะที่ไฟล์กำลังถูกเขียนซ้ำพอดี โปรแกรมอ่านจะเจอข้อผิดพลาดประเภทรวมถึง JSONDecodeError ทันที เนื่องจากไฟล์ถูกล้างข้อมูลจนว่างเปล่าแต่ยังเขียนข้อมูลชุดใหม่ไม่เสร็จสิ้น
จากการทดสอบบนสภาพแวดล้อม Linux ที่ใช้ Python 3.10.12 พบว่าปัญหาดังกล่าวไม่ได้เกิดขึ้นได้ยากอย่างที่คิด ขนาดของไฟล์ที่ใหญ่ขึ้นยิ่งเพิ่มช่องว่างให้โปรแกรมอ่านเข้ามาสุ่มเจอจังหวะที่ไฟล์ไม่สมบูรณ์ได้ง่ายยิ่งขึ้น และหากกระบวนการเขียนถูกตัดจบกะทันหันด้วยคำสั่ง SIGKILL ไฟล์จะเสียหายคาอยู่แบบนั้นจนกว่าจะมีรอบเขียนใหม่มาทับ

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
แนวทางแก้ไขที่ถูกต้องคือการเขียนข้อมูลใหม่ลงในไฟล์ชั่วคราว (temporary file) ให้เสร็จสมบูรณ์เสียก่อน จากนั้นจึงใช้คำสั่งเปลี่ยนชื่อไฟล์เพื่อสับเปลี่ยนกับไฟล์เดิมในขั้นตอนเดียวตามข้อกำหนด POSIX ของฟังก์ชัน os.replace ซึ่งรับประกันว่าโปรแกรมอ่านจะเห็นแค่ไฟล์เก่าหรือไฟล์ใหม่แบบเต็มๆ เท่านั้น ไม่มีทางเห็นข้อมูลครึ่งๆ กลางๆ
อย่างไรก็ตาม การใช้งานเทคนิคนี้ยังมีกับดักสำคัญที่นักพัฒนาต้องระวัง:
- ระวังเรื่อง Filesystem ต่างกัน: การเรียกใช้งาน
tempfile.mkstemp()โดยไม่ระบุโฟลเดอร์ ระบบจะเลือกใช้ไดเรกทอรีชั่วคราวกลาง ซึ่งบนเครื่องทดสอบพบว่า/tmpและโฟลเดอร์โปรเจกต์อยู่คนละดิสก์กัน ทำให้คำสั่งos.replaceล้มเหลว การสร้างไฟล์ชั่วคราวในโฟลเดอร์เป้าหมายโดยตรงจึงปลอดภัยที่สุด - สิทธิ์การเข้าถึงไฟล์ถูกจำกัด: ฟังก์ชัน
mkstempจะสร้างไฟล์ที่มีสิทธิ์ให้เจ้าของอ่านและเขียนได้เท่านั้น จึงต้องคัดลอกโหมดเดิมของไฟล์เก่ามาใส่ด้วยคำสั่งos.chmodเสมอ - ปัญหาเรื่อง Symlink และ Hard Link: หากไฟล์เป้าหมายเป็น Symlink การแทนที่ไฟล์จะเปลี่ยนเป้าหมายของลิงก์ ส่วน Hard link จะยังคงชี้ไปยังเนื้อหาชุดเก่าเหมือนเดิม
การทำ Atomic Write ถือเป็นหัวใจสำคัญในการพัฒนาซอฟต์แวร์ที่ต้องจัดการกับสถานะ (State) ของข้อมูลในรูปแบบไฟล์ เนื่องจากระบบปฏิบัติการสมัยใหม่มักจะมีการแคชข้อมูลไว้ในหน่วยความจำชั่วคราว การใช้ฟังก์ชันอย่าง os.fsync() ทั้งกับไฟล์และไดเรกทอรีจึงเป็นปราการด่านสุดท้ายที่ช่วยบังคับให้ข้อมูลถูกเขียนลงสู่ฮาร์ดดิสก์จริงๆ ป้องกันไฟล์พังยามเกิดไฟดับหรือระบบล่มกะทันหัน

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
ที่มา: Dev.to
พบข้อมูลผิดพลาดในบทความนี้? แจ้งปัญหาบทความนี้
ความคิดเห็น
แสดงความคิดเห็น