Как мы полгода отдавали неверные цены и почему не замечали этого
У сборщика протух ключ к API одного провайдера. Падения были каждую ночь, но выглядели как «у него ничего не изменилось» — и каталог тихо разошёлся с реальностью на 120 тысяч рублей в одной позиции. Разбираю аварию и предохранитель, которого не хватало.
Что случилось
Каталог собирается автоматически: раз в сутки скрипт обходит провайдеров, забирает их прайсы и открывает merge request с изменениями. У одного из них — Рег.облака — доступ шёл через API по ключу. В какой-то момент ключ перестал работать.
Сбор устроен так, что упавший провайдер сохраняет прежние данные. Это осознанное решение: одна сетевая ошибка не должна обнулять целую линейку в каталоге. Логика правильная, но у неё оказалось последствие, которого я не предусмотрел.
В ежедневном отчёте провайдер, чей сбор упал, и провайдер, у которого ничего не изменилось, выглядят одинаково: ноль изменений. Разница только в строке статуса, на которую перестаёшь смотреть, когда она повторяется каждый день. Тишина стала выглядеть нормой.
Насколько разошлись данные
Пока мы молчали, провайдер снял с продажи линейку HP и завёл вместо неё две новые — на SSD и на NVMe. В каталоге всё это время висели старые тарифы.
Обычные серверы оказались завышены примерно на 30% и с неверным типом диска. С GPU вышло наоборот: они были занижены, и сильно. Самая крупная ошибка — конфигурация с A100: у нас стояло 162 163 ₽ в месяц, реальная цена оказалась 282 064 ₽. Разница в 120 тысяч рублей на одной позиции.
Для агрегатора цен это худший из возможных дефектов. Не «неудобный интерфейс» и не «мало фильтров», а именно то, ради чего к нам приходят: число, на которое человек рассчитывает при выборе.
Почему API оказался хуже витрины
Разбираясь, я обнаружил вторую проблему, которая существовала независимо от протухшего ключа. Месячная цена в этом API была производной: часовая ставка умножалась на 730. С ценой на сайте она не совпадала — 423 ₽ против 390 ₽ на одном и том же тарифе.
То есть даже при живом ключе мы отдавали не то число, которое увидит покупатель на витрине провайдера. Формально данные «из официального API», фактически — не те, по которым платят.
Поэтому переход сделали не на новый ключ, а на публичную страницу с тарифами. Она не требует авторизации, не может протухнуть и показывает ровно то, что видит клиент. Старый адаптер остался в репозитории на случай, если API однажды починят и он станет отдавать витринные цены.
Предохранитель, которого не хватало
Исправить цены — половина работы. Вторая половина в том, чтобы такая ситуация больше не могла тянуться незаметно.
Теперь сборщик хранит дату последнего успешного сбора по каждому провайдеру. Если данных нет три дня, прогон выдаёт предупреждение. Если семь — падает целиком, и это видно сразу, потому что ночной джоб становится красным.
Правило звучит просто: молчание должно шуметь. Система, которая при поломке ведёт себя ровно так же, как при штатной работе, рано или поздно вас обманет — не потому, что кто-то ошибся, а потому, что различить два состояния нечем.
Отдельно я решил не проставлять дату сверки задним числом, хотя соблазн был: каталог выглядел бы свежее. Непроверенная дата хуже её отсутствия — она превращает честное «мы не знаем» в уверенное враньё.
Что из этого следует читателю
Первое и главное: сверяйте цену на сайте провайдера перед оплатой. Любой агрегатор — включая наш — показывает снимок, снятый когда-то в прошлом. Мы указываем дату этого снимка на каждой странице именно поэтому.
Второе: спрашивайте, откуда у площадки данные. Ручной ввод протухает молча и незаметно даже для самой площадки. Автоматический сбор протухает тоже, но у него хотя бы можно поставить сигнализацию — что мы теперь и сделали.
Третье, менее очевидное: «данные из официального API» звучит солиднее, чем «данные с сайта», но означает это не всегда то, что кажется. Иногда в API лежит производная величина, а настоящую цену показывают на витрине.