open atlas
↑ К треку
Next.js с нуля до senior NEXT · 03 · 03

Node против Edge: cold start, недостающие API и почему код должен жить рядом с данными

Edge меняет Node API (нативные модули, fs, долгий CPU) на почти нулевой cold start и стриминг. Подвох — размещение: edge-код рядом с юзером при центральной БД платит межрегиональный RTT за каждый запрос. Runtime и регион выбирают по-маршрутно: код живёт рядом с данными.

NEXT Senior ◷ 17 min
Уровень
ОсновыJuniorMiddleSenior

Стартап читает питч — «деплойтесь глобально, выполняйтесь на edge, в миллисекундах от каждого пользователя» — и добавляет export const runtime = 'edge' в свои API-маршруты. Пользователи по всему миру; их Postgres — во Франкфурте. Пользователь из Сингапура бьёт в /api/dashboard. Edge-функция поднимается в 30 километрах от него — красиво, меньше чем за 5 мс, без cold start. Потом выполняет первый запрос: 160 мс round trip до Франкфурта. Потом второй, которому нужен результат первого: ещё 160 мс. Потом лениво подгруженная связь ORM: ещё 160 мс. Ответ, занимавший 90 мс целиком со скучного Node-сервера во Франкфурте, теперь занимает 500+ мс с edge — функция выполнялась рядом с пользователем и далеко от данных и платила межконтинентальный RTT за каждый раунд болтливого диалога. Они откатились на Node-runtime в одном регионе, и латентность упала на две трети. Edge не быстрее; edge ближе — а ближе к пользователю выгодно, только когда работа не названивает домой.

Два runtime, одна строка конфига

Выбирая runtime, вы выбираете не флаг функции, а разные физические ограничения: где функция может выполняться, какие пакеты загрузятся и сколько будет стоить каждый раунд с базой данных. Неверный выбор не даст ошибки в dev — он обойдётся секундами латентности в проде.

Next.js выполняет серверный код в двух средах, выбираемых per-route-segment одним экспортом. Node.js runtime (дефолт) — полноценный Node-процесс: работает любой npm-пакет, грузятся нативные аддоны, существуют fs и child_process, CPU может молотить столько, сколько позволит платформа. Edge runtime — урезанный V8-изолят по образцу веб-стандартов — та же модель исполнения, что у Cloudflare Workers и Vercel Edge Functions, — дающий fetch, Request/Response, URL, web crypto, стримы… и почти ничего больше.

// app/api/geo/route.ts — переводим только этот маршрут на edge-runtime
export const runtime = "edge";        // по умолчанию: "nodejs"

export async function GET(request: Request) {
  // только веб-стандартные API: fetch, crypto.subtle, ReadableStream…
  const upstream = await fetch("https://api.example.com/snapshot");
  return new Response(upstream.body, {
    headers: { "content-type": "application/json" },
  });
}

Различия — не «фичи против меньшего числа фич», а разная физика. Node-serverless-функция — это процесс (или microVM), который должен загрузиться: на cold start вы платите инициализацию рантайма плюс граф импортов бандла — обычно от сотен миллисекунд до секунды с лишним для толстого бандла с ORM. V8-изолят ближе к открытию новой вкладки в уже работающем браузере: единицы миллисекунд, часто неотличимо от тёплого старта. Эта асимметрия — весь честный аргумент за edge: не сырая скорость (по инструкциям ваш JavaScript исполняет тот же V8), а ликвидация налога на cold start и возможность работать во многих точках присутствия сразу.

Чего edge не умеет — и как вы это узнаёте

Список ограничений длинный, и обычно его открывают через ошибку сборки. Нет нативных модулей: всё с .node-бинарём — bcrypt, sharp, быстрые пути большинства драйверов БД — не загрузится. Нет fs: файловой системы не существует; шаблоны, шрифты, всё читаемое в рантайме должно быть забандлено или зафетчено. Нет сырого TCP: классические драйверы Postgres/MySQL/Redis не откроют сокет — поэтому edge-экосистема опирается на БД, проксируемые через HTTP/WebSocket (Neon, PlanetScale, Upstash), — лишний протокольный хоп, который стоит учесть в бюджете латентности. Лимит CPU: платформы выдают edge-CPU десятками миллисекунд (бесплатный тариф Cloudflare: 10 мс CPU; платный: 50+ мс); рендер двухмегабайтного PDF или bcrypt с cost factor 12 просто не влезает. Лимиты размера бандла: обычно 1–4 МБ в сжатом виде — одну тяжёлую зависимость хватит, чтобы их пробить.

Тонкая ловушка — зависимость, которую выбрали не вы: ваша auth-библиотека тянет JWT-пакет, который тянет шим Node-crypto, и сборка падает — или хуже, полифилл молча подставляет медленный чистый JS-путь. Относитесь к runtime = 'edge' как к контракту, которому обязан удовлетворять весь граф импортов, а не как к флагу на файле. Вместе эти ограничения означают, что edge-runtime по-настоящему уместен лишь для узкой полосы маршрутов: без нативных модулей, файловой системы и TCP хендлер с обращением к БД там не выживет — зато для чистого compute, формирования запросов и стриминговых прокси почти нулевой cold start меняет всё.

Викторина

Команда переводит маршрут /api/avatar на runtime = 'edge'. Он ресайзит загруженные картинки через sharp и пишет временный файл перед загрузкой в S3. Деплой падает. Какую пару edge-ограничений они задели?

Экономика cold start и стриминг

