Инженерия затрат: пять статей счёта и паттерны, умножающие их в 10 раз
Разложите счёт на ГБ-часы, трансформации изображений, egress, операции ISR и edge-инвокации — затем охотьтесь на умножители: ферма ресайза, middleware на каждом ассете, revalidate:1 на длинном хвосте, налог случайной динамики. Помаршрутная математика и ограждения.
CFO пересылает счёт с одним словом: «объясните». Счёт вырос с $1 900 до $6 400 за месяц; трафик — на 8%. Декомпозиция занимает двадцать минут и отвечает на всё: ГБ-часы функций ровные, egress ровный, оптимизация изображений — в 14 раз: 1,3 миллиона трансформаций против 90 тысяч месяцем раньше. Логи доступа показывают почему: партнёрский блог захотлинкал картинки товаров через /_next/image со своими параметрами w=3840&q=100, а в remotePatterns всё ещё жил wildcard, добавленный кем-то на прототипе. Оптимизатор стал бесплатной публичной фермой ресайза для чужих сайтов. Фикс — конфиг-дифф на шесть строк. Дорогое началось потом: впервые проведя аудит стоимости по маршрутам, команда нашла вызов cookies() в корневом layout, из-за которого весь маркетинговый сайт рендерился динамически — каждая страница, каждый запрос, с марта. Никто не заметил, потому что ничего не сломалось. Счёт был единственным монитором, который это видел.
Читаем счёт: пять статей
Когда CFO пересылает счёт, можете ли вы ответить «какой счётчик это вызвал» за двадцать минут? Если нет — этот раздел даёт вам фреймворк, чтобы делать это каждый раз. Serverless-счёт Next.js раскладывается на пять счётчиков, и каждый крутит своя часть архитектуры. ГБ-часы функций — время выполнения, умноженное на выделенную память, просуммированное по инвокациям, — крутит доля динамически рендерящегося трафика и вес каждого рендера. Оптимизация изображений биллится за трансформацию: каждая уникальная комбинация исходного URL, ширины и качества — один ресайз; повторные запросы — кеш-хиты почти даром, так что счётчик считает уникальные варианты, а не просмотры картинок. Bandwidth/egress — доллары за исходящий ГБ: клиентские бандлы, картинки, RSC-payload, ответы API. Чтения и записи ISR метрят операции о хранилище, стоящее за Full Route и Data Cache. Edge/middleware-инвокации биллятся за миллион запросов, прошедших через middleware или edge-маршруты.
Какая статья доминирует, зависит от формы приложения, и неверная догадка сжигает квартал оптимизаций. Контентный сайт с хорошим кешированием тратится на картинки и egress, его ГБ-часы округляются к нулю. Залогиненный SaaS-дашборд — инверсия: всё рендерится динамически на пользователя, доминируют ГБ-часы, картинки — шум. Большой ISR-каталог платит третий профиль: чтения и записи хранилища кеша плюс вычисления регенерации. Первая дисциплина инженерии затрат — двадцатиминутная декомпозиция из пролога: назвать доминирующую статью до любой оптимизации, потому что интуиция команды обычно отстаёт на релиз от правды.
Паттерны, умножающие счёт в 10 раз
Каждый из них — механизм, а не страшилка, и у каждого фикс в один экран.
Оптимизатор как публичная ферма ресайза. /_next/image — открытый HTTP-эндпоинт. С wildcard в remotePatterns кто угодно в интернете может скормить ему любой URL картинки с любой шириной и качеством, и каждая уникальная комбинация — оплаченная трансформация плюс запись кеша, которую храните вы. Хотлинкеры и скрейперы перебирают ширины бесплатно. Фикс: явный allowlist в remotePatterns, урезанные deviceSizes/imageSizes (каждая лишняя ширина умножает пространство вариантов) и поднятый minimumCacheTTL, чтобы повторные трансформации амортизировались.
Middleware на каждом статическом ассете. Без matcher middleware выполняется на каждом запросе — включая 40–50 чанков /_next/static, картинок и favicon, которые тянет каждый просмотр страницы. Ваш счётчик инвокаций — не просмотры, а просмотры, умноженные на ассеты-на-страницу. Канонический matcher исключает _next/static, _next/image и favicon.ico, срезая множитель с ~46x до 1x — и убирая лишний хоп задержки с каждого ассета.
revalidate: 1 на длиннохвостом каталоге. Посещаемая страница с revalidate: 1 перегенерируется до раза в секунду: 86 400 потенциальных записей на страницу в день. Сотня горячих страниц — 8,6 миллиона ISR-записей в сутки — ради контента, который реально меняется раз в неделю. Честное окно плюс инвалидация по тегам дают контент свежее панического значения — на четыре порядка дешевле по записям.
Налог случайной динамики. Один вызов cookies() в общем layout выводит все маршруты под ним из Full Route Cache. Тяжёлое RSC-дерево, которому положено рендериться раз в деплой, рендерится на каждый запрос: 5 млн запросов в месяц по 0,4 ГБ-с — это 2 млн ГБ-с за страницы, чей HTML не отличается никогда. Это паттерн из пролога, и счёт — его единственный симптом: задержка приемлемая, страницы правильные, счётчик просто крутится круглосуточно. Аудит: выпишите маршруты по режиму рендеринга и оспорьте каждую динамическую маркетинговую страницу.
Бандлы, перекачиваемые каждый деплой. Имена файлов /_next/static/* содержат хеш содержимого — они созданы, чтобы отдаваться с Cache-Control: public, max-age=31536000, immutable. Reverse-proxy на своём хостинге, теряющий или ослабляющий эти заголовки, заставляет каждого пользователя перекачивать каждый чанк после каждого деплоя: пользователи на мегабайты на деплои-в-неделю чистого egress плюс регрессия задержки, которую никто не атрибутирует правильно.
Оптимизация изображений подскочила в 14 раз за месяц при росте трафика на 8%. В логах — запросы /_next/image на URL картинок партнёрского сайта с w=3840&q=100 и внешними referer. Каков биллинговый механизм скачка?
Помаршрутная математика, точка пересечения и ограждения
Прежде чем решать, переходить ли на self-host или managed-платформу, спросите: сколько стоит конкретный маршрут — и соответствует ли это замыслу? Решения о затратах принимаются по маршрутам — и арифметику делайте по маршрутам. Динамическая страница /pricing на 1 млн запросов в месяц, 280 мс при 1,7 ГБ памяти, стоит около 0,48 ГБ-с на запрос — 480 тысяч ГБ-с в месяц. По сырым инфраструктурным тарифам это единицы долларов; через помесячные тарифы managed-платформы та же форма приземляется в кратное число — но сравнение, которое имеет значение, — с альтернативой: отрендеренная раз в деплой и отданная с CDN, та же страница стоит примерно ноль вычислений и копейки egress. Отношение между «динамика по случайности» и «статика по замыслу» практически бесконечно — поэтому налог случайной динамики заслуживает отдельного аудита. Гоняйте эту математику и в обратную сторону: маршрут, честно персонализирующий каждый ответ, обязан стоить ГБ-часы — цель не минимальный счёт, а счёт, совпадающий с замыслом.
Точка пересечения с self-host из юнита про деплой становится резче после декомпозиции. Ровный трафик — скажем, устойчивые 80 req/s с ограниченными пиками — имеет форму плоской ёмкости: пара VM плюс обычный CDN приземляются возле 10–15% managed-счёта на том же профиле, и картиночный конвейер (sharp на своём CPU за кешем CDN) — обычно самая крупная статья, которую можно репатриировать первой. Пиковый трафик с низким полом инвертирует математику: тариф за запрос обыгрывает оплату простаивающей ёмкости, а операционная цена флота съедает экономию. Декомпозиция говорит, какой формы вы — и переносить ли всё, ничего или только картиночную статью.
Ограждения превращают это в систему вместо героического квартального аудита. Алерты расходов по статьям, а не по итогу — скачок картинок в 14 раз из пролога был итогом 3,4x, а скачок 40x на маленькой статье неделями прячется внутри итога 1,5x. Ревью стоимости в шаблоне PR для новых маршрутов: четыре вопроса — статика или динамика, и намеренно ли; какие request-API трогает; добавляет ли источники картинок или ширины; меняет ли matcher middleware. Каждый вопрос отображается в один из пяти счётчиков. Ежемесячный ритуал топ-10: отранжируйте маршруты по запросам, умноженным на ГБ-с (для картиночных приложений добавьте счётчики трансформаций), прочитайте список вслух за пятнадцать минут и почините голову списка. Список скучно стабилен — именно поэтому месяц, когда он изменился, и есть месяц, когда вы рано поймали регрессию.
Страницы маркетингового сайта выглядят правильно и грузятся быстро, но ГБ-часы функций месяцами высокие. Корневой layout вызывает cookies(), чтобы прочитать тему оформления. Каков механизм затрат и самый дешёвый фикс?
- 01Назовите пять статей serverless-счёта Next.js и какая форма приложения максимизирует каждую.
- 02Приведите три паттерна 10x с механизмами и фиксами в один экран.
Инженерия затрат начинается с декомпозиции: счёт — это пять счётчиков: ГБ-часы функций, которые крутит доля динамического рендера и его вес; трансформации изображений, считаемые за уникальный вариант исходник-ширина-качество; egress (исходящий трафик, биллируемый за ГБ) за исходящий ГБ; чтения и записи ISR о хранилище кеша; edge-инвокации через middleware — и какой из них доминирует, определяет форма приложения: контентные сайты жгут картинки и egress, SaaS-дашборды — ГБ-часы, ISR-каталоги — операции кеша. Паттерны, умножающие счёт в 10 раз, механичны, и у каждого фикс в один экран: wildcard в remotePatterns превращает /_next/image в публичную ферму ресайза, где чужаки чеканят оплаченные варианты (allowlist, урезать массивы размеров, поднять minimumCacheTTL); middleware без matcher выполняется на каждом статическом ассете и умножает инвокации на ассеты-на-страницу (канонический исключающий matcher); revalidate:1 на длинном хвосте пишет до 86 400 раз на страницу в день ради еженедельно меняющегося контента (честные окна плюс инвалидация по тегам); один cookies() в layout переводит целый статический сайт в рендеры на каждый запрос, чей единственный симптом — счёт (сузить динамическую границу); прокси на self-host, ослабляющий immutable-кеширование контент-хешированных файлов /_next/static, перекачивает набор бандлов на пользователя на деплой. Считайте по маршрутам — запросы на ГБ-с против примерно нулевой статики — и пересмотрите точку пересечения с self-host на декомпозированных числах: ровные формы трафика репатриируются хорошо (картиночный конвейер первым), пиковые с низким полом — нет. Затем институционализируйте: алерты расходов по статьям, потому что строка 40x прячется в мягком итоге; вопросы стоимости в шаблоне PR, отображённые в пять счётчиков; ежемесячный ритуал топ-10 дорогих маршрутов — чтобы поймать регрессию в месяц, когда она вышла. Теперь, когда увидите всплеск в счёте, вы сначала декомпозируете, а не оптимизируете вслепую: назовёте доминирующий счётчик, найдёте механизм и потянетесь к однострочному фиксу.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.
Примени это
Примени этот урок в реальном проекте.