งานเก็บข้อมูลอาจทำงานได้ดีเมื่อมีเป้าหมายสิบแห่ง แต่ความน่าเชื่อถืออาจลดลงเมื่อขยายเป็นหลักพัน การจำแนกความล้มเหลวและการตัดข้อมูลซ้ำ การจำกัดอัตราและงานพร้อมกัน การทำงานต่อหลังหยุด การจัดการทางออกเครือข่าย การตรวจความสอดคล้อง และตัวชี้วัดหลักจึงสำคัญมากเมื่อระบบมีขนาดใหญ่
สคริปต์เก็บข้อมูลอาจทำงานได้ราบรื่นกับเป้าหมายสิบแห่ง แต่เมื่อขยายเป็นหลักพัน อัตราความสำเร็จอาจเริ่มลดลง แม้จะเพิ่มการ retry เปลี่ยน proxy และปรับ concurrency แล้ว ปัญหาก็ยังเกิดซ้ำ เมื่อตรวจลึกลงไป คอขวดมักไม่ได้อยู่ที่ parsing logic แต่อยู่ที่ชั้นวิศวกรรมหลายส่วนที่ยังไม่ได้สร้างขึ้น ในสเกลเล็ก ปัญหาเหล่านี้อาจไม่ปรากฏเลย
จำแนกความล้มเหลวก่อน แล้วการ retry จึงมีความหมาย
การเก็บข้อมูลย่อมมีความล้มเหลว สิ่งสำคัญคือการแยกประเภท: ความผันผวนของเครือข่ายและการ reset การเชื่อมต่อสามารถ retry ได้ทันที; การจำกัดอัตราชั่วคราวควร backoff ก่อน retry; หากโครงสร้างหน้าเปลี่ยนจน parsing result ว่าง การ retry หมื่นครั้งก็ไม่ช่วย ควรบันทึกและแจ้งเตือน; หากเป้าหมายไม่มีอยู่จริง ให้ทำเครื่องหมายว่างานเสร็จ; หาก environment หรือ network egress เริ่มไม่ได้ ให้เปลี่ยนตัวอื่นแล้วลองใหม่
การ retry ทุกอย่างแบบไม่แยกประเภทเป็นข้อผิดพลาดที่พบบ่อย มันซ่อนปัญหาที่ต้องใช้คนแทรกแซงไว้ใน loop และยังสิ้นเปลือง quota กับทรัพยากร egress โดยไม่จำเป็น Backoff ก็สำคัญ ช่วงเวลาระหว่าง retries ควรเพิ่มขึ้น ไม่เช่นนั้นงานทั้งชุดจะย้อนกลับไปยังเป้าหมายในช่วงเวลาเดียวกันและทำให้ rate limiting หนักขึ้น
Retries ยังนำไปสู่เรื่องการตัดข้อมูลซ้ำโดยตรง งานหนึ่งอาจถูกเรียกใช้หลายครั้งเพราะ retry ดังนั้นแต่ละงานต้องมีตัวระบุที่ไม่เปลี่ยนและไม่ซ้ำกัน เช่นค่าหลังทำ URL normalization และการเขียนลงฐานข้อมูลควรเป็น idempotent ตามตัวระบุนั้น มิฉะนั้นยิ่ง retry มาก ก็ยิ่งได้ข้อมูลสกปรกมาก
การจำกัดอัตราและ concurrency เป็นคนละเรื่อง
การเพิ่ม concurrency ไม่ได้หมายความว่า throughput จะเพิ่มตามเสมอ มีข้อจำกัดสามอย่างทำงานพร้อมกัน: เป้าหมายรองรับโหลดได้แค่ไหนก่อน rate limiting จะลด throughput รวม, หน่วยความจำและ CPU ของเครื่องภายใน, และ environment หรือ session เดียวสามารถรันหลายงานพร้อมกันได้หรือไม่
วิธีที่เสถียรกว่าคือเริ่มจาก concurrency ต่ำแล้วค่อย ๆ เพิ่มโหลด พร้อมดู success rate และ response time ไปด้วยเพื่อหาจุดที่ประสิทธิภาพเริ่มแย่ลงอย่างชัดเจน การจำกัดอัตราเป็นอีกเรื่องหนึ่ง เพราะควบคุมจังหวะการเข้าถึงเป้าหมายเดียวและไม่เหมือน global concurrency หากงานชุดหนึ่งกระจายไปหลายเว็บไซต์ แต่ละเว็บไซต์ควรมีจังหวะการเข้าถึงของตัวเอง
การทำงานต่อหลังหยุดต้องอาศัย state ที่บันทึกถาวร
สำหรับงานที่รันหลายชั่วโมง การหยุดกลางคันเป็นเรื่องปกติ การเริ่มใหม่ทั้งหมดมักมีต้นทุนสูงเกินไป จึงต้องบันทึก state ลง storage ได้แก่ pending, running, completed รวมถึงจำนวน retry, เวลาที่อนุญาตให้รันครั้งถัดไป และประเภทข้อผิดพลาด เมื่อ process เริ่มทำงาน ควรกู้ queue จาก storage แทนการสร้างใหม่จาก memory
การเก็บ queue ไว้เฉพาะใน memory เป็นวิธีที่พบได้บ่อยและดูเหมือนใช้งานได้ แต่เมื่อ process ล่ม งานที่รอทั้งหมดจะหายไปและจำนวนต่าง ๆ จะไม่ตรงกัน
แยกจัดการความล้มเหลวของ proxy และ network egress
การที่เป้าหมายบล็อก egress, proxy หลุด หรือ regional node เปลี่ยนสภาพจะเกิดขึ้นต่อเนื่องเมื่อระบบมีขนาดใหญ่ นี่ไม่ใช่ข้อยกเว้นแต่เป็นสภาพปกติ ควรมอง egress เป็นทรัพยากรที่เปลี่ยนทดแทนได้: เมื่อ task ล้มเหลว ให้แยกก่อนว่าเป็น rate limiting จากเป้าหมายหรือ egress ใช้งานไม่ได้; กรณีแรกใช้ backoff กรณีหลังเปลี่ยน egress แล้ว retry พร้อมบันทึก failure rate ของแต่ละ egress และถอดกลุ่มที่แย่ลงอย่างชัดเจนออก
ในทางกลับกัน หากทุก task ใช้ egress เดียวกัน task หนึ่งอาจทำให้เส้นทางมีปัญหาและกระทบงานทั้งหมดที่ตามมา ตอนตรวจสอบก็ต้องไล่ย้อน log เพื่อหาว่างานใดเป็นต้นเหตุ
ตรวจสอบความสอดคล้องของข้อมูล
รันสำเร็จไม่ได้หมายความว่าข้อมูลถูกต้อง หลังเขียนลง storage แล้วควรตอบคำถามได้ว่า จำนวน task ที่เสร็จตรงกับจำนวนแถวที่บันทึกหรือไม่, parsing result ว่างกี่เปอร์เซ็นต์, อัตราการหายของ critical fields เพิ่มขึ้นผิดปกติหรือไม่ และมีแถวซ้ำกี่แถว
การตรวจเหล่านี้ไม่จำเป็นต้องซับซ้อน การสุ่มตรวจเป็นราย batch ก็เพียงพอ แต่ต้องมีคนดูผลลัพธ์ เมื่อสเกลใหญ่ขึ้น ข้อมูลผิดอาจสร้างปัญหามากกว่าการไม่มีข้อมูล
ควรเฝ้าดู metric ใดบ้าง
ไม่จำเป็นต้องมี metric จำนวนมาก เลือกเพียงไม่กี่ตัวที่สะท้อนสุขภาพของระบบก็พอ
- Success rate และการกระจายของ failure types เพื่อดูว่าข้อผิดพลาดชนิดใดกำลังเพิ่มขึ้น
- ความยาวของ task queue และเวลาเฉลี่ยที่รอ หาก backlog เพิ่มขึ้นต่อเนื่อง แสดงว่า intake และ processing capacity ไม่สมดุลกัน
- จำนวน active environments และ processes ที่เกี่ยวข้อง หากเพิ่มขึ้นทางเดียวเป็นเวลานาน มักบ่งชี้ว่ามี leak ใน resource cleanup
- ปริมาณ output ต่อหน่วยเวลา เพื่อดูว่า throughput ถูก rate limiting กดไว้หรือไม่
- Egress failure rate เพื่อใช้ตัดสินใจว่าควรเปลี่ยนกลุ่ม nodes หรือไม่
หาก metric ใด metric หนึ่งเปลี่ยนไปในทิศทางเดียวเป็นเวลานาน ให้ตรวจ resource cleanup และ retry logic ก่อน
แยก environment layer ออกจาก scripts
เมื่อมองปัญหาเหล่านี้ร่วมกัน จะได้ข้อสรุปเดียวกันว่า environment layer ควรถูกจัดการแยกจาก scripts การทำ environment pooling ต้องให้ environments ถูก schedule จากส่วนกลาง แทนที่จะกระจายอยู่ในแต่ละ script; การคืนทรัพยากรต้องมี state ที่ query ได้ แทนการพึ่ง script แต่ละตัวให้แก้ปัญหาเอง; การ retry ด้วย environment หรือ egress อื่นจะทำได้ก็ต่อเมื่อ environments สามารถ schedule แยกกันได้
Scripts ดูแล logic ส่วน environment layer ดูแล resources และ identity ในสถาปัตยกรรมแบบนี้ PurpleMark ทำหน้าที่เป็นชั้นดังกล่าว โดยให้ environment resources ที่สร้างเป็น batch ได้ ผูกกับ network egress แยกอิสระได้ และ query status ได้
ขอบเขตด้านการปฏิบัติตามข้อกำหนด
การขยายสเกลได้ไม่ได้หมายความว่าจะเก็บข้อมูลอย่างไรก็ได้ ควรปฏิบัติตาม robots rules และเงื่อนไขการให้บริการของเว็บไซต์เป้าหมาย ไม่เก็บข้อมูลส่วนบุคคล ไม่หลีกเลี่ยงมาตรการป้องกันทางเทคนิค และควบคุมความถี่ของ request ไม่ให้กระทบการทำงานปกติของบริการ ความเสถียรเป็นปัญหาทางเทคนิค ส่วนการได้รับอนุญาตให้เก็บข้อมูลเป็นอีกเรื่องหนึ่ง ทั้งสองด้านต้องผ่านเงื่อนไข


