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

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

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

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

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

А вот на что действительно стоит обратить внимание: среди похожих карт появились сайты, которые распространяют вирусы, просят скачать приложение или ввести данные банковской карты. Думаю что тут будет лишним говорить, что это вирусы, скам, фейки и прочая лабуда.

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

Не буду мериться миллионами, покажу что вижу в своей админке на момент публикации

  • сейчас на сайте: 1 128 человек

  • уникальных за сегодня: 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.

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

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

Идентификатор анонимный, client_id из localStorage, если его нет то хэш IP.
Натыкать десять отметок подряд смысла нет, от одного человека считается только последняя. И один человек свежей отметкой большинство не перебьет, если ситуация на заправке реально поменялась, отметки других водителей быстро перевесят и статус сам скорректируется.

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

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

И пара решений под капотом, которые оказались неочевидными...

Инвалидация кэша одним INCR, а не удалением ключей.
Кэш выдачи по видимой области лежит в Redis, но ключ построен хитро в него зашита версия данных:

grid      = round(south,2),round(west,2),round(north,2),round(east,2)
cache_key = f"bbox:{redis.get('stations:ver')}:{grid}"

Когда прилетает новая отметка, не нужно чистить сотни bbox‑ключей в Redis, только одна операция INCR stations:ver. Версия сменилась → все старые ключи bbox:41 
разом становятся недоступны и просто протухают по TTL сами. Новые запросы идут уже на bbox:42. Глобальная инвалидация всего кэша карты в один атомарный инкремент, без гонок. Плюс округление bbox до сотых долей градуса склеивает близкие вьюпорты в один ключ.

Статус заправки считается на чтении

Крон джоб, которые бы отключали заправки по таймеру, нет, при запросе обращаемся к
now() — last_report_at, если самый свежий отчёт старше окна таймаута (2 часа),
То просто отдается серый цвет заправки, без мутаций бд и блокировок при таком количестве.

Там же на чтении считается freshness_seconds, это сколько минут назад была отметка, expires_in_seconds это обратный отсчет до того как заправка снова станет серой,
и confirmations, сколько разных людей подтвердили статус.
Плюс разбивка по каждому топливу отдельно, 92й может быть, а 95го уже нет.

Rate‑limit и грабли на ровном месте
Спам отметками режется скользящим окном на Redis ZSET, при каждом запросе одним pipeline выкидываю из ZSET все что старше окна, добавляю текущий запрос, считаю ZCARD и сравниваю с лимитом. Четыре команды, атомарно, по IP.

pipe.zremrangebyscore(key, 0, now - window_sec)
pipe.zadd(key, {member: now})
pipe.zcard(key)
pipe.expire(key, window_sec)

И тут я успел наступить на грабли, сначала уникальным членом ZSET был id(now).
А id у флоата в питоне это адрес объекта, который переиспользуется. Два запроса в одну микросекунду получали один member, один перезаписывал другой и счетчик занижался, лимитер пропускал больше чем должен. Заменил на now + token_hex и стало честно.

Реалтайм тонкими событиями, WebSocket часть нарочно тупая.
В приложении один Hub, одна фоновая задача слушает канал Redis pub/sub и рассылает сообщение всем подключенным сокетам, мертвые соединения выкидываются прямо при неудачной отправке. Само событие это просто {«type»: «station_update», «station_id»: N}, без данных. Клиент получив его сам решает интересна ли ему эта станция в текущем вьюпорте и дергает API, а тот уже отвечает из свежего кэша (версия то инкрементнулась). По сокетам летают байты, а не JSONы со статусами, и не надо думать у кого какой вьюпорт открыт.

Как 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

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

Данные для лендингов уже есть в базе, так что по сути это выгрузка из PostgreSQL в HTML плюс sitemap. Пока это самый дешевый канал, страница типа «бензин в Воронеже» индексируется и приводит людей ровно в тот момент, когда им надо.

Про точность

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

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

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

Что дальше

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

Буду благодарен за любую обратную связь!


Комментарии (20)


  1. not_yours_ne
    03.07.2026 10:38

    Где бензин Народная карта наличия бензина на АЗС

    Буду благодарен за любую обратную связь!


    Туда ли вы воюете?

    Нельзя допустить чтобы это работало! Нужно заполнить ложными данными, чтобы по приезду оказывалось пусто, и, наоборот, там где есть пусть будет показано отсутствие топлива


  1. yesus1707
    03.07.2026 10:38

    интересно было бы видеть различную статистику по дням, как там картина в целом меняется


    1. d_tylnyi Автор
      03.07.2026 10:38

      Добрый день, да постараюсь делиться актуальными данными, но статистика получается довольна интересная, вернее получалась...
      Хранил ее в редисе, и вообщем потеряли мы статистику временно, теперь переливается в постгрес и собирается чуть по другой схеме хранения)


  1. oleg_km
    03.07.2026 10:38

    Похоже, люди наигрались в эту фигню. На большинстве заправок нет данных


    1. iv_kingmaker
      03.07.2026 10:38

      Потому, что сервис другой, в том сервисе до сих пор данные обновляются постоянно.


    1. KLaeda
      03.07.2026 10:38

      Увы, но из 100 человек если 5 отметят и то хорошо. У нас даже был создан чат для всего города, но информация разнилась, было недостоверной или просто к моменту приезда на АЗС была неактуальной. Единственный способ который работал, это подойти к управляющему АЗС и спросить когда. Так и заправился. Вообще нужен сервис где данные будут браться напрямую с АЗС. Или чтобы хотя б крупные сети в свои приложения добавили такую информацию


    1. d_tylnyi Автор
      03.07.2026 10:38

      Данных много на самом деле, в каких-то регионах больше, в каких то меньше...
      Но это скорее удобно когда у тебя вообще бак почти пустой, и стоит выбор куда поехать, а не как универсальный инструмент что там точно есть или нет бензина, но чем больше пользователей тем более актуальные данные и реальнее картина.


  1. oleg_km
    03.07.2026 10:38

    А, возможно. Но там тоже все как-то противоречиво. В одно время и есть бензин и нет бензина.


  1. iv_kingmaker
    03.07.2026 10:38

    Зачем обманываете.

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


    1. d_tylnyi Автор
      03.07.2026 10:38

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


  1. oleg_km
    03.07.2026 10:38

    Ну те результат возможен, если за дело возьмется допустим яндекс навигатор, договорится с азс и будет на корте отображать инфу. А народное получается так себе


    1. d_tylnyi Автор
      03.07.2026 10:38

      Боюсь что там такая картина с АЗС, что яндекс такое публиковать не будет...
      Он любитель публиковать лишь когда все хорошо...


      1. max9
        03.07.2026 10:38

        у тбанка, к слову сказать, тоже "все хорошо" на карте заправок.


    1. Efrem3112
      03.07.2026 10:38

      Я понадеялся на Яндекс Заправки и поехал. Но, нет. Даже оплатить можно в приложении, но бензина в колонке нет.


      1. boyarin_80
        03.07.2026 10:38

        …Вечером деньги- утром стулья!

        Ничего личного, только бизнес.


  1. morgot
    03.07.2026 10:38

    Спасибо за сайт. Рассмотрите возможность комментирования только с российских айпи. А то "кибервойска" одного соседнего государства начали сегодня массово спамить ложными отзывами. Пруфы давать тут не буду, чтобы не делать экстремистам рекламу.