ข้ามไปยังเนื้อหา
AutoValue
17 ตุลาคม 2569 · Data Quality

สัปดาห์ที่เราเกือบเสียข้อมูลรถ 84,000 รายการ เพราะบั๊กที่เราเคยโทษพร็อกซี่

AutoValue Editorial
สัปดาห์ที่เราเกือบเสียข้อมูลรถ 84,000 รายการ เพราะบั๊กที่เราเคยโทษพร็อกซี่

14 กันยายน

เราเฝ้ารอวันนี้มาหลายสัปดาห์ ผู้ให้บริการพร็อกซี่แบบ residential ของเราหยุดให้บริการไปตั้งแต่เดือนกรกฎาคมเพราะปัญหาการเก็บเงิน และตลอดสองเดือนที่ผ่านมา สัดส่วนที่เพิ่มขึ้นเรื่อย ๆ จากทั้งหมด 1,375 แหล่งข้อมูลของเราก็ครอว์ลแบบลดคุณภาพหรือไม่ครอว์ลเลย — การดึงข้อมูลที่ไทม์เอาต์ ไอพีที่ถูกบล็อกกลางการทำงาน เป็นการไหลเลือดช้า ๆ แบบที่ไม่แสดงออกมาเป็นเหตุขัดข้องชัดเจน แต่เป็นตัวเลขที่ค่อย ๆ หดลงโดยไม่มีใครสังเกตจนกว่ามันจะน่าอายพอ เราต่ออายุสัญญา พูลใหม่ มีไลน์เซสชัน residential แบบหมุนไอพี 20,000 ไลน์ ตรวจสอบ exit ทุกตัวก่อนปล่อยให้เข้าใกล้โปรดักชัน การตรวจสอบผ่าน 100% ทั้ง AE, DE, JP — ทำงานได้ปกติทั้งหมด

เราจึงรันการครอว์ลแบบมีคนเฝ้าดู คือแบบที่มีคนจริง ๆ จับตาดูแดชบอร์ดแทนการเชื่อ cron อย่างเดียว ผมคาดว่าจะได้ผลตรงข้ามกับหายนะ

แต่สิ่งที่เราได้คือฐานข้อมูลที่เสียข้อมูลไป 84,401 แถวในการรันเพียงครั้งเดียว โดยมี 102 แหล่งข้อมูลที่ลดลงมากกว่า 25% จากสแนปช็อตก่อนหน้า แย่กว่าสองเดือนที่ขาดพร็อกซี่ที่เราเพิ่งแก้ไปเสียอีก

สมมติฐานที่ผิด แบบสั้น ๆ

สัญชาตญาณแรกของผม — และผมจะยอมรับตรง ๆ — คือคิดว่าพูลใหม่นั้นแย่กว่าที่การตรวจสอบบอกไว้ไม่ทางใดก็ทางหนึ่ง บางทีไอพีของเซสชันอาจถูกจำกัดอัตรา (rate-limited) กลางการครอว์ลในแบบที่การตรวจสอบครั้งเดียวจับไม่ได้ เป็นสมมติฐานที่สมเหตุสมผล แต่ก็ผิด และการเชื่อแบบนั้นแม้เพียงครึ่งวันก็ทำให้เราเสียเวลาที่เราไม่มีให้เสีย เพราะ cron ของการครอว์ลจะถูกหยุดพักไว้ตลอดช่วงที่คุณกำลังไล่ตรวจสมมติฐานแบบนั้น — คุณไม่อยากให้การรันที่พังไปเขียนทับสแนปช็อตที่ดีอยู่ขณะที่คุณยังเดากันอยู่

สัญญาณที่ทำให้สมมติฐานนั้นพังคือ dubizzle หนึ่งในแหล่งข้อมูล UAE ที่มีปริมาณสูงสุดของเรา จริง ๆ แล้วมันดึงข้อมูลได้ 32,595 รายการในการรันครั้งนั้น — ตัวเลขที่ปกติดีมาก พร็อกซี่ทำงานได้ชัดเจน — แล้วก็ทิ้งข้อมูลไปเกือบทั้งหมด ไม่ใช่ดึงไม่สำเร็จ แต่ดึงมาได้แล้วทิ้งไป

16 กันยายน: ตามหาบั๊กตัวจริง

นั่นคือจุดที่เรื่องนี้เลิกเป็นเรื่องพร็อกซี่และกลายเป็นเรื่องการแทนที่แถวข้อมูล (row-replacement) เลเยอร์การจัดเก็บข้อมูลของเรานำสแนปช็อตของแต่ละแหล่งข้อมูลมาเขียนทับทั้งไฟล์ <site>_cars_for_sale.csv ทุกครั้งที่รัน — เฉพาะรายการที่ยังขายอยู่ เขียนทับทั้งตาราง นั่นคือพฤติกรรมที่ถูกต้องเมื่อการครอว์ลเสร็จสมบูรณ์ แต่มันคือหายนะเมื่อการครอว์ลไทม์เอาต์ไปครึ่งทางและสคริปต์เขียนข้อมูลชุดที่ดึงมาได้บางส่วน เสมือนว่ามันคือสแนปช็อตทั้งหมด แหล่งข้อมูลที่ปกติมีรายการขายอยู่ 9,000 รายการ ถูกเขียนทับด้วยแค่ 1,200 รายการที่ดึงได้ก่อนเวลาจะหมด แล้วอีก 7,800 รายการก็หายไป — ไม่ใช่เพราะรถถูกขายไปแล้ว แต่เพราะโค้ดของเราเองตัดสินว่าการดึงข้อมูลที่ไม่สมบูรณ์คือสแนปช็อตที่สมบูรณ์