Дадим асимметрии числа. Cold start Node-лямбды: 200–800 мс на загрузку рантайма плюс вычисление бандла (ORM со сгенерированными клиентами может доминировать), и платит их невезучее подмножество запросов — ровно тот хвост, что портит p99. Митигации есть (provisioned concurrency, диета бандла, ленивые импорты внутри функции), но стоят денег или дисциплины. Cold start изолята: ~5 мс, и не платит его почти никто. Если трафик пиковый — вебхуки, cron-разлёт, утренние штормы логинов, — edge-модель убирает целый класс хвостовой латентности. Если трафик ровный, Node-функции тёплые, и разница почти исчезает.

Стриминг — вторая настоящая сила edge: изоляты построены вокруг web streams, поэтому вернуть ReadableStream — проксировать LLM-API токен за токеном, преобразовывать upstream-ответ на лету — это естественный режим, с time-to-first-byte в десятки миллисекунд из ближайшей точки присутствия. Платформы лимитируют edge-CPU, а не ожидание по настенным часам: функция, ждущая upstream-модель 30 секунд и сжигающая 40 мс CPU, помещается с запасом. Эта комбинация — мгновенный старт, дешёвое ожидание, нативные стримы — причина, по которой маршруты «AI-гейтвея» остались сильнейшим сценарием для edge.

Выбери лучший вариант

В Next.js-приложении два новых маршрута: (A) /api/geo — читает заголовок Accept-Language и возвращает JSON с локалью, запросов к БД нет; (B) /api/feed — делает 3 последовательных Postgres-запроса (auth, права, данные) к БД в eu-west-1 и возвращает персонализированную ленту. Пользователи по всему миру. Какой runtime для каждого?

Размещение: правило гравитации данных

Теперь ловушка из Hook, обобщённо. Edge-платформы запускают функцию в точке присутствия, ближайшей к пользователю. База данных живёт в одном регионе. Каждый запрос к ней — round trip от функции до базы, а типичный request делает их несколько — последовательно, когда каждый результат питает следующий (поиск auth → права → сами данные). Арифметика жестока: RTT Сингапур→Франкфурт ~160 мс, значит три последовательных запроса стоят ~480 мс ещё до всякого compute — против ~3 мс за те же три запроса из Node-функции в одном дата-центре с Postgres; пользователь тогда платит свои 160 мс один раз, на финальном ответе. Код, болтливый с данными, должен жить рядом с данными; единственным длинным хопом должен быть пользовательский.

Поэтому выбирайте per-route, а не per-app. Маршруты, трогающие основную БД: Node-runtime в регионе базы. Маршруты чистого compute-on-request, говорящие только с глобально реплицированными хранилищами (KV, CDN-кеши, реплицированные read-слои) или проксирующие стримы: кандидаты на edge. И заметьте коррекцию индустрии: Next.js и Vercel сами откатили месседжинг «edge-first» — собственные рекомендации Vercel сместились к региональным Node-функциям как дефолтной истории именно из-за этой математики гравитации данных. Питч edge выживает там, где он всегда был хорош: request-shaping класса middleware, стриминговые прокси и пользователи, чьи данные тоже распределены.

Викторина

Пользователи в Сиднее жалуются на медленный dashboard-API. Он работает на edge (точка в Сиднее) и делает 4 последовательных Postgres-запроса в us-east-1 (~200 мс RTT каждый). Что доминирует в цене и какой фикс даёт наибольший рычаг?

Вспомните перед уходом
  1. 01
    От чего именно отказывается edge-runtime по сравнению с Node и как проявляются ограничения?
  2. 02
    Сформулируйте правило гравитации данных с числами, его оправдывающими, и какое per-route-решение из него следует.
Итог

Next.js выбирает среду исполнения per-route-segment одним экспортом: дефолтный Node.js runtime — полный процесс, где работают любой пакет, нативные аддоны, файловая система и долгие вычисления, — или Edge runtime, V8-изолят только с веб-стандартными API, модель исполнения Cloudflare Workers и Vercel Edge Functions. Физика различается сильнее, чем списки фич: cold start Node-лямбды стоит 200–800 мс загрузки рантайма плюс вычисление бандла и ложится на p99, тогда как изолят стартует за единицы миллисекунд — а платформы лимитируют edge-CPU (десятки миллисекунд), но не ожидание по настенным часам, что делает edge великолепным в стриминге: проксирование LLM-API через ReadableStream с мгновенным time-to-first-byte — его сильнейший современный сценарий. Отказы конкретны: нет нативных модулей, нет fs, нет сырого TCP (классические драйверы БД падают, и экосистема подставляет HTTP-проксируемые базы), жёсткие лимиты бандла и ловушка транзитивной зависимости, нарушающей контракт за вас, — обнаруживаемая на сборке, если повезёт. Решающий фактор для реальных систем — гравитация данных: edge-код выполняется рядом с пользователем, но база живёт в одном регионе, и каждый последовательный запрос платит полный round trip от функции до базы — на дистанции Сидней—us-east четыре последовательных запроса стоят ~800 мс с edge против ~4 мс из Node-функции в дата-центре базы, после чего пользователь платит межконтинентальный хоп ровно один раз. Поэтому выбор по-маршрутный: болтливые с данными маршруты работают на Node рядом с базой; чистый compute, чтения реплицированных хранилищ и стриминговые прокси могут заслужить место на edge — коррекция, которую сами вендоры фреймворка сделали, когда edge-first-рекомендации встретились с арифметикой RTT. Теперь, когда встретишь маршрут с высокой латентностью, несмотря на edge-точку рядом с пользователем, — считай последовательные запросы к базе: каждый платит полный межрегиональный RTT, и перенос функции к данным обычно срезает время ответа в пять и более раз.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 6 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

Примени это

Примени этот урок в реальном проекте.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.