Поэтому я написал одну, которая объединяет всё.

Когда простой API-клиент превращается в зоопарк

Любой проект начинается с чего-то такого:

import httpx

async def fetch_user(user_id: str):
    async with httpx.AsyncClient() as client:
        r = await client.get(f"https://api.example.com/users/{user_id}")
        return r.json()

А потом реальность:

  • API лимитирует запросы

  • иногда отвечает 503

  • иногда виснет

  • иногда падает полностью

  • и retry может убить уже ваш сервис

И начинается подключение библиотек:

  • rate limit

  • retry

  • circuit breaker

И функция превращается в это:

@breaker
@retry(...)
@sleep_and_retry
@limits(...)
async def fetch_user(...):
    ...

Формально всё работает.

Но появляется ощущение, что ты не пишешь бизнес-логику.

Ты пишешь инфраструктурный клей.


Главная проблема — не в библиотеках

Каждая библиотека сама по себе нормальная.

Проблема в композиции:

  • разные модели времени

  • разные абстракции ошибок

  • разные API

  • сложное тестирование

  • непредсказуемое поведение при нагрузке

Retry + limiter + breaker — это уже система.

А мы собираем систему из несвязанных кусочков.


Идея: сделать устойчивость конвейером

Вместо “башни декораторов” я хотел одно:

единый pipeline выполнения вызова

Чтобы каждый запрос проходил через понятную схему:

Circuit breaker
→ Rate limiter
→ Retry loop
→ Запись результата

Без магии.
Без хаоса.
Без glue-кода.

Получилось вот так:

from limitpal import (
    AsyncResilientExecutor,
    AsyncTokenBucket,
    RetryPolicy,
    CircuitBreaker
)

executor = AsyncResilientExecutor(
    limiter=AsyncTokenBucket(capacity=10, refill_rate=100/60),
    retry_policy=RetryPolicy(max_attempts=3),
    circuit_breaker=CircuitBreaker(failure_threshold=5)
)

result = await executor.run("user:123", api_call)

Одна точка входа.
Одна модель поведения.
Одна архитектура.


Почему это важно

Retry без limiter — опасен.
Limiter без breaker — слеп.
Breaker без retry — жёсткий.

Только вместе они образуют стратегию устойчивости.

Именно стратегия, а не набор фич.


Тестирование — скрытая боль

Самая неприятная часть таких систем — время.

Традиционно:

time.sleep(1)

Медленно.
Флейково.
Непредсказуемо.

Поэтому внутри используется управляемые часы:

clock.advance(1.0)

Тесты выполняются мгновенно, но поведение идентично реальному.

Это звучит как мелочь.

На практике — спасение для CI и нервов.


Что в итоге получилось

Я собрал всё в одну библиотеку — LimitPal.

Это не “ещё один rate limiter”.

Это попытка сделать устойчивость архитектурным примитивом:

  • rate limiting

  • retry с backoff и jitter

  • circuit breaker

  • композиция стратегий

  • sync + async API

  • детерминированные тесты

Без внешних зависимостей.


Когда это имеет смысл

Если вы:

  • пишете API-клиенты

  • ходите во внешние сервисы

  • делаете фоновые джобы

  • боитесь retry-штормов

  • хотите предсказуемое поведение под нагрузкой

тогда архитектурный подход окупается.

Если нужен только retry — можно взять маленькую либу.

Но как только появляется композиция — начинается системная инженерия.


Документация:
https://limitpal.readthedocs.io/

Репозиторий:
https://github.com/Guli-vali/limitpal

