14 вересня
Ми чекали на цей день кілька тижнів. Наш провайдер резидентних проксі замовк через збій білінгу ще в липні, і два місяці дедалі більша частка наших 1 375 джерел сканувалася з перебоями або не сканувалася взагалі — запити вилітали за таймаут, IP-адреси блокувалися посеред сесії, типова повільна кровотеча, яка не виглядає як збій, а радше як цифра, що потроху зменшується, поки хтось не помітить незручну правду. Ми продовжили підписку. Новий пул, 20 000 ротаційних резидентних сесійних ліній, кожен вихідний вузол прозондований ще до того, як йому дозволили наблизитися до продакшну. 100% зондування пройшло успішно. ОАЕ, Німеччина, Японія — усі живі, усі чисті.
Тож ми запустили контрольоване сканування — таке, за яким хтось справді стежить на дашборді, а не просто довіряє крону. Я очікував протилежного до катастрофи.
А отримали базу даних, яка за один прохід втратила 84 401 рядок, причому 102 джерела впали більш ніж на 25% порівняно з попереднім знімком. Гірше, ніж два місяці голодування без проксі, які ми щойно виправили.
Коротко про хибну теорію
Моя перша думка — і я чесно визнаю цю помилку — полягала в тому, що новий пул чомусь гірший, ніж показало зондування. Можливо, сесійні IP-адреси потрапляли під обмеження швидкості посеред сканування так, що одноразове зондування цього не вловлювало. Теорія цілком логічна. І водночас хибна, а віра в неї навіть протягом півдня коштувала нам часу, якого в нас не було, бо крон сканування залишається на паузі весь час, поки перевіряєш подібну теорію — не хочеться, щоб невдалий прохід перезаписав хороший знімок, поки ти ще тільки гадаєш.
Ознака, що зламала цю теорію, — dubizzle. Одне з наших найбільших за обсягом джерел в ОАЕ, воно під час того прогону фактично завантажило 32 595 оголошень — цілком здорове число, проксі явно працювали, — а потім відкинуло майже всі з них. Не "не вдалося завантажити". А завантажило — і потім викинуло.
16 вересня: пошук справжнього баг
Саме тоді історія перестала бути про проксі й стала історією про заміну рядків. Наш шар збереження даних робить знімок кожного джерела як повний перезапис <site>_cars_for_sale.csv на кожному проході — тільки актуальні оголошення, заміна всієї таблиці. Це коректна поведінка, коли сканування завершується повністю. Але це катастрофа, коли сканування обривається за таймаутом на половині шляху, а скрапер записує те, що встиг зібрати частково, так, наче це повний знімок. Джерело, яке зазвичай тримає 9 000 активних оголошень, перезаписується 1 200, які вдалося зібрати до того, як скінчився час, а решта 7 800 зникають — не тому, що машини продалися, а тому, що наш власний код вирішив, що неповне завантаження — це повне.
Проксі нам не брехали. Проблема була в логіці повторних спроб, і вона тихо була поганою давно — просто маскувалася здоровішим пулом, який раніше встигав завершити завантаження до того, як це мало значення.
Ось у що насправді обходилася ця хвиля повторних спроб, коли ми зробили арифметику:
| Стара хвиля повторних спроб | Виправлена хвиля | |
|---|---|---|
| Повторних спроб на проксі | 3 | 1 |
| Затримка між спробами | 1с | — |
| Ефективна межа для пулу з 50 проксі | ~56 хвилин на URL | бюджет 120 секунд загалом |
| Поведінка при таймауті | записує частковий пакет як повний знімок | джерело пропускається, зберігається попередній знімок |
П'ятдесят шість хвилин, щоб здатися на одній URL-адресі. Помножте це на сканування, яке охоплює понад тисячу джерел, і отримаєте саме той тип тихого, нерівномірно розподіленого збою, який ми побачили — якісь джерела в порядку, 102 спотрошені, і в логах нічого не кричить "збій проксі", бо, технічно, його й не було.
25 вересня: виправлення і те, що насправді важливо для вас
Патч, випущений дев'ять днів пізніше, зробив дві непоказні речі. Він обмежив арифметику повторних спроб так, що застрягле завантаження швидко провалюється, а не поглинає майже годину, і — ось це вже справді запобігає втраті даних — часткове завантаження більше не замінює повний набір рядків. Якщо джерело встигає обробити лише 1 200 із звичних 9 000 оголошень, ми зберігаємо попередній повний знімок, а не перезаписуємо його часткою. У той же день з'явилася друга зміна, яка виставляє видимий прапорець CRAWL_PAUSED, який наш моніторинг перевіряє перед тим, як комусь дзвонити про падіння показників — тож свідома пауза читається як пауза, а не як тривога.
Чому читачу, який шукає вживаний Hyundai, має бути до цього якась справа? Тому що позначки "нижче ринку", які ми публікуємо — 12 823 з них живі на сайті станом на цей тиждень — чесні лише тоді, коли когорта, з якою порівнюється оголошення, є справжнім, повним регіональним ринком, а не обтятим через таймаут фрагментом. Значок угоди, побудований на знімку, що втратив три чверті своєї порівняльної вибірки, — це не значок угоди, це шум у зеленій етикетці. Ми радше поставимо сканування на дев'ятиденну паузу, ніж опублікуємо таке.
Сьогодні набір даних складає 1 108 535 оголошень по 73 регіонах і 1 375 джерелах — більше, ніж було до початку всієї цієї історії, проксі оновлено, а баг із заміною рядків усунено. Але число, до якого я постійно повертаюся, — не загальна сума. Це 84 401 — розрив між сканування, яке виглядало здоровим на рівні проксі, і тим, яке насправді було здоровим.



