Народная карта наличия бензина: честно про цифры, архитектуру и как PostGIS прод положил

Народная карта наличия бензина: честно про цифры, архитектуру и как PostGIS прод положил

Коллеги всем привет, меня зовут Дмитрий Тыльный, я автор проекта gde-benzin.ru Народная карта наличия бензина на АЗС, где водители в реальном времени отмечают, на каких заправках сейчас есть топливо, где очередь, где скоро будет, а где уже все...

Сразу скажу про спорный момент. На волне топливного дефицита появилось сразу несколько похожих карт, и в комментариях уже считают, "кто у кого скопипастил".

Мы с "гдебенз" запустились примерно в одно время, если я не путаю, зарегистрировал даже на день раньше домен, но к разработке приступил позже, по личным причинам).

Оба проекта это вайбкодинг с Claude, поэтому визуально и по логике они неизбежно похожи...

В основе у всех одно и то же — карта, на ней заправки, несколько статусов. Утверждать, что кто-то кого-то намеренно копирует, я не буду — это некрасиво и, скорее всего, неправда.
Идея простая, время одно, инструмент один.

Мои честные цифры

Народная карта наличия бензина: честно про цифры, архитектуру и как PostGIS прод положил
  • сейчас на сайте: 1 128 человек
  • пользователей за сегодня: 28 764
  • уникальные по дням подряд: 22 547 → 25 981 → 28 764
  • всего АЗС в базе: 27 558 (вся Россия)
  • АЗС со свежими данными прямо сейчас: 3 276
  • отметок за сегодня: 4 382, за трое суток: 12 847
  • добавили на экран: 18 642

Отметки за трое суток по статусам:
есть — 5 142
скоро будет — 3 287
очередь — 2 814
закончился — 1 604

Цифры скромнее, чем 1,8 млн за трое суток у соседей.
Но это ровно то, что я могу показать по факту...
Для нишевого сервиса, результат за три дня огонь)

Как все работает

Стек намеренно простой и дешёвый в эксплуатации:

  • Бэкенд — FastAPI (async), PostgreSQL + PostGIS для геозапросов, Redis (кэш выдачи по видимой области, rate-limit, pub/sub → WebSocket).
  • Фронт — статика + Яндекс.Карты (JS API), mobile-first, тёмная тема. Работает прямо в браузере: ни приложения, ни регистрации, ни разрешений.
  • Реалтайм — как только кто-то ставит отметку, она через WebSocket прилетает всем открытым картам; плюс карта раз в минуту сама подтягивает свежие данные.
  • Всё живёт в Docker Compose, статику отдаёт системный nginx, мониторинг — Prometheus + node-exporter + cAdvisor + Grafana.

Пара решений, которые оказались важнее, чем кажется:

Концепция последний прав, ломается от одного тролля, который натыкает нет бензина. Поэтому статус АЗС — это большинство среди отметок, где один человек (по анонимному идентификатору) = один голос, а при равенстве побеждает более свежая. Отметка живёт 2 часа, потом заправка становится серой — чтобы на карте не висела вчерашняя правда.

Донат-кластеры. При отдалении заправки схлопываются в кластер, и кластер закрашен кольцом по долям статусов: одна зелёная и одна красная рядом — кольцо ровно пополам.
Рисуется как SVG-иконка на лету, без сторонних библиотек кластеризации.

Только Россия, со всеми регионами. Точки берём из OpenStreetMap, но в выдаче нужна строго РФ.
Границу собираем из OSM (osm2geojson + shapely) и режем по ней — включая Крым, Севастополь, ДНР, ЛНР, Херсонскую и Запорожскую области.И вот на этой самой границе прод упал)

Как PostGIS ронял прод

Первую версию фильтра по границе я сделал «в лоб»: на каждый запрос выдачи по видимой области — ST_Contains(граница_РФ, точка_АЗС). При в теории. На практике граница России — это мультиполигон на ~147 тысяч вершин, и PostGIS честно проверял вхождение каждой из 27 тысяч станций в этот монстр-полигон на каждый скролл карты.На пике трафика load average сервера ушёл под 47, а postgres ел ~415% CPU.Посыпались таймауты и недогруз карты.

Чинил так:

  1. Убрал ST_Contains из горячего пути. Граница нужна ровно один раз — при импорте, чтобы выкинуть заграничные АЗС.
    В рантайме проверять её незачем: в базе и так только Россия.
  2. Добавил кэш выдачи по bbox в Redis с коротким TTL и мгновенной инвалидацией при новой отметке (через инкремент версии).

После этого postgres с 415% CPU вернулся к ~1%, и сайт перестал складываться под нагрузкой. Мораль банальная, но выстраданная руками: тяжёлую геометрию нельзя дёргать на каждый запрос — предрасчитывай и выноси из горячего пути.По классике мониторинг: Grafana + cAdvisor

SEO

Отдельно повозился с тем, чтобы карту находили не только по прямой ссылке: сгенерировал лендинги по регионам, городам, сетям АЗС и маркам топлива.

Про точность

Карта верна и релевантна ровно настолько, насколько активны водители.Там, где сегодня никто не отметился, заправка серая и ничего не придумывает.Достоверное есть бензин карта показывает там, где только что был живой человек, поэтому в карточке видно, сколько минут назад пришла отметка и сколько ей ещё жить.Это не замена звонку на АЗС, а способ не объехать три пустые заправки подряд, как это когда-то сделал я — собственно, поэтому проект и появился.

Безопасность и немного оффтопа

Раз под видом карт бензина набежали мошенники, я сразу зашил в сервис принципы: ничего не скачивать, никакой регистрации, не собираем персональные данные, не просим оплату или данные карт. Геолокация включается только по кнопке и остаётся в браузере.Плюс отдельный раздел с дисклеймером: сервис информационно-справочный, статусы — субъективные сообщения пользователей, наличие всегда уточняйте на самой АЗС.

Что дальше

Хороший вопрос, но буду развивать проект пока в нем есть необходимость и потребность.

Мои честные цифры

2