14 сентября
Мы ждали этого дня несколько недель. Наш провайдер резидентных проксі ещё в июле исчез из-за сбоя оплаты, и два месяца растущая доля наших 1 375 источников краулилась в деградированном режиме или вообще не краулилась — запросы обрывались по таймауту, IP блокировались посреди сессии; та медленная утечка, которая не выглядит как авария, а просто незаметно уменьшает цифру, пока однажды не становится неловко. Мы продлили подписку. Новый пул, 20 000 роутируемых резидентных сессионных строк, каждый выход проверен пробой перед тем, как подпускать его к продакшену. 100% проб прошли. ОАЭ, Германия, Япония — всё живое, всё чистое.
Поэтому мы запустили supervised-краулинг — тот, где кто-то реально следит за дашбордом, а не доверяет крону. Я ожидал чего угодно, кроме катастрофы.
А получили базу, которая за один проход потеряла 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 — разрыв между краулингом, который выглядел здоровым на уровне проксі, и тем, который был здоровым на самом деле.



