umbot
    Preparing search index...

    Производительность: чего ожидать и когда волноваться

    umbot спроектирован с прицелом на высокую производительность и предсказуемое время отклика. Это особенно важно, учитывая жёсткие ограничения по времени от платформ (например, 3 секунды для Яндекс Диалогов).

    Этот документ описывает:

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

    Все данные получены в контролируемых условиях и воспроизводимы — вы можете запустить тесты самостоятельно.

    В типичных сценариях (до 1 000 команд) внутренняя обработка umbot занимает менее 30 мс для холодного запуска. Это включает:

    • парсинг входящего запроса,
    • определение нужной платформы
    • чтение/запись пользовательских данных,
    • поиск подходящей команды,
    • формирование ответа.

    В подавляющем большинстве сценариев umbot укладывается в десятки миллисекунд. Однако есть два случая, когда время обработки может возрасти, — и для каждого из них фреймворк предлагает готовое решение.

    1. Первичная загрузка медиафайлов
      Когда голосовой навык или чат-бот впервые отправляет изображение или аудио, фреймворк загружает файл на сервер платформы и сохраняет полученный токен в базе данных.
      Почему это может быть долго: размер файла, скорость соединения с API платформы, очередь на стороне платформы.
      Как избежать: используйте встроенный класс Preload. Он позволяет заранее, при старте приложения, загрузить все медиафайлы и закэшировать их токены. Тогда во время реального диалога отправка будет мгновенной — фреймворк просто подставит уже готовый токен.

    2. Холодный запуск с большим числом сложных регулярных выражений
      Если ваше приложение использует 10 000+ регулярных выражений, при первом запросе фреймворку нужно скомпилировать их все.
      Почему это может быть долго: компиляция большого количества RegExp — ресурсоёмкая операция в JavaScript.
      Как избежать:

      • Подключите библиотеку re2 — она ускоряет обработку регулярных выражений в 2–15 раз и снижает потребление памяти в 3–7 раз.
      • Используйте кэширование RegExp (фреймворк делает это автоматически).
      • «Прогрейте» кэш при старте, выполнив несколько тестовых запросов.
      • Включите строгий режим (strict_prod), чтобы отсечь потенциально опасные ReDoS-выражения ещё на этапе регистрации команд.

    Важно: все описанные ситуации решаются стандартными средствами фреймворка. В типовом проекте (до 1000 команд, умеренное использование регулярок) вы никогда не столкнётесь с задержками — они возникают только в экстремальных сценариях и имеют простые пути оптимизации.

    ⚠️ Важно: время выполнения пользовательской логики (ваш код в addCommand или action) не входит в эти цифры. Если ваш обработчик работает 2.5 сек — это ваша зона ответственности.


    umbot — это мультиплатформенный фреймворк, тогда как Telegraf, vk-io, alice-kit и т.д. — **специализированные SDK **.

    Прямое сравнение производительности некорректно по нескольким причинам:

    1. Разные архитектурные задачи — специализированные библиотеки фокусируются на глубокой интеграции с одной платформой (например, опросы в Telegram), тогда как umbot обеспечивает единый API для всех платформ
    2. Разные точки замера — бенчмарки umbot измеряют чистое время маршрутизации команд, в то время как другие библиотеки часто включают сетевое взаимодействие с API платформ
    3. Разные приоритетыumbot оптимизирован для сценариев, где важна поддержка нескольких платформ с одним кодом

    Ключевой вывод: в реальных проектах узким местом почти всегда становится либо API платформы (ограничения на RPS), либо ваша бизнес-логика (работа с БД, внешними сервисами) — но не маршрутизация команд.

    Даже при пиковой нагрузке ядро umbot (16 000+ RPS в бенчмарках) обрабатывает запросы быстрее, чем:

    • платформы передают их боту (ограничения API)
    • выполняется типичная логика приложения
    • происходят сетевые запросы к базам данных

    Даже в "реальных" условиях (с фоновой нагрузкой) umbot показывает 16 000+ RPS.

    Что это значит на практике:

    • Средний навык или бот получает ~1-1000 запросов в секунду
    • Даже при пиковой нагрузке (праздники, рекламные акции) у вас будет 15-кратный запас
    • "Узким местом" практически всегда будет API платформы, а не umbot

    Результаты тестирования: На сервере 2 ядра / 4 ГБ RAM (FirstVDS, тариф «Разгон», фоновая нагрузка: 3 сайта + 10 навыков + MariaDB):

    • 16 000 RPS — при 1000 командах и простой логике
    • 4500 параллельных запросов — выполняются без ошибок, и меньше чем за 1с.

    На чистом сервере 1 ядро / 1 ГБ RAM(без фоновой нагрузки, только бенчмарк):

    • 27 000–35 000 RPS — в идеальных условиях

    ⚠️ Важно: RPS сильно зависит от окружения. Цифры выше — ориентиры для сравнения сценариев, а не гарантии. На менее нагруженной системе достигается более высокий RPS

    📊 Контекст: Популярные платформы имеют различные ограничения на частоту запросов — от десятков до нескольких тысяч в секунду в зависимости от тарифа и типа запроса. Показатели umbot находятся на уровне или выше этих ограничений, что означает, что фреймворк не станет узким местом вашего приложения.

    Потребление памяти (инкрементальное, разница до/после инициализации):

    • Базовая инициализация: < 1 МБ
    • 1000 команд: < 1 МБ
    • 10 000 регулярок: +8 МБ

    💡 Абсолютное потребление процесса Node.js (~45–60 МБ) не учитывается — это стоимость среды выполнения, а не фреймворка.


    Длительное тестирование (48 часов) не выявило утечек памяти или снижения производительности: средняя пропускная способность в последовательном сценарии осталась на уровне 67 000 RPS, а потребление памяти стабильно.

    Ключевые метрики теста:

    • Обработано запросов: 11.6 млрд за 172 800 секунд
    • Пропускная способность: 67 582 RPS (среднее, без просадок)
    • Память (Heap): старт 16 МБ → среднее ~38 МБ → завершение 21 МБ
    • Память (RSS): старт 102 МБ → среднее ~98 МБ → завершение 86 МБ (Δ: -16 МБ)

    Что это значит:

    • Утечек нет. Потребление памяти не растёт линейно, а колеблется в коридоре. Отрицательная дельта RSS (-16 МБ) говорит о том, что сборщик мусора корректно освобождает ресурсы. Приложение не «раздуется» и не упадёт через неделю работы из-за нехватки памяти.
    • Производительность не деградирует. Результаты теста на 3 секунды (66 551 RPS), 15 секунд (66 597 RPS) и 48 часов (67 582 RPS) совпадают в рамках статистической погрешности (<0.2%). Нет эффекта «разогрева», нет просадок из-за фрагментации памяти или накопления состояния.
    • Предсказуемость. Фреймворк ведёт себя одинаково на коротких и длинных дистанциях. Это позволяет точно планировать ресурсы: если на тесте ядро выдало 67k RPS — в продакшене вы получите сопоставимую производительность ядра (с поправкой на сеть и БД).

    💡 Примечание: Тест проводился в изолированной среде (без сети и БД). В реальном проекте итоговое потребление памяти будет зависеть от вашей бизнес-логики, но ядро фреймворка не станет источником утечек или деградации. Исходный код бенчмарков открыт — вы можете запустить их самостоятельно и проверить результаты на своей инфраструктуре.

    Все тесты открыты и находятся в репозитории:

    # Установка
    git clone https://github.com/max36895/universal_bot.git
    npm install
    npm run build

    # Тест производительности по количеству команд
    npm run bench

    # Стресс-тест на параллельные запросы
    npm run stress

    Полные результаты (включая память, разные типы регулярок, сравнение «первый vs повторный запуск») — в файле BENCHMARKS.md.

    Также есть возможность запустить тестирование в легком и долгом режиме. В легком режиме количество обрабатываемых команд составляет 8 (В обычном режиме 10003). В долгом режиме запускается длительный тест на 48 часов.

    # Стресс-тест в легком режиме
    npm run stress:lite

    # Стресс-тест в долгом режиме
    npm run stress:long

    ✅ Подходит, если:

    • Вы разрабатываете единое приложение для нескольких платформ.
    • Ожидается высокая нагрузка.
    • Вам важна предсказуемость и простота сопровождения.
    • Вы делаете голосового ассистента, справочник, текстовую игру.

    umbot отлично справляется с диалоговыми сценариями, но если ваша задача — глубокая интеграция со специфическими функциями одной платформы (например, опросы в Telegram, которые требуют прямого вызова API), вам может потребоваться написать небольшую обёртку. Однако и в этом случае 90% логики (обработка команд, состояние, кнопки) останется единой для всех платформ.

    По умолчанию umbot использует линейный поиск с поддержкой подстрок и регулярных выражений. Также для команд, обрабатываемых как обычный текст, логика поиска была оптимизирована:

    1. Сначала проверяется точное совпадение
    2. Если точного совпадения нет — выполняется последовательный перебор. Это обеспечивает простоту, предсказуемость и соответствие поведению других платформ (где порядок регистрации важен).

    Однако при числе команд >1000 или в условиях высокой нагрузки вы можете подключить собственный алгоритм поиска:

    const bot = new Bot();
    bot.setCustomCommandResolver((userCommand, commands) => {
    // Пример: возврат команды по хэшу (ваши правила)
    for (const [name, cmd] of commands) {
    if (cmd.slots.some((slot) => userCommand.includes(slot as string))) {
    return name;
    }
    }
    return null;
    });

    💡 Рекомендации:

    Сохраняйте порядок перебора, если он критичен для вашей логики. Используйте кэширование (Map<string, string>) для часто встречающихся фраз. Для fuzzy-поиска рассмотрите fuse.js или natural. При использовании регулярок — не забывайте про защиту от ReDoS.

    Если в вашем приложении используется регулярные выражения для поиска команд, то стоит рассмотреть вариант перехода на re2, за счет чего время обработки может существенно сократиться, а также сократится потребление памяти. При внутреннем тесте при большом числе регулярных выражений (>10 000) использование re2 снизило время обработки в 5–7 раз и уменьшило потребление памяти.

    Долгая обработка команд может быть связана:

    1. Большим количеством команд. В таком случае стоит пересмотреть подход, и переписать сами команды, объединив их при необходимости.
    2. Неоптимальный код в обработчике команды. Сам код команды может быть не оптимальным, и занимать значительную часть времени обработки. Убедитесь что в вашем коде нет тяжелых операций, а если они необходимы, то можно попробовать разбить большую операцию на команды, либо выполнить длительную обработку асинхронно, все запроса пользователя.

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

    umbot — это производительный мультиплатформенный фреймворк, который:

    • Показывает производительность на уровне специализированных решений.
    • Обрабатывает десятки тысяч запросов в секунду на одном VPS
    • Позволяет поддерживать единую кодовую базу для нескольких платформ, сохраняя при этом приемлемую производительность для большинства сценариев использования. Он не решает все задачи — но для диалоговых, интерактивных сценариев он обеспечивает стабильную, предсказуемую и масштабируемую основу.

    Если ваша задача — диалог с пользователем, а не управление чатом, umbot даст вам единый, предсказуемый и масштабируемый фундамент.