Если идея зашл — буду рад обратной связи.

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


  1. Flux82
    06.02.2026 07:17

    А я почему-то чувствую клоунами тех, кто клепает на llm без оглядки новые либы (помните xkcd про новый стандарт, кстати?) и нейростатьи к ним тысячами. И нет, опечатка в конце не делает тест естественным.


    1. maslievilya1 Автор
      06.02.2026 07:17

      Я понимаю ваше негодование от массы безликих ЛЛМ продуктов и статей. Но обьективности ради, не все проекты где используются ЛЛМ плохие по дефолту. Было здорово все таки жить в парадигме презумпции невиновности.

      Моя библиотека результат конкретной боли, и представляет собой конкретное решение моей боли, ничего больше:) Буду рад обьективной критике: если где то есть проблемы в самой библиотеке, или если она сможет помочь вам в похожей ситуации, хорошего вам дня и спасибо за комментарий!


      1. Flux82
        06.02.2026 07:17

        Не отношу себя к неолуддитам. Получив от вас ответ, хочется прокомментировать. Я не написал бы достаточно резкий комментарий, если бы сама библиотека был описана вами. Однако здесь я вижу не только код, написанный вами с помощью LLM за одну-две недели, но и насквозь нейросетевой пост с громкими словами. Если бы не было громких слов, если бы чётко была вынесена роль LLM в написании либы, если бы примеры были привязаны к большему количеству реальных задач, реакция была бы другой.


        1. maslievilya1 Автор
          06.02.2026 07:17

          Огромное спасибо за конструктивную критику и взгляд со стороны, которого лично мне не хватает как автору!

          В продолжение дискуссии соглашусь с вами, что статья может звучать громко и пафосно - она была нарочно написана в таком маркетингово-рекламном стиле, в противовес моей предыдущей технически «нагруженной» статье.

          Если вам по душе более хабровый стиль и вас правда заинтересовала техническая часть без маркетинга и пафоса, то приглашаю почитать более развернутый вариант статьи на 12 минут - с логикой архитектурных решений, примерами кода и общей логикой проблем и решений:
          https://habr.com/ru/articles/992902/

          Также приглашаю вас почитать более раннюю статью, где я рассказывал про свой опыт, который сформировал видение проблемы и вдохновил меня на создание библиотеки:
          https://habr.com/ru/articles/983066/

          Чтобы закрыть вопрос о роли ИИ в написании библиотеки:

          • Документация (докстринги, документирование аргументов классов/методов, генерация техдокументации)

          Роль ИИ: помог дотянуть репозиторий до стандартов документирования open source-проектов, что особенно непросто для соло-девелоперов.
          Моя роль: контроль логики и достоверности документации.

          • Тесты

          Роль ИИ: генерация тестов по заданному мной плану покрытия функционала тестами.
          Моя роль: контроль корнер-кейсов (переполнение памяти), соблюдение таймингов лимитеров, решение использовать pluggable-модель времени для тестов и т.д.

          • Контроль thread-safety и работа с памятью

          Роль ИИ: учитывая, что моя библиотека (на данный момент) in-memory и исповедует бакет-ориентированную архитектуру лимитирования, в теории могли возникать проблемы с памятью из-за переполнения ключами, а также гонки состояний, особенно в синхронном варианте моих алгоритмов лимитирования. ИИ помог выявить потенциально проблемные места, которые человеческий глаз может упустить.
          Моя роль: иметь представление о возможных корнер-кейсах и картину надежности с точки зрения того, что и где стоит предусмотреть.

          Искренне благодарен за интерес к проекту!


    1. SiberianMouse
      06.02.2026 07:17

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


  1. mgis
    06.02.2026 07:17

    Простите, почему нельзя просто?

    npx @openapitools/openapi-generator-cli generate \
      -i http://localhost:8000/openapi.json \
      -g typescript-axios \
      -o ./src/api/generated

    p.s. ни разу не фронтендер.


    1. maslievilya1 Автор
      06.02.2026 07:17

      Спасибо за комментарий, не совсем понял что вы имели ввиду. Дело не в самом клиенте к API, а в упаравлении обработке "скользких" мест такие как рейт лимиты, ретраи и т д. То есть моя боль была скорее в том что на питоне нет библиотеки с единным пайплайном для контроля нагрузки и устойчивости.