พร็อกซี่ไม่ได้โกหกเรา ตรรกะการลองซ้ำ (retry logic) คือตัวปัญหา และมันแย่แบบเงียบ ๆ มานานแล้ว เพียงแต่ถูกกลบไว้ด้วยพูลที่สุขภาพดีกว่าซึ่งเคยดึงข้อมูลเสร็จก่อนที่การลองซ้ำจะมีผล

นี่คือต้นทุนจริงของรอบการลองซ้ำนั้น เมื่อเราลองคำนวณดู

รอบลองซ้ำแบบเดิมรอบที่แก้ไขแล้ว
จำนวนครั้งลองซ้ำต่อพร็อกซี่31
ระยะหน่วงต่อครั้ง1 วินาที—
เพดานเวลารวมสูงสุดต่อพูล 50 พร็อกซี่~56 นาทีต่อ URLงบเวลารวม 120 วินาที
พฤติกรรมเมื่อไทม์เอาต์เขียนชุดข้อมูลบางส่วนเป็นสแนปช็อตเต็มข้ามแหล่งข้อมูลนั้น เก็บสแนปช็อตก่อนหน้าไว้

56 นาทีกว่าจะยอมแพ้กับ URL เดียว ลองคูณด้วยการครอว์ลที่แตะแหล่งข้อมูลกว่าพันแหล่ง แล้วคุณก็จะได้ความล้มเหลวแบบเงียบ ๆ และกระจายไม่เท่ากันแบบที่เราเจอพอดี — บางแหล่งข้อมูลปกติดี 102 แหล่งถูกเฉือนเนื้อไป ไม่มีอะไรในล็อกที่ตะโกนว่า "พร็อกซี่ล่ม" เพราะในทางเทคนิคแล้ว มันไม่ได้ล่ม

25 กันยายน: การแก้ไข และส่วนที่สำคัญกับคุณ

แพตช์ที่ปล่อยออกมาเก้าวันต่อมาทำสองอย่างที่ไม่หวือหวาแต่ได้ผล มันจำกัดเพดานการคำนวณรอบลองซ้ำให้การดึงข้อมูลที่ติดขัดล้มเหลวเร็วขึ้นแทนที่จะกินเวลาเกือบทั้งชั่วโมง และ — นี่คือส่วนที่ป้องกันการสูญเสียข้อมูลจริง ๆ — การดึงข้อมูลบางส่วนจะไม่เขียนทับชุดแถวข้อมูลทั้งหมดอีกต่อไป ถ้าแหล่งข้อมูลหนึ่งดึงได้แค่ 1,200 จาก 9,000 รายการตามปกติ เราจะเก็บสแนปช็อตที่สมบูรณ์ก่อนหน้าไว้ แทนที่จะเขียนทับด้วยข้อมูลเสี้ยวเดียว ในวันเดียวกันนั้น การเปลี่ยนแปลงอีกอย่างก็ถูกนำมาใช้ คือการเขียนแฟล็ก CRAWL_PAUSED ที่มองเห็นได้ ซึ่งระบบมอนิเตอร์ของเราจะตรวจสอบก่อนจะแจ้งเตือนใครเกี่ยวกับการลดลงของข้อมูล — เพื่อให้การหยุดพักที่ตั้งใจทำถูกอ่านว่าเป็นการหยุดพัก ไม่ใช่การแจ้งเตือนเหตุขัดข้อง

ทำไมผู้อ่านที่กำลังมองหารถ Hyundai มือสองควรสนใจเรื่องนี้ด้วย? เพราะป้ายบอกราคาต่ำกว่าตลาด (below-market flags) ที่เราเผยแพร่ — ซึ่งมีอยู่ 12,823 ป้ายที่ใช้งานจริงบนเว็บไซต์ ณ สัปดาห์นี้ — จะซื่อตรงได้ก็ต่อเมื่อกลุ่มข้อมูลที่เรานำรายการนั้นไปเทียบ เป็นตลาดระดับภูมิภาคที่แท้จริงและสมบูรณ์ ไม่ใช่เศษข้อมูลที่ถูกตัดทิ้งจากการไทม์เอาต์ ป้ายดีลที่สร้างขึ้นจากสแนปช็อตที่สูญเสียกลุ่มเปรียบเทียบไปสามในสี่ ไม่ใช่ป้ายดีล มันคือสัญญาณรบกวนที่สวมป้ายสีเขียว เราขอหยุดการครอว์ลไว้เก้าวันดีกว่าที่จะเผยแพร่แบบนั้น

วันนี้ชุดข้อมูลของเรามีรายการอยู่ 1,108,535 รายการ ครอบคลุม 73 ภูมิภาค จาก 1,375 แหล่งข้อมูล — ใหญ่กว่าก่อนที่เรื่องทั้งหมดนี้จะเริ่มต้นเสียอีก พร็อกซี่ได้รับการต่ออายุและบั๊กการแทนที่แถวข้อมูลก็ถูกปิดไปแล้ว แต่ตัวเลขที่ผมยังคงกลับมานึกถึงอยู่เสมอไม่ใช่ตัวเลขรวมนั้น มันคือ 84,401 — ช่องว่างระหว่างการครอว์ลที่ดูเหมือนปกติดีในระดับพร็อกซี่ กับการครอว์ลที่ปกติดีจริง ๆ

Data QualityWEB ScrapingCrawl ReliabilityBelow Market Deals
แชร์

บทความที่เกี่ยวข้อง