𝗛𝗼𝘄 𝗜 𝗕𝘂𝗶𝗹𝘁 𝗮 𝗦𝗲𝘁 𝗜𝘁 𝗮𝗻𝗱 𝗙𝗼𝗿𝗴𝗲𝘁 𝗜𝘁 𝗦𝘆𝗻𝗰 𝗦𝘆𝘀𝘁𝗲𝗺 วิธีที่ผมสร้างระบบ Sync แบบ Set It and Forget It

ราคาสินค้ามีการเปลี่ยนแปลงในหลายที่ ไม่ว่าจะเป็นใน admin panel, ผ่านการนำเข้าข้อมูลแบบ bulk imports หรือผ่าน API webhooks

หากคุณต้องการซิงค์ (sync) การเปลี่ยนแปลงเหล่านี้ไปยัง marketplace ภายนอก คุณจะพบกับปัญหา การเพิ่มคำสั่ง sync เข้าไปในทุกๆ เส้นทางของโค้ด (code path) คือความผิดพลาด เพราะคุณอาจจะลืมบางจุด หรือทำบางจุดพัง ซึ่งจะทำให้การดูแลรักษา (maintenance) กลายเป็นฝันร้าย

Django signals สามารถแก้ปัญหานี้ได้ โดยคุณสามารถ hook เข้ากับ event การ save ของ model ซึ่งจะช่วยดักจับทุกการเปลี่ยนแปลงได้ในที่เดียว

แต่ signals ก็มีข้อเสีย หากคุณอัปเดตราคา 100 รายการพร้อมกัน signal จะทำงานถึง 100 ครั้ง ซึ่งจะไปกระตุ้นให้เกิด API calls ถึง 100 ครั้ง ส่งผลให้คุณติด rate limits หรือสิ้นเปลืองทรัพยากรโดยไม่จำเป็น

ผมใช้รูปแบบ 3 ส่วนในการแก้ไขปัญหานี้:

• Signal handler ที่ทำหน้าที่เก็บรวบรวม ID แทนที่จะทำงานทันที • การใช้ set แบบ per-thread เพื่อกำจัดข้อมูลที่ซ้ำกัน • Flush callback ที่ใช้ transaction.on_commit เพื่อประมวลผลทุกอย่างพร้อมกันในคราวเดียว

นี่คือวิธีการทำงานของมัน

  1. ใช้ threading.local() อย่าใช้ตัวแปร global เพราะตัวแปร global จะแชร์ state ร่วมกันในทุกๆ request ซึ่งอาจนำไปสู่ปัญหาข้อมูลรั่วไหล (data leakage) การใช้ threading.local() จะช่วยแยกข้อมูลให้เป็นอิสระเฉพาะในแต่ละ thread เท่านั้น

  2. บันทึกไว้ แต่อย่าเพิ่งทำทันที Signal handler จะทำเพียงแค่เพิ่ม product ID ลงใน set จากนั้นจะบอกให้ Django รันฟังก์ชัน flush เฉพาะหลังจากที่ database transaction สำเร็จแล้วเท่านั้น วิธีนี้จะช่วยป้องกันการซิงค์ข้อมูลที่บันทึกไม่สำเร็จ

  3. ทำงานแบบเป็นชุด (Batch) เมื่อ transaction ทำการ commit ฟังก์ชัน flush จะเริ่มทำงาน โดยจะทำการคัดลอก set นั้นไว้แล้วล้างค่าเดิมทิ้ง จากนั้นจึงส่งรายการ ID ทั้งหมดไปยัง service layer

Service layer จะทำการ query แบบ bulk เพียงครั้งเดียวเพื่อดึงข้อมูลสินค้าทั้งหมด แล้วจัดกลุ่มตามร้านค้า (store) สุดท้ายจะส่ง task เพียงหนึ่งเดียวไปยัง Celery ต่อหนึ่งร้านค้า

ข้อดีนั้นชัดเจนมาก:

คุณสร้างระบบเพียงครั้งเดียว และทุกฟีเจอร์ใหม่ที่คุณเพิ่มเข้ามาในภายหลังจะทำงานร่วมกับระบบ sync นี้โดยอัตโนมัติ

แล้วคุณล่ะ มีวิธีจัดการการซิงค์กับ external API ใน Django อย่างไร? คุณใช้ signals หรือใช้รูปแบบอื่น?

Source: https://dev.to/acel/how-i-built-a-set-it-and-forget-it-sync-system-with-django-signals-2ld7