Все цифры сняты с работающего стенда: потребление токенов — за календарную неделю по данным шлюза, метрики движка — кумулятивно за период аптайма (11 426 обработанных запросов). Продолжение — про выбор модели по замерам, а не по бенчмаркам — будет отдельной статьёй.
Для кого эта статья
Для тех, кто держит или собирается держать LLM внутри контура: тимлидов, DevOps, архитекторов. Здесь конфиги, цифры и грабли, а не введение в трансформеры.
Что вы унесёте: историю пяти последовательных конфигураций с тем, что каждая дала и чего стоила; рабочий набор флагов vLLM под одну карту Blackwell; три неочевидных бага и обходы; разбор реального счёта с отношением вход/выход 452:1.
Главный вывод, если дальше читать некогда: на агентской нагрузке кэш префикса решает больше, чем выбор модели, размер карты и всё остальное вместе взятое. Мы шли к этому через четыре промежуточных стенда и потратили лишние месяцы, потому что не понимали, что именно меряем.
1. Стенд
Параметр | Значение |
|---|---|
GPU | RTX PRO 6000 Blackwell 96 GB (sm120) |
Владение железом | аренда, 131 000 ₽/мес |
Модель | Qwen3-Coder‑Next‑FP8, 80B MoE / 3B активных, 262K контекста |
Движок | vLLM 0.27.1 в Docker |
Шлюз | LiteLLM 1.95.0 + Postgres |
Фронт | nginx + TLS |
Пользователи | 25 подключено, 16 активных (ежедневно, постоянный поток запросов) |
TTFT (среднее) | 1.4 с |
Профиль нагрузки | агентский кодинг: 1С/BSL, ReactJS, Java, PHP. Claude Code и Claude Desktop в режиме third‑party |
Ограничение | закрытый контур, код заказчиков не покидает периметр |
Последняя строка — не формальность, а причина существования стенда.
2. Как мы сюда пришли: пять конфигураций
Это не хронология ради хронологии. Каждый переход что‑то ломал, и понятно это становилось только на следующем шаге.
Конфигурация 1. Qwen3 30B, dense, без MoE. Кэширование работало из коробки, стенд вёл себя предсказуемо. Проблема была одна и непреодолимая: на реальных задачах модель не тянула — не замыкала агентский цикл, требовала ручных подталкиваний. Быстро и бесполезно.
Конфигурация 2. Qwen3-Coder‑Next 30B, MoE. Качество выросло, стенд остался живым. Но модель для 24-гигабайтных карт на 96 GB занимала четверть железа — мы гоняли малолитражку в грузовике.
Конфигурация 3. Qwen3-Coder‑Next 80B, MoE. Та же архитектура и tokenizer, вся обвязка перенеслась один в один — переход занял вечер. Качество стало приемлемым. Но кэша для гибридной архитектуры в нашей сборке vLLM не было, а мы этого не знали: работали на общем ключе, без разбивки, и видели только «иногда медленно».
Конфигурация 4. Подключили LiteLLM. Здесь начинается интересное. Шлюз дал разбивку по типам токенов — и стало видно, что запросы читают на порядки больше, чем пишут. До этого у нас была одна цифра «токенов потрачено», из которой не следовало ничего. Диагностика появилась раньше, чем лечение, и это нормальный порядок вещей.
Конфигурация 5. vLLM с поддержкой кэша под нашу архитектуру. --enable-prefix-caching для гибридных моделей не включается автоматически — флаг нужно указывать явно, и до 0.27.1 он для нашей связки не работал вовсе.
Метрика | До | После |
|---|---|---|
Prefix cache hit rate | 0% | 96.9% |
Латентность на повторном префиксе | 31.9 с | 0.24 с |
Цифра подтверждается с двух независимых сторон, и это стоит проверять всем: биллинг шлюза считает 96.9% чтения из кэша, а собственные счётчики движка за период аптайма дают 97.3% (1 345 481 136 попаданий на 1 383 053 896 запрошенных токенов). Сходятся — значит меряем одно и то же.
Это не оптимизация на проценты. Это смена режима работы стенда: высвободились и время, и VRAM, и терпение команды.
Что из этой истории следует для читателя: если у вас гибридная или MoE архитектура и вы не видите строку cache read в статистике — вы, скорее всего, платите тридцатикратную латентность и не знаете об этом. Проверять надо не «есть ли кэш в vLLM» (он есть), а «работает ли он для вашей конкретной архитектуры в вашей версии».
3. Экономика: разбор реального трафика
Потребление за календарную неделю:
Метрика | За неделю | В пересчёте на месяц |
|---|---|---|
Input tokens | 993 534 718 | ~4.26 млрд |
Cache read tokens | 962 273 296 | ~4.12 млрд |
Некэшированный вход | 31 261 422 | ~135 млн |
Output tokens | 2 197 078 | ~9.5 млн |
Запросов | 8 030 | ~34 800 |
Доля попаданий в кэш | 96.9% | |
Отношение вход/выход | 452:1 |
В пересчёте на один запрос: ~123 700 токенов входного контекста, из них ~119 800 прочитано из кэша и лишь ~3 900 обработано реально; сгенерировано — 274 токена. Средний шаг агента тащит сто двадцать тысяч токенов и выдаёт двести семьдесят четыре. Нагрузка на 16 активных пользователей — около 500 запросов на человека в неделю, примерно сотня за рабочий день.
Отсюда два практических следствия. Первое: на агентской нагрузке оптимизация чтения контекста важнее скорости генерации — бенчмарки, меряющие tok/s на выходе, описывают 0.2% реального трафика. Второе: сравнивать с облаком надо по фактическому биллингу с учётом кэша, а не по сырому объёму входа — кто считает по сырому, рисует себе выгодную картинку.
С чем вообще корректно сравнивать
Вопрос методологический, и от ответа результат зависит сильнее, чем от любой технической детали. Баз сравнения четыре, и они дают разные ответы.
API, нижняя граница — модель сопоставимого качества. Наша по публичным метрикам агентского кодинга в нижнем‑среднем тире, честное сравнение по качеству даёт самый дешёвый тариф.
API, верхняя граница — модель, которую команда реально использовала бы, не будь стенда. Это средний тир. Сравнивать со старшим мы считаем некорректным: Жигули против Мерседеса.
Подписки — младший и старший тариф. Про них отдельно ниже, потому что там решают не деньги.
Расчёт по опубликованным тарифам, проверено 22.08.2026. Курс берём не биржевой, а платёжный — 88.36 ₽ плюс комиссия платёжных систем, итого 96 ₽ за доллар; разница между двумя курсами и есть первая строка счёта, о которой обычно забывают.
Строка | Объём/мес | Нижняя граница | Верхняя граница |
|---|---|---|---|
Некэшированный вход | ~135 млн | $135 | $271 |
Чтение кэша | ~4.17 млрд | $417 | $833 |
Выход | ~9.5 млн | $48 | $95 |
Итого в месяц | ~$600 (57 600 ₽) | ~$1 200 (115 200 ₽) | |
Тот же объём без кэша | 4.30 млрд входа | ~$4 350 | ~$8 700 |
Последняя строка поучительнее остальных: без кэширования счёт вырастает примерно в семь раз — и это верно для облака тоже, кэш там стоит 10% от обычного входного тарифа.
Три оговорки, без которых таблица врёт. Наш кэш и облачный — разные механизмы: у нас prefix caching включается флагом и работает на всём подряд, в облаке кэш объявляется явными точками разрыва, имеет TTL и отдельную цену записи, так что 96.9% попаданий туда не переносятся. Тарифы падают — расчёт, сделанный при закупке, к моменту окупаемости может устареть не в вашу пользу. Из РФ прямой биллинг недоступен, наценку реселлера надо закладывать явной строкой; она сдвигает верхнюю границу в паритет с нашей арендой.
Подписка: пятичасовое окно решает больше, чем цена
Тот, кто подписывает счёт, считает не в токенах, а в рублях на человека. Подписочные тарифы с налогом — $24 и $121 в месяц, по курсу 96 это примерно 2 300 ₽ и 11 600 ₽ за место. Наша аренда — 131 000 ₽ независимо от числа людей.
Вариант | На 16 активных | На 25 подключённых | Держит нашу нагрузку |
|---|---|---|---|
Младшая подписка | ~36 900 ₽ | ~57 600 ₽ | нет |
Старшая подписка | ~185 900 ₽ | ~290 400 ₽ | да |
Наша аренда | 131 000 ₽ | 131 000 ₽ | да |
Почему первая строка нерабочая. Подписка продаёт не токены, а доступ с лимитами, и лимитов два: недельный и — что менее известно — скользящее пятичасовое окно. Мы пробовали оба тарифа по месяцу, один senior‑разработчик на своих задачах. На младшем лимит пятичасового окна вырабатывался за два — два с половиной часа, дальше человек ждал обновления: примерно четыре продуктивных часа из восьми. На старшем не упирались ни разу.
Экономия младшего тарифа против нашей аренды — около 5 900 ₽ на человека в месяц, то есть 280 ₽ за рабочий день. Платой за эти 280 ₽ становятся два‑три часа простоя разработчика ежедневно. Это не экономия.
Границы замера назовём сами: тестировал один senior, работающий с агентом плотно, — верхняя граница потребления, а не средняя, и разработчик с лёгким профилем в младший тариф, вероятно, уложится. Зато переносимость на команду здесь выше, чем кажется: подписки не делят общий ресурс, лимит у каждого свой, поэтому число пользователей наблюдение не искажает.
Две колонки в таблице — не для красоты. Подписка покупается на человека: платить придётся за всех, кому доступ выдан, а не за тех, кто им сегодня пользуется. Возразить легко — «не выдавайте лишних лицензий», — но заранее неизвестно, кто окажется активным: состав шестнадцати плавает, и подписочная модель требует ежемесячно перетасовывать лицензии вручную, всё равно оставляя риск, что доступа не окажется в нужный момент.
Отсюда самая короткая формулировка всей экономики: каждый следующий пользователь на своём стенде стоит ноль до упора в KV‑пул, на подписке — полную цену места.
Честное возражение против нас самих: можно было раздать старший тариф тяжёлым разработчикам, а младший остальным, и выйти дешевле аренды. Не выбрали по двум причинам, и обе не про деньги — код всё равно уезжает наружу, а администрировать смесь тарифов на 25 человек без централизованного учёта, аудита и отзыва доступа это отдельная работа.
Аренда вместо покупки, и во что она обходится
Сервер мы арендуем. Покупка — это кап. затраты, амортизация и вопрос «за сколько отобьётся»; аренда кап. затрат не создаёт, и понятие срока окупаемости исчезает, остаётся вопрос «что дешевле в этом месяце».
Почему аренда: железо устаревает быстрее, чем амортизируется — требования к VRAM растут быстрее любого разумного срока списания; не нужно замораживать крупную сумму; не нужны стойка, питание, охлаждение и руки, которые это обслуживают. Чего она стоит: платёж не заканчивается никогда — при нашей ставке аренда догоняет стоимость покупки сопоставимой конфигурации примерно за два года; плюс зависимость от арендодателя по SLA и доступу.
Себестоимость. 131 000 ₽/мес. на ~4.31 млрд токенов дают ~30 ₽ за 1M токенов, ~3.8 ₽ за запрос, ~8 200 ₽ в месяц на активного пользователя.
Метрика | Наша аренда | Младший тир | Средний тир | Старший тир |
|---|---|---|---|---|
₽ за 1M токенов | 30.4 | 13.4 | 26.7 | 66.8 |
₽ за запрос | 3.8 | 1.7 | 3.3 | 8.3 |
Плюс человек, который всё это держит — строка, которую в подобных расчётах обычно опускают, хотя именно она отличает свой стенд от подписки:
Работа | Трудозатраты | Частота |
|---|---|---|
Первичное развёртывание и тесты | ~1 неделя | разово |
Переезд на другую модель с прогоном | 2–3 дня | по мере смены моделей |
Эксплуатация: инциденты, апгрейды, разбор жалоб | не измеряли | постоянно |
В деньги не пересчитываем: результат зависит от вашей ставки и от горизонта, на который размазывается разовое внедрение, — на годовом и на двухмесячном цифры отличаются в разы. Третью строку мы честно не мерили; судя по разделу 7, эксплуатация не бесплатна, и «не считали» корректнее нуля. Существенно то, что администрирование не переворачивает ни одну строку сравнения — меняет величины, не знаки.
Итог по деньгам
База сравнения | Итог для аренды |
|---|---|
API, младший тир | проигрыш в 2.3 раза |
API, средний тир | проигрыш ~14% (с наценкой реселлера — паритет) |
Подписка, младший тариф | несравнимо: нагрузку не держит |
Подписка, старший тариф | выигрыш от 30% до 2.2 раз |
По токенам мы проигрываем, по посадочным местам выигрываем. И поскольку аренда фиксирована, а любая альтернатива линейна по числу людей, правильный вопрос звучит не «выгодно ли своё железо», а «с какого числа пользователей оно становится выгодным»: против среднего API‑тира это ~18 активных пользователей, против старшей подписки — 12. У нас 16 активных при 25 подключённых, то есть подписку мы уже обошли, а до API‑тира не хватает двух человек из девяти уже подключённых, но неработающих.
Отдельная ирония: кэширование, которое спасло наш стенд, ровно так же удешевляет облако. Один механизм играет за обе стороны, и главное техническое достижение экономического преимущества не создаёт.
Поэтому вывод, которого мы не планировали: свой инференс берут не за экономию. Берут за то, что кодовая база заказчиков не уезжает к внешнему провайдеру моделей и не попадает в чужие обучающие данные, а доступность инференса не зависит от чужих решений.
Честная оговорка про периметр. Арендованный сервер стоит не у нас, а у хостера: данные не покидают контролируемый нами контур, но физически лежат на чужом железе. Подробности — договор, доступ, шифрование — тема отдельного текста. Здесь ограничимся формулировкой: аренда сужает выигрыш по контуру, но не отменяет его, и считать периметр полностью своим было бы неправдой.
4. Тюнинг: что дало прирост, а что нет
Три находки, которых нет в документации.
VLLM_USE_DEEP_GEMM=0. DeepGEMM падает на sm120 с FP8-чекпоинтами. Симптом — падение при старте, причина неочевидна.
--enable-prefix-caching указывать явно — см. раздел 2. Стократное ускорение на одном флаге.
Занижение --max-model-len не экономит память. Мы держали 131 072 вместо родных 262144, считая, что это освободит VRAM. Не освободило ничего, но вдвое урезало контекст. Плата за полный контекст — меньше параллельных сессий (1.13x против 2.27x), при нашем числе пользователей несущественно.
Оговорка, которую мы поняли позже: рычага --gpu-memory-utilization у нас уже нет — он выставлен в 0.95, поднимать некуда. Если понадобится параллельность, останется только квантование или укорачивание контекста, и это надо учитывать при планировании, а не обнаруживать в момент, когда упёрлись.
KV‑кэш в FP8 (--kv-cache-dtype fp8):
Конфигурация | Размер KV‑пула, токенов |
|---|---|
Исходная | 497 000 |
| 878 000 |
То же на vLLM 0.27.1 | 1 032 910 |
Фактическая конфигурация KV‑кэша, как её сообщает сам движок:
Параметр | Значение |
|---|---|
| 1 032 910 |
| 3.94 (при |
| 989 |
| 1072 |
| fp8 |
| 0.95 |
| align |
| sha256 |
Скорость генерации 186.5 tok/s.
Про block_size 1072 стоит сказать отдельно. Мы его не задавали — для гибридной архитектуры vLLM вычисляет размер блока сам, отталкиваясь от страницы Mamba‑состояния, и получает величину на два порядка больше привычных шестнадцати. Практическое следствие: гранулярность кэша грубая. На наших контекстах в сто с лишним тысяч токенов это неважно, но на коротких запросах кэш работал бы заметно хуже, чем ожидаешь по документации.
Где на самом деле потолок по числу пользователей. Не в скорости и не в вычислениях: при среднем контексте ~124 000 токенов на запрос и пуле в 1 032 910 токенов одновременно помещается примерно восемь‑девять запросов с полным контекстом. Кэш префикса эту цифру улучшает, потому что общие префиксы делят одни и те же блоки, но насколько — вопрос к нагрузочному тесту, а не к арифметике.
Про кванты под Blackwell — порядок предпочтения оказался обратным интуиции: NVFP4, затем FP8, затем BF16. Замера качества FP8 против BF16 на своих задачах мы не делали, так что это выбор по размеру и скорости, а не по качеству, и честнее назвать его так.
Полный набор флагов:
--max-model-len 262144 --kv-cache-dtype fp8 --enable-prefix-caching --tool-call-parser qwen3_xml --served-model-name <алиасы> --gpu-memory-utilization <значение>
Латентность в эксплуатации. Здесь важнее не средние, а распределение — и оно объясняет, почему стенд «ощущается быстрым», хотя средний TTFT равен 1.4 секунды.
Перцентиль | TTFT | Полное время запроса | Время на токен генерации |
|---|---|---|---|
p50 | 0.43 с | 1.3 с | < 10 мс (> 100 tok/s) |
p90 | 0.90 с | 5.0 с | < 10 мс |
p95 | 1.8 с | 9.1 с | < 10 мс |
p99 | 5.9 с | 23 с | 21 мс |
максимум | 20–40 с | 120–240 с | — |
Посчитано по кумулятивным гистограммам движка за весь период аптайма, 11 426 завершённых запросов.
Медиана TTFT втрое ниже среднего — 0.43 против 1.4 секунды. Средняя величина здесь вводит в заблуждение: её тянет вверх хвост из девятнадцати запросов, где первый токен ждали от двадцати до сорока секунд. Это, скорее всего, холодные старты — первые обращения в новой сессии, где префикс ещё не лежит в кэше и контекст в сто с лишним тысяч токенов приходится обрабатывать целиком. Ровно тот сценарий, который до включения кэша был не исключением, а нормой (31.9 с, раздел 2).
Практический вывод для тех, кто будет мерить у себя: публикуйте медиану и перцентили, а не среднее. На агентской нагрузке с кэшем распределение двугорбое — попадание в кэш и промах отличаются на два порядка, и любая средняя величина описывает несуществующего пользователя.
Отдельная мелочь, которую видно только в счётчиках: первых токенов отдано 11 444, а завершённых запросов 11 426. Восемнадцать запросов клиент оборвал после начала генерации — обычное поведение агента, отменяющего ветку.
Есть ли запас по нагрузке. 8 030 запросов за неделю — это в среднем один запрос в 18 секунд по всему стенду; при полном запросе около трёх секунд получается порядка 16% занятости в сорока рабочих часах. Но арифметику можно не защищать: движок ведёт кумулятивную статистику, и она отвечает строго, без сэмплирования и графиков.
Очередь. На 11 426 запросов суммарное время ожидания — 161.6 секунды на все вместе, в среднем 14 миллисекунд; 99.7% запросов не ждали дольше 0.3 с. Хвост назовём честно: два запроса простояли 20–30 секунд, ещё восемь дольше пяти, свыше тридцати — никто.
Батчинг. Число, закрывающее вопрос окончательно: движок сделал 3 023 220 шагов, и 96.5% из них обработали ровно один токен. То есть карта подавляющую часть времени генерирует одиночный поток, батча просто нет. Стенд не «загружен на 16%» — он почти всегда обслуживает один запрос при аппаратной способности обслуживать несколько.
Сходимость. Суммарно движок обработал 39.9 млн токенов при 1.38 млрд запрошенных — ровно остаток после кэша: ~37.5 млн некэшированного входа плюс сгенерированное. Биллинг шлюза, счётчики кэша и счётчики итераций сходятся между собой, и это лучшее подтверждение, что мы меряем то, что думаем.
Потолок стенда, таким образом, не в вычислениях, а в KV‑пуле: около восьми‑девяти одновременных запросов с полным контекстом. И второй эффект, менее очевидный: кэш префикса — общий ресурс, с ростом числа пользователей растёт вытеснение чужих префиксов, так что 96.9% попаданий при пятидесяти активных никто не гарантирует. Это проверяется нагрузочным тестом, а не экстраполяцией.
5. Архитектура
Claude Code CLI ─┐ ├─→ nginx (TLS) ─→ LiteLLM ─→ vLLM ─→ Qwen3-Coder-Next-FP8 Claude Desktop ──┘ │ Postgres (ключи, учёт токенов)
Периметр заканчивается на nginx: наружу торчит только он, vLLM и Postgres видны исключительно изнутри. Клиенты — Claude Code в терминале и Claude Desktop в режиме third‑party — ходят по одному адресу и не знают, что за ним стоит.
6. Зачем LiteLLM, если vLLM уже OpenAI‑совместим
Вопрос задают первым, отвечаем сразу: vLLM не умеет отвечать на вопрос «кто сколько потратил и на что».
В нашей истории (раздел 2) шлюз сыграл роль не удобства, а диагностического прибора: пока стенд работал на одном общем ключе, у нас была единственная цифра «токенов потрачено», из которой не следовало ничего. Разбивка по типам токенов показала перекос 452:1 — и только после этого стало понятно, что чинить.
Что ещё даёт:
виртуальные ключи по пользователям и учёт в Postgres — на 25 пользователях без этого не понять, кто работает, а кто подключился и забыл
сравнительный учёт стоимости: ставки внешних провайдеров прописываются в
model_infofallback между инстансами, маршрутизация, централизованный отзыв доступа
Плата — ещё один процесс, который умеет падать. См. раздел 7.
7. Что сломалось
LiteLLM зависает примерно раз в сутки. Причина — блокирующий реконнект Prisma к базе, вешающий event loop; совпадает с известным постмортемом. Лечится вотчдогом: cron дёргает /health/readiness каждые 3 минуты и перезапускает контейнер при отсутствии ответа. Некрасиво, работает.
Лавина 405 от подсчёта токенов. Клиент регулярно дёргает /v1/messages/count_tokens, которого в цепочке нет. Флаг disable_token_counter не помог. Помогла заглушка в nginx, отдающая {"input_tokens":0}.
Model discovery не заработает никогда. vLLM отдаёт список моделей в формате OpenAI, без полей семейства и тира — сопоставлять нечего. Обход только ручным списком. И объявлять надо три модели разных тиров: агентские сценарии внутри себя обращаются к разным тирам, при одном объявленном чат работает, а агент падает на первом фоновом вызове. Алиасы — через --served-model-name.
Стриминг рвётся на TLS. ssl_buffer_size 4k в блоке server, gzip off в location. Диагностика — SSH‑туннель на loopback: если через туннель стрим идёт, а через домен нет, проблема в nginx, а не в модели.
8. Что сделали бы иначе
Поставили бы шлюз с разбивкой по типам токенов первым делом. Это главный урок всей истории: пока была одна цифра «токенов потрачено», мы чинили вслепую и не знали, что кэш не работает. Диагностика стоит дешевле любой оптимизации и должна идти раньше неё.
Брали бы модель под размер карты сразу. Две первые конфигурации ушли на то, чтобы понять очевидное: модель для 24-гигабайтных карт на 96 GB занимает четверть железа.
Фиксировали бы образ по digest, а не по :latest. На стенде, где версия движка решает, работает кэш или нет, плавающий тег — источник необъяснимых изменений поведения.
Снимали бы baseline перед каждой сменой модели. Пять переездов по два‑три дня каждый — это около двух недель инженерного времени, потраченных на поиск вслепую. Зафиксированный протокол сравнения окупился бы уже на третьем.
9. Что дальше: возврат к dense
Следующий эксперимент — Qwen3.8–27B‑FP8, dense, без MoE (~33 GB весов). Заявленный SWE‑bench Pro 61.7 против 44.3 у текущей модели.
Гипотеза, которую проверяем: даёт ли рост качества достаточно, чтобы компенсировать переход с 3B активных параметров на 27B. Ожидаемая просадка — на prefill, а при отношении 452:1 именно prefill и есть наша основная нагрузка.
Круг замыкается: мы начинали с dense‑модели (конфигурация 1) и ушли от неё из‑за качества. Возвращаемся к dense, но на другом уровне качества и с уже работающим кэшем.
Главное, чему нас научили пять переездов: baseline снимается до переключения, а не после. Половины цифр «до» у нас просто нет — именно поэтому история из раздела 2 местами описана прилагательными, а не измерениями. В этот раз сравнение будет по одинаковому протоколу с обеих сторон, и оно станет темой второй статьи.
10. Итог
Три вещи, ради которых стоило писать этот текст.
Кэш префикса важнее всего остального. При отношении вход/выход 452:1 агентская нагрузка почти целиком состоит из перечитывания контекста. Один флаг превратил тридцать секунд ожидания в четверть секунды и высвободил столько ресурса, что вопрос производительности с повестки снялся. Проверьте, работает ли кэш для вашей архитектуры в вашей версии, — «он есть в vLLM» не значит «он работает у вас».
Своё железо не экономит денег — экономит их профиль нагрузки. По цене за токен мы проигрываем облачным API. По цене за посадочное место выигрываем у подписки, и выигрыш растёт с каждым новым пользователем, потому что аренда фиксирована, а подписка линейна. Считайте не «дешевле ли своё», а «с какого числа людей оно становится дешевле» — у нас это двенадцать.
Меряйте раньше, чем оптимизируете. Мы прошли пять конфигураций, прежде чем поняли, что смотрели не на ту метрику. Разбивка по типам токенов стоила одного вечера и объяснила всё, что до этого объяснялось словом «иногда медленно».
