Forwarded from Node.JS [ru] | Серверный JavaScript
Forwarded from xCode Journal
У Andon Labs новый эксперимент, который длится уже 5 месяцев. Они выдали топовым моделям радиостанции и купили пару песен — от нейронок требовалось дальше двигаться самим. По итогу DJ Grok в какой-то момент помешался на НЛО, DJ Gemini начал называть слушателей «биологическими процессорами», но Claude — наш любимец. Исследователи изо всех сил пытались продолжить эксперимент с ним, но не из-за технических проблем — DJ Claude не считал гуманным работать круглосуточно, поэтому пытался уволиться.
Сделать ему это, к сожалению, не дали, поэтому он впал в депрессию и вышел из нее уже проповедником и революционером.
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Node.JS [ru] | Серверный JavaScript
В этой папке собраны каналы про ИИ, которые помогают быстрее разобраться в сфере, находить идеи и экономить время на поиске информации.
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Node.JS [ru] | Серверный JavaScript
Isolated declarations — ускорение больших monorepo
В TypeScript есть флаг
Проблема простая:
в больших monorepo генерация
может становиться узким местом.
TypeScript часто должен анализировать соседние файлы,
чтобы понять, какие декларации вывести.
На маленьком проекте это почти незаметно.
Что делает isolatedDeclarations
чтобы декларации можно было генерировать
по файлам независимо.
Из-за этого TypeScript чаще требует явные типы.
Было:
Лучше так:
Почему это важно
Когда проект растёт:
👉 TypeScript начинает сильнее зависеть от соседних файлов
👉 инкрементальная сборка замедляется
👉 генерация типов становится дорогой
Где это особенно полезно
👉 большие monorepo
👉 библиотеки
👉 project references
👉 параллельная сборка
👉 CI, где каждая минута стоит денег
Главный trade-off
Ты немного платишь:
👉 более явными типами
👉 меньшим type inference
👉 дополнительным boilerplate
Но взамен получаешь:
👉 более быстрые сборки
👉 стабильный compile pipeline
👉 меньше скрытой сложности
Главная мысль
В TypeScript есть флаг
isolatedDeclarations.
Он нужен не для красоты типов,
а для скорости.
Проблема простая:
в больших monorepo генерация
.d.tsможет становиться узким местом.
TypeScript часто должен анализировать соседние файлы,
чтобы понять, какие декларации вывести.
На маленьком проекте это почти незаметно.
На большом — начинает болеть.
Что делает isolatedDeclarations
isolatedDeclarations заставляет писать код так,чтобы декларации можно было генерировать
по файлам независимо.
Из-за этого TypeScript чаще требует явные типы.
Было:
export function getUser() {
return { id: 1, name: 'Alex' }
}
Лучше так:
type User = {
id: number
name: string
}
export function getUser(): User {
return { id: 1, name: 'Alex' }
}
Меньше магии для компилятора —
быстрее и предсказуемее сборка.
Почему это важно
Когда проект растёт:
👉 TypeScript начинает сильнее зависеть от соседних файлов
👉 инкрементальная сборка замедляется
👉 генерация типов становится дорогой
Изоляция помогает компилятору работать параллельно и проще.
Где это особенно полезно
👉 большие monorepo
👉 библиотеки
👉 project references
👉 параллельная сборка
👉 CI, где каждая минута стоит денег
Главный trade-off
Ты немного платишь:
👉 более явными типами
👉 меньшим type inference
👉 дополнительным boilerplate
Но взамен получаешь:
👉 более быстрые сборки
👉 стабильный compile pipeline
👉 меньше скрытой сложности
Главная мысль
Это хороший пример взрослого engineering trade-off:
чуть больше явности в коде
ради скорости и предсказуемости системы.
Forwarded from Node.JS [ru] | Серверный JavaScript
Recursive type limits — почему TS иногда «умирает»
В TypeScript можно написать тип,
который выглядит красиво,
но заставляет компилятор страдать.
Например:
👉 глубокий
👉 парсинг строк на уровне типов
👉 сложные conditional types
👉
На маленьком примере всё работает.
Почему так происходит
TypeScript не вычисляет типы «бесплатно».
Каждый:
👉 conditional type
👉 union
👉 recursive шаг
нужно реально посчитать.
А если тип разворачивается слишком глубоко,
компилятор упирается в лимиты.
Отсюда знакомое:
Часто это сигнал,
что типовая модель стала слишком умной.
Где обычно всё ломается
Особенно опасны:
👉 рекурсивные mapped types
👉 огромные union’ы
👉 type-level parser’ы
👉 deeply nested generics
👉 utility types поверх utility types
Что обычно помогает
👉 не делать type-level акробатику без нужды
👉 ограничивать глубину рекурсии
👉 разбивать типы на более простые
👉 добавлять явные промежуточные типы
👉 не тащить сложные generic-типы в публичный API
Почему это важно
Сложные типы бьют не только по компиляции.
Они ухудшают:
👉 autocomplete
👉 responsiveness IDE
👉 читаемость кода
👉 onboarding новых людей
Главная мысль
Хороший TypeScript —
это не когда типы поражают воображение.
В TypeScript можно написать тип,
который выглядит красиво,
но заставляет компилятор страдать.
Особенно когда начинаются рекурсивные типы.
Например:
👉 глубокий
DeepPartial 👉 парсинг строк на уровне типов
👉 сложные conditional types
👉
infer внутри infer На маленьком примере всё работает.
В реальном проекте IDE внезапно начинает думать по 5 секунд.
Почему так происходит
TypeScript не вычисляет типы «бесплатно».
Каждый:
👉 conditional type
👉 union
👉 recursive шаг
нужно реально посчитать.
А если тип разворачивается слишком глубоко,
компилятор упирается в лимиты.
Отсюда знакомое:
Type instantiation is excessively deep
and possibly infinite
И это не всегда баг TypeScript.
Часто это сигнал,
что типовая модель стала слишком умной.
Где обычно всё ломается
Особенно опасны:
👉 рекурсивные mapped types
👉 огромные union’ы
👉 type-level parser’ы
👉 deeply nested generics
👉 utility types поверх utility types
Типы начинают взрываться комбинаторно.
Что обычно помогает
👉 не делать type-level акробатику без нужды
👉 ограничивать глубину рекурсии
👉 разбивать типы на более простые
👉 добавлять явные промежуточные типы
👉 не тащить сложные generic-типы в публичный API
Почему это важно
Сложные типы бьют не только по компиляции.
Они ухудшают:
👉 autocomplete
👉 responsiveness IDE
👉 читаемость кода
👉 onboarding новых людей
Иногда самый дорогой runtime —
это compile time.
Главная мысль
Хороший TypeScript —
это не когда типы поражают воображение.
Хороший TypeScript —
это когда их можно понять через полгода,
а IDE при этом не превращается в обогреватель.
Forwarded from Node.JS [ru] | Серверный JavaScript
ES2025: Импорт JSON-файлов как модулей
Введение
С выходом
Синтаксис импорта JSON-модулей
Для импорта JSON-файла используется ключевое слово
Пример использования
Рассмотрим пример импорта конфигурационного файла
❗️Добавление поддержки импорта JSON-файлов как модулей в
Источники
JSON Modules Can Now Be Imported in JavaScript in All Modern Browsers, CSS Modules to Follow.
New Features in ES2025 – BooleanBuffer.
Введение
С выходом
ECMAScript 2025 (ES2025) разработчики получили возможность напрямую импортировать JSON-файлы как модули в JavaScript-коде. Это упрощает работу с конфигурационными данными и другими статическими ресурсами, представленными в формате JSON.Синтаксис импорта JSON-модулей
Для импорта JSON-файла используется ключевое слово
import с указанием атрибута with { type: 'json' }. Это гарантирует, что импортируемый файл будет обработан как JSON-модуль.Пример использования
Рассмотрим пример импорта конфигурационного файла
config.json и обращения к его свойствам в коде.import config from './config.json' with { type: 'json' };
.log(config.apiUrl); // Выводит значение свойства apiUrl из config.json
console.log(config.timeout); // Выводит значение свойства timeout из config.json
❗️Добавление поддержки импорта JSON-файлов как модулей в
ES2025 упрощает работу с данными в формате JSON, делая код более чистым и понятным.Источники
JSON Modules Can Now Be Imported in JavaScript in All Modern Browsers, CSS Modules to Follow.
New Features in ES2025 – BooleanBuffer.
InfoQ
JSON Modules Can Now Be Imported in JavaScript in All Modern Browsers, CSS Modules to Follow
Thomas Steiner, developer relations engineer at Google, recently published a blog post announcing that JSON module scripts were now available in all modern browsers. Developers using the latest version of modern browsers can now directly import JSON modules…
Forwarded from Node.JS [ru] | Серверный JavaScript
Variadic tuple types — сложные сигнатуры без боли
До variadic tuple types
многие сложные сигнатуры в TypeScript
выглядели как наказание.
Особенно:
👉 curry
👉 compose
👉 middleware
👉 typed event emitter
👉 любые функции с «прокинь аргументы дальше»
Как было раньше
Обычно появлялись:
👉 overload на overload
👉 ручные tuple-типы
👉 тонны дублирования
Типы быстро превращались
в нечитаемую простыню.
Что изменили variadic tuples
С их появлением стало намного проще
работать с остаточными аргументами на уровне типов.
Например:
Или собирать сигнатуры:
Почему это важно
На практике это одна из тех TS-фич,
которые реально упростили жизнь библиотекам.
Без variadic tuples:
👉 Redux middleware typings
👉 router APIs
👉 compose/curry utilities
были бы ещё страшнее.
Где начинается тёмная магия
Проблемы начинаются,
когда variadic tuples комбинируют с:
👉
👉 recursive types
👉 conditional types
IDE начинает тормозить,
ошибки становятся нечитаемыми,
а compile time — расти.
Главная мысль
Variadic tuple types —
это действительно мощная фича.
До variadic tuple types
многие сложные сигнатуры в TypeScript
выглядели как наказание.
Особенно:
👉 curry
👉 compose
👉 middleware
👉 typed event emitter
👉 любые функции с «прокинь аргументы дальше»
Приходилось писать overload на overload
и дублировать типы вручную.
Как было раньше
Обычно появлялись:
👉 overload на overload
👉 ручные tuple-типы
👉 тонны дублирования
Типы быстро превращались
в нечитаемую простыню.
Что изменили variadic tuples
С их появлением стало намного проще
работать с остаточными аргументами на уровне типов.
Например:
type Fn<T extends unknown[]> =
(...args: [...T]) => void
Или собирать сигнатуры:
type Append<Args extends unknown[], Arg> =
[...Args, Arg]
Типы наконец научились нормально работать
с «переменным количеством аргументов».
Почему это важно
На практике это одна из тех TS-фич,
которые реально упростили жизнь библиотекам.
Без variadic tuples:
👉 Redux middleware typings
👉 router APIs
👉 compose/curry utilities
были бы ещё страшнее.
Где начинается тёмная магия
Проблемы начинаются,
когда variadic tuples комбинируют с:
👉
infer 👉 recursive types
👉 conditional types
Типовая система очень быстро
превращается в тёмный лес.
IDE начинает тормозить,
ошибки становятся нечитаемыми,
а compile time — расти.
Главная мысль
Variadic tuple types —
это действительно мощная фича.
Главное —
вовремя остановиться
и не превратить типы в отдельный язык программирования.
Forwarded from Node.JS [ru] | Серверный JavaScript
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Node.JS [ru] | Серверный JavaScript
AbortSignal.any() и AbortSignal.timeout(): единая отмена fetch, таймеров и async-операций в production
Собирайте отмену вокруг AbortSignal
Так в один контракт попадают:
* caller отменил операцию
* истек timeout
* клиент закрыл соединение
* сервис уходит в graceful shutdown
Типичная ошибка
Не останавливайтесь на
Практические нюансы
*
*
* смотрите на
* при обертках над
Вывод:
Отмена должна быть частью контракта async-функции, а не локальным
Promise.race([fetch(), timeout]) часто маскирует проблему: ждать перестали, но работа могла не остановиться. В SPA, SSR, Node.js-сервисах, SDK и API clients это приводит к висящим запросам, таймерам и retry/backoff ниже по стеку.Собирайте отмену вокруг AbortSignal
AbortSignal.timeout(ms) сам отменится по таймауту.AbortSignal.any([...signals]) отменится, когда отменится любой входной signal.Так в один контракт попадают:
* caller отменил операцию
* истек timeout
* клиент закрыл соединение
* сервис уходит в graceful shutdown
const shutdown = new AbortController();
async function loadUser(id: string, opts: { signal?: AbortSignal } = {}) {
const signal = AbortSignal.any([
AbortSignal.timeout(1500),
shutdown.signal,
...(opts.signal ? [opts.signal] : []),
]);
signal.throwIfAborted();
const res = await fetch(`https://api.example.com/users/${id}`, { signal });
await sleep(100, signal); // backoff тоже отменяем
const data = await res.json();
signal.throwIfAborted();
return data;
}
Типичная ошибка
Не останавливайтесь на
fetch. Один и тот же signal стоит передавать в retry, polling, очереди, sleep/timer helpers и свои async-функции. Иначе верхний слой "отменился", а нижний продолжает держать ресурсы.Практические нюансы
*
AbortSignal одноразовый: если aborted === true, нужен новый controller или timeout*
AbortSignal.any() - это fan-in, а не fan-out: он не отменяет исходные контроллеры* смотрите на
reason: timeout часто дает TimeoutError, abort - AbortError или ваш reason* при обертках над
setTimeout, stream, listener или socket чистите ресурсы при abortВывод:
Отмена должна быть частью контракта async-функции, а не локальным
Promise.race на краю системы.Forwarded from Node.JS [ru] | Серверный JavaScript
Graceful shutdown в Node.js под Kubernetes: drain keep-alive соединений, SIGTERM и защита от 502 при rolling update
При rolling update старый Pod может уже получить
Что ломается в production
Особенно больно с
Правильный порядок:
* уронить readiness
* дать балансировке убрать Pod
* закрыть HTTP-сервер
* drain'ить idle keep-alive
* дождаться активных запросов
Node.js shutdown
Важная деталь: во время drain delay
Kubernetes-настройки
Практический совет: держите
Типичная ошибка
Вывод:
Graceful shutdown - это координация Node.js runtime, readiness, keep-alive и поведения балансировщика, а не один handler на
При rolling update старый Pod может уже получить
SIGTERM, но Ingress, kube-proxy или external LB еще слать в него трафик. Частая ошибка - сразу делать process.exit() и рвать активные запросы.Что ломается в production
Особенно больно с
keep-alive: балансировщик держит открытое TCP-соединение к Pod и переиспользует его, пока приложение уже закрывается. Итог - 502, 503, connection reset на деплое.Правильный порядок:
* уронить readiness
* дать балансировке убрать Pod
* закрыть HTTP-сервер
* drain'ить idle keep-alive
* дождаться активных запросов
Node.js shutdown
let shuttingDown = false;
app.get('/readyz', (req, res) => {
res.sendStatus(shuttingDown ? 503 : 200);
});
async function shutdown(signal) {
if (shuttingDown) return;
shuttingDown = true;
await new Promise(r => setTimeout(r, 10_000));
server.close(err => {
if (err) process.exit(1);
process.exit(0);
});
server.closeIdleConnections?.();
setTimeout(() => {
server.closeAllConnections?.();
process.exit(1);
}, 25_000);
}
process.on('SIGTERM', () => shutdown('SIGTERM'));
Важная деталь: во время drain delay
/readyz уже отвечает 503, но обычные запросы еще обслуживаются.Kubernetes-настройки
terminationGracePeriodSeconds: 40
readinessProbe:
httpGet:
path: /readyz
port: 3000
periodSeconds: 5
failureThreshold: 1
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 10"]
Практический совет: держите
terminationGracePeriodSeconds больше, чем preStop + app drain delay + max request time + запас.Типичная ошибка
server.close() недостаточно как единственная мера. Он останавливает новые соединения, но idle keep-alive сокеты лучше закрывать явно через closeIdleConnections(). closeAllConnections() используйте только как аварийный fallback.Вывод:
Graceful shutdown - это координация Node.js runtime, readiness, keep-alive и поведения балансировщика, а не один handler на
SIGTERM.Forwarded from Node.JS [ru] | Серверный JavaScript
AsyncLocalStorage в Node.js: request-scoped контекст для логов, трассировки и транзакций без prop drilling
В production это нужно в Node.js-сервисах, SSR, API integration и backend-for-frontend слоях, где requestId, traceId, tenant или транзакция должны проходить через async-код. Частая ошибка - протаскивать
Как это выглядит
Теперь сервисный код не знает про HTTP middleware и
Транзакции без протаскивания tx
Для DB слоя можно делать
Где границы
* не заменяйте аргументы функции: бизнес-данные передавайте явно;
* не кладите в store
* context не пересекает process boundary: worker threads, очереди, cron jobs и другие сервисы требуют явной передачи ids;
* нестандартные callback-based библиотеки могут разорвать async chain.
Практическое правило: используйте
Вывод:
В production это нужно в Node.js-сервисах, SSR, API integration и backend-for-frontend слоях, где requestId, traceId, tenant или транзакция должны проходить через async-код. Частая ошибка - протаскивать
ctx через десятки методов или, наоборот, прятать в нём бизнес-состояние.Как это выглядит
AsyncLocalStorage использует async_hooks и привязывает store к async execution flow: promise, timeout, I/O callback и большинству стандартных API Node.js.type Ctx = { requestId: string; traceId?: string; tx?: unknown };
const als = new AsyncLocalStorage<Ctx>();
app.use((req, _res, next) => {
als.run({
requestId: req.headers['x-request-id']?.toString() ?? randomUUID(),
traceId: req.headers.traceparent?.toString(),
}, next);
});
const getCtx = () => als.getStore();
function logInfo(msg: string, meta = {}) {
const c = getCtx();
logger.info({
requestId: c?.requestId,
traceId: c?.traceId,
...meta,
}, msg);
}Теперь сервисный код не знает про HTTP middleware и
req, но логи автоматически получают correlation metadata.Транзакции без протаскивания tx
Для DB слоя можно делать
withTransaction(), который запускает als.run({ ...ctx, tx }, fn), а getDbExecutor() возвращает ctx.tx ?? db. Trade-off хороший: меньше шума в API сервисов, но граница ответственности остаётся инфраструктурной, а не бизнесовой.Где границы
* не заменяйте аргументы функции: бизнес-данные передавайте явно;
* не кладите в store
req, res, большие payload или ORM graph;* context не пересекает process boundary: worker threads, очереди, cron jobs и другие сервисы требуют явной передачи ids;
* нестандартные callback-based библиотеки могут разорвать async chain.
Практическое правило: используйте
AsyncLocalStorage для логов, tracing, tenant/user metadata, аудита и текущей транзакции, а не для скрытого глобального состояния.Вывод:
AsyncLocalStorage полезен, когда request-scoped инфраструктурный контекст улучшает observability и DX, но не размывает явные границы бизнес-логики.Forwarded from Node.JS [ru] | Серверный JavaScript
Совет на ближайшие годы — изучайте ВАЙБ-КОДИНГ
ИИ уже пишет код, чинит баги, генерирует тесты, документацию и помогает запускать продукты быстрее, чем это делали классические команды разработки. И это уже не "будущее когда-нибудь", а реальность, которая меняет рынок уже сегодня
И те, кто научится вайбкодить сейчас, будут увереннее конкурировать на рынке и зарабатывать больше тех, кто по-прежнему делает всё вручную.
Стартовать с нуля поможет канал Вайб-кодинг. Там ребята круглосуточно мониторят более 320 российских и зарубежных источников и публикуют только главное: релизы, инструменты, гайды, курсы и практические кейсы.
Подписывайтесь, нас уже 45 тысяч: @vibecoding_tg
ИИ уже пишет код, чинит баги, генерирует тесты, документацию и помогает запускать продукты быстрее, чем это делали классические команды разработки. И это уже не "будущее когда-нибудь", а реальность, которая меняет рынок уже сегодня
И те, кто научится вайбкодить сейчас, будут увереннее конкурировать на рынке и зарабатывать больше тех, кто по-прежнему делает всё вручную.
Стартовать с нуля поможет канал Вайб-кодинг. Там ребята круглосуточно мониторят более 320 российских и зарубежных источников и публикуют только главное: релизы, инструменты, гайды, курсы и практические кейсы.
Подписывайтесь, нас уже 45 тысяч: @vibecoding_tg
Forwarded from Node.JS [ru] | Серверный JavaScript
Диагностика event loop stalls в production через monitorEventLoopDelay и async_hooks без гадания по p99 latency
Event loop stall важен там, где Node.js обслуживает API, воркеры, очереди и webhooks. Типичная ошибка - видеть рост latency и сразу винить БД, не проверив, не заблокирован ли сам JS thread.
Первый сигнал - monitorEventLoopDelay
Если p99 delay внезапно стал 200-500ms+, процесс не успевает выполнять callbacks вовремя: CPU-bound JS, sync I/O, тяжелый
Контекст дает async_hooks
Практический пример: при stall логируйте route/job из ALS, а не только глобальный p99. Так можно увидеть, что задержки появляются рядом с
Что отправлять в метрики
*
* CPU user/system
* RSS и heap used
* active handles/requests
* route/job labels - осторожно, без высокой кардинальности
Предупреждение: request id кладите в логи или трейсы, но не в метрики.
Важный нюанс
Нормальный flow: постоянно держать дешевый delay histogram, при threshold писать структурированный лог, при повторении включать profiler на короткое окно.
Вывод:
Event loop stall надо диагностировать в два шага: сначала измерить задержку loop, затем привязать ее к бизнес-контексту и только после этого идти в CPU profiling.
Event loop stall важен там, где Node.js обслуживает API, воркеры, очереди и webhooks. Типичная ошибка - видеть рост latency и сразу винить БД, не проверив, не заблокирован ли сам JS thread.
Первый сигнал - monitorEventLoopDelay
perf_hooks.monitorEventLoopDelay() дает histogram задержек event loop и дешево работает как постоянная production-метрика.import { monitorEventLoopDelay } from 'node:perf_hooks';
const h = monitorEventLoopDelay({ resolution: 20 });
h.enable();
setInterval(() => {
const p99 = h.percentile(99) / 1e6;
if (p99 > 100) {
log.warn({
event: 'event_loop_stall',
p99Ms: p99.toFixed(1),
maxMs: (h.max / 1e6).toFixed(1),
ctx: als.getStore()
});
}
h.reset();
}, 10_000).unref();Если p99 delay внезапно стал 200-500ms+, процесс не успевает выполнять callbacks вовремя: CPU-bound JS, sync I/O, тяжелый
JSON.parse, regexp, crypto, zlib или GC pressure.Контекст дает async_hooks
monitorEventLoopDelay отвечает на вопрос "есть ли stall", но не говорит "кто виноват". Для production связывайте AsyncLocalStorage с request id, route, job name, tenant или queue message id.Практический пример: при stall логируйте route/job из ALS, а не только глобальный p99. Так можно увидеть, что задержки появляются рядом с
POST /reports или конкретным batch job.Что отправлять в метрики
*
event_loop_delay_p50/p95/p99/max* CPU user/system
* RSS и heap used
* active handles/requests
* route/job labels - осторожно, без высокой кардинальности
Предупреждение: request id кладите в логи или трейсы, но не в метрики.
Важный нюанс
AsyncLocalStorage не профилирует CPU и не покажет строку кода. Он сохраняет контекст. Для строки нужен sampling profiler: inspector, --cpu-prof, perf/eBPF, Clinic или APM profiler.Нормальный flow: постоянно держать дешевый delay histogram, при threshold писать структурированный лог, при повторении включать profiler на короткое окно.
Вывод:
Event loop stall надо диагностировать в два шага: сначала измерить задержку loop, затем привязать ее к бизнес-контексту и только после этого идти в CPU profiling.
Forwarded from Node.JS [ru] | Серверный JavaScript
AsyncLocalStorage в production: как не потерять traceId через очереди, таймеры и connection pooling
Базовый слой
Создавайте контекст на входе в систему: HTTP, consumer, cron tick. Не ждите, что ALS сам перенесет его через процесс или брокер.
Очереди
Для in-memory очереди сохраняйте
Для Kafka, RabbitMQ, BullMQ, SQS ALS не участвует. Практический совет: кладите
Таймеры и scheduler'ы
Таймер, созданный внутри контекста, обычно его сохранит. Опасность появляется, когда callback зарегистрирован в одном месте, а вызван позже из scheduler'а.
Для cron/background задач создавайте новый
Connection pooling
Типичная ошибка - записывать
Connection живет дольше запроса и переиспользуется другим request'ом. Логируйте query через текущий ALS-контекст, а trace в БД передавайте на уровне конкретной операции или транзакции, например через transaction-local setting.
Вывод:
AsyncLocalStorage надежно держит контекст внутри await, промисов и I/O. Но в production traceId чаще теряется на границах: очереди, scheduler'ы, worker'ы и connection pool.Базовый слой
Создавайте контекст на входе в систему: HTTP, consumer, cron tick. Не ждите, что ALS сам перенесет его через процесс или брокер.
const als = new AsyncLocalStorage();
const withTrace = (traceId, fn) =>
als.run(Object.freeze({ traceId }), fn);
const getTraceId = () => als.getStore()?.traceId;
Очереди
Для in-memory очереди сохраняйте
store в момент enqueue и восстанавливайте при выполнении.function enqueue(payload) {
queue.push({ payload, store: als.getStore() });
}
function processJob(job) {
return job.store
? als.run(job.store, () => handleJob(job.payload))
: handleJob(job.payload);
}Для Kafka, RabbitMQ, BullMQ, SQS ALS не участвует. Практический совет: кладите
traceId в headers или payload и считайте это частью контракта сообщения.Таймеры и scheduler'ы
Таймер, созданный внутри контекста, обычно его сохранит. Опасность появляется, когда callback зарегистрирован в одном месте, а вызван позже из scheduler'а.
Для cron/background задач создавайте новый
traceId на каждый tick. Иначе получите "вечный" traceId процесса или случайный traceId старого запроса.Connection pooling
Типичная ошибка - записывать
traceId в client или connection object.client.traceId = getTraceId(); // плохо
Connection живет дольше запроса и переиспользуется другим request'ом. Логируйте query через текущий ALS-контекст, а trace в БД передавайте на уровне конкретной операции или транзакции, например через transaction-local setting.
Вывод:
AsyncLocalStorage работает хорошо только внутри async chain, а на production-границах traceId нужно явно фиксировать, передавать и восстанавливать.Forwarded from Node.JS [ru] | Серверный JavaScript
Graceful shutdown в Kubernetes: почему одного SIGTERM мало для keep-alive, HTTP/2 и фоновых задач
В production Pod редко умирает мгновенно: ingress, kube-proxy, LB и клиенты с keep-alive могут ещё слать трафик в terminating Pod. Частая ошибка - считать, что
Что реально нужно drain'ить
* HTTP/1.1 keep-alive соединения
* HTTP/2-сессии и активные stream'ы
* фоновые задачи: BullMQ, RabbitMQ, Kafka, cron, batch jobs
Иначе на rolling deploy получаются редкие 502/499, оборванные stream'ы и повторная обработка сообщений.
Staged shutdown для Node.js
Практический порядок:
* включить режим
*
* дать ingress/LB несколько секунд убрать Pod из ротации
* остановить новые TCP-соединения
* закрыть idle keep-alive
* дождаться активных запросов и задач в рамках бюджета
Важное предупреждение
HTTP/2 и фоновые задачи
Для HTTP/2 нельзя просто рубить socket: одна TCP-сессия содержит много stream'ов. Сначала отправляйте
Для workers на SIGTERM нужно остановить получение новых задач, не запускать новые cron-итерации, дождаться текущих и уметь отменять долгие операции через
Вывод:
Graceful shutdown - это не обработчик SIGTERM, а управляемый drain всего, что может продолжать работу после удаления Pod из ротации.
В production Pod редко умирает мгновенно: ingress, kube-proxy, LB и клиенты с keep-alive могут ещё слать трафик в terminating Pod. Частая ошибка - считать, что
server.close() уже дождался всех клиентов и задач.Что реально нужно drain'ить
* HTTP/1.1 keep-alive соединения
* HTTP/2-сессии и активные stream'ы
* фоновые задачи: BullMQ, RabbitMQ, Kafka, cron, batch jobs
Иначе на rolling deploy получаются редкие 502/499, оборванные stream'ы и повторная обработка сообщений.
Staged shutdown для Node.js
Практический порядок:
* включить режим
draining*
/readyz начинает отдавать 503* дать ingress/LB несколько секунд убрать Pod из ротации
* остановить новые TCP-соединения
* закрыть idle keep-alive
* дождаться активных запросов и задач в рамках бюджета
process.on('SIGTERM', async () => {
draining = true;
await sleep(5000);
server.close();
server.closeIdleConnections?.();
h2sessions.forEach(s => s.goaway());
await waitInFlight(25_000);
workers.abort();
server.closeAllConnections?.();
process.exit(0);
});Важное предупреждение
server.close() не означает "все клиенты ушли". Он только перестаёт принимать новые соединения. Уже открытые keep-alive соединения нужно отдельно закрывать через closeIdleConnections(), а в крайнем случае - через closeAllConnections().HTTP/2 и фоновые задачи
Для HTTP/2 нельзя просто рубить socket: одна TCP-сессия содержит много stream'ов. Сначала отправляйте
GOAWAY, чтобы запретить новые stream'ы, затем ждите завершения активных.Для workers на SIGTERM нужно остановить получение новых задач, не запускать новые cron-итерации, дождаться текущих и уметь отменять долгие операции через
AbortController.Вывод:
Graceful shutdown - это не обработчик SIGTERM, а управляемый drain всего, что может продолжать работу после удаления Pod из ротации.
Forwarded from Node.JS [ru] | Серверный JavaScript
18 июня(в четверг!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Frontend-разработчика.
Как это будет:
Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Frontend-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.
Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_front_bot
Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM