SuperOleg dev notes
1.87K subscribers
56 photos
149 links
Обзоры новостей и статей из мира frontend, интересные кейсы, исследования и мысли вслух

https://github.com/SuperOleg39

https://twitter.com/ODrapeza

@SuperOleg39
Download Telegram
Привет!

Сделали очередной заход на добавление кэша DNS lookup.

Теория - резолв DNS синхронный, и треды libuv занимает, и негативно влияет на производительность приложений, лишняя нагрузка на event loop.

Использовал либу https://github.com/szmarczak/cacheable-lookup

TTL кэша высокий ставить кажется опасным, начали с 15 секунд, оставил 1 минуту, и хочется попробовать еще повысить - риск тут вижу, что можем ловить совершенно не нужные ошибки запросов к сторонним сервисам в момент смены этими сервисами IP адреса, что по идее может происходить не редко в k8s кластерах.

На этой неделе раскатили и понаблюдали на одном приложении, какой эффект получили по HTTP запросам:
- заметно ускорились запросы, которые идут в обход трамвайных HTTP клиентов
- незначительно ускорились запросы через наши базовые HTTP клиенты - тут большой буст к перформансу уже дает активная опция keepAlive для http/https агентов ноды, установка соединений происходит гораздо реже. Плюс lru-кэши и дедупликация для запросов.
- соответственно на наших метриках активных соединений почти пропали GetAddrInfoReqWrap

По производительности, сначала показалось что эффекта вообще нет - приложение рестартует, отъедает побольше памяти, и снова повышается лаг эвент лупа - кажется по большей части из-за роста времени работы Garbage Collector.

Но к концу недели все-таки стал заметен эффект, пиковые значения лага эвент лупа, времени сбора мусора, потребление CPU и CPU троттлинг - все стало пониже, пример графиков закину в канал отдельным сообщением (там где перцентиль не указан вроде бы 95).

Я думаю, этот эффект был бы менее заметен если бы мы выделяли подам больше 1000 mCPU в k8s, так как и треды libuv и сборка мусора выполнялись бы по настоящему параллельно, и не было бы такого высокого CPU троттлинга.

Хорошая статья, где предлагается оптимальным использовать 1150 mCPU, или 1.15 ядра на под, и объясняются все механизмы из предыдущего абзаца - https://medium.com/pipedrive-engineering/how-we-choked-our-kubernetes-nodejs-services-932acc8cc2be

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

Обязательно делитесь вашим опытом аналогичных оптимизаций, и в особенности не удачных!
🔥42👍2
Пример ускорения запроса без агента с keepAlive: true
В комментарии к посту закинули очень крутой кейс, запуск ноды с увеличенным параметром max-semi-space-size!

Грубо говоря, реже делаем очистки памяти и соответственно реже нагружаем приложение этой работой, за счет большего потребления памяти.

Больше инфы по теме:
- https://www.alibabacloud.com/blog/better-node-application-performance-through-gc-optimization_595119
- https://github.com/nodejs/node/issues/42511

При чем недавно пол-дня гуглил, какие есть возможности управлять GC, как делать очистку пореже, и этот флаг не нашел)

Отдельно отмечу работу ChatGPT, который придумал специально для меня целый набор флагов и опций, как менять стратегии сборщика мусора v8 :)
🔥8💯1
Привет!

Ищу коллегу в нашу платформенную команду для разработки и поддержки React мета-фреймворка tramvai

Мы - одна из команд в Tinkoff Coretech Frontend, которая отвечает за создание инструментов и общих практик для огромного фронтенд коммьюнити в Тинькофф.

Почему у нас классно:
- работа в небольшой и технически прокачанной команде, без менеджмента, бюрократии и лишних встреч
- сами себе продакты - ставим задачи и выбираем приоритеты, ориентируясь на потребности наших пользователей и тренды развития JS экосистемы
- ежедневные челленджи - сложные задачи из самых разных областей: клиентская и серверная разработка, консольные утилиты, бандлеры, производительность, логи и метрики, CI/CD процессы
- самые крутые пользователи - разработчики, активный Inner Source и хорошая обратная связь
- несколько десятков tramvai приложений в продакшене - battle-tested это про нас
- заботимся о качестве продукта - пишем RFC и ADR, интеграционные тесты и документацию, проходим code review, придерживаемся Zero Bug Policy, собираем обратную связь
- регулярно контрибьютим в Open Source, также имеем большие планы на OSS для tramvai

Наши основные задачи:
- Разработка собственного SSR фреймворка
- Поддержка пользователей фреймворка - исправление багов, ответы на вопросы, помощь с ревью и миграциями
- Обеспечивать качественную работу всех tramvai приложений в Tinkoff экосистеме - интеграционные тесты, помощь в разборе инцидентов, рекомендации по улучшению
- Исследовать и исправлять проблемы производительности и на сервере, и на клиенте
- Совместная работа с другими платформенными командами - tramvai содержит много полезных интеграций для итоговых пользователей и приложений

Наш технический стек:
- монорепозиторий на 150+ пакетов и модулей - Typescript, Nx + Yarn workspaces, PVM + SemVer + Conventional Commits, Rollup, Gitlab CI
- view слой - React, CSS Modules, Tinkoff UI-kit
- серверная часть - Node.js и Fastify
- тестирование - Playwright, Jest, React Testing Library
- сборка приложения - Webpack, Module Federation, Babel / SWC, Postcss

Идеальный кандидат подходит под любое описание из списка:
- знает и любит React, изучал новую Suspense SSR архитектуру и React Server Components
- имеет широкий кругозор, понимает принципы работы других фреймворков, интересуется развитием таких мета-фреймворков как Next.js, Remix, Nuxt.js, SvelteKit, Astro, Qwik City
- разрабатывал и поддерживал Node.js приложения в продакшене, знает на какие метрики стоит обращать внимание, исправлял высокий event loop lag или исследовал утечки памяти
- имеет опыт работы в платформенной команде - поддержка продукта для других разработчиков, обратная совместимость и long-term миграции, написание технической документации
- хорошо знаком с любым популярным бандлером и транспайлером, писал под них плагины и обходил AST, не боится изучать исходных код инструментов и контрибьютить в них
- умеет профилировать и оптимизировать web приложения, знает как улучшить waterfall загрузки ресурсов страницы или медленные анимации, использовать такие инструменты как WebPageTest, Lighthouse и Performance Insights, знает про Web Vitals и Chrome UX Report, читает CPU Flamegraph как открытую книгу
- есть опыт организации JS библиотек - организации репозиториев, сборки, тестирования, линтера, полного релизного цикла, настройки CI/CD, работы с транзитивными зависимостями и дубликатами, понимание плюсов и минусов чистых ESM пакетов
- знаком с различными стратегиями деплоя серверных и SPA приложений, есть опыт работы с Docker и k8s
- интересуется архитектурными подходами, знаком с IoC и Dependency Injection в частности, Clean Architecture, Feature-Sliced Design
- вдохновлен развитием сетевых протоколов, знает как устроены DNS, TCP, HTTP и QUIC, пользовался такими инструментами как Wireshark
- знаком с мониторингом - собирает, рисует и использует метрики и логи, участвует в разборе инцидентов

Пишите ваши вопросы или присылайте резюме в личку!
🔥201😢1💩1
SuperOleg dev notes pinned «Привет! Ищу коллегу в нашу платформенную команду для разработки и поддержки React мета-фреймворка tramvai Мы - одна из команд в Tinkoff Coretech Frontend, которая отвечает за создание инструментов и общих практик для огромного фронтенд коммьюнити в Тинькофф.…»
Привет!

Получилось подвести некий итог возможных оптимизаций на стороне сервера в формате рекомендаций для tramvai приложений - https://tramvai.dev/docs/guides/server-optimization

Отличная рекомендация от Андрея Марченко и Александра Хороших в комментариях к предыдущему посту про DNS кэши - увеличить параметр ноды `--max_semi_space_size`!

Добавил график по количеству срабатываний GC, после увеличения до 64mb - minor очисток стало примерно в 10 раз меньше, но их продолжительность тоже примерно в 10 раз.
По метрикам, заметно улучшилось время ответа и пиковые значения лага эвент лупа на 99м перцентиле, на 95м перцентиле уже не так сильно но тоже видны изменения в лучшую сторону.
CPU троттлинг в пике стал ниже примерно на 2%.

В общем без сомнения пошло в дефолтные рекомендации.

По DNS кэшам. Оптимизация не дала такого заметного эффекта, как semi space size, но и никаких проблем с использованием до сих пор не увидел, сегодня буду проводить эксперимент с увеличением времени жизни кэша до 5 минут, а после до 10 минут.

Эффект заметен слабо з-за того что активно используется keepAlive, про это уже писал, по сути в рамках эксперимента виден яркий эффект на единственном запросе из тестируемого приложения, который без keepAlive.
Поэтому интересно увеличить время жизни кэша, но с текущим TTL в минуту, в приложении происходит очень мало вызовов системной функции getaddrinfo, поэтому больших ожиданий нет.

Итого, DNS кэш идет в дефолты, но при любых возможных проблемах будет не жалко отключить.

Дополнительно закину скриншоты графиков после увеличения max semi size.
🔥8🍾4
Раскатили параметр 3 июля в 16 часов
Привет!

Сейчас у Next.js проходит эпик по ускорению development сборки - очень уж сильно жалуются пользователи на перф при использовании новой app directory.

Периодически ищу что-то интересное в issues некста, так как тоже используем webpack и swc, и в этом квартале тоже работаем над ускорением сборки.

Пример постов, где можно найти какие-то интересные PR или ссылочки:
- https://github.com/vercel/next.js/issues/48748#issue-1680013792
- https://github.com/vercel/next.js/issues/49929#issuecomment-1637185156
- из последнего issue сразу же неплохая вводная статья про GC и утечки в Node.js - https://www.dynatrace.com/news/blog/understanding-garbage-collection-and-hunting-memory-leaks-in-node-js/

Всю эту историю я начал смотреть после профилирования `tramvai start` на одном из наших приложений (одно из самых больших, очень много модулей), где повторная сборка с прогретым webpack file-system cache занимала очень много времени (до минуты на m1, полторы-две минуты на intel). Вот парочкой наших проблем и решений, где малыми усилиями получили хорошие результаты, хочу поделиться.

Профилировка показала что примерно 60-70% времени занимает работа либы `watchpack`, которая отвечает в вебпаке за отслеживание изменений файлов и папок.

Оказалось, проблема специфичная для MacOS, где проблему вызывает закрытие огромного количества watchers. Кардинальное уменьшение решает проблему и при этом я не заметил никакого замедления реакции на изменение файлов.

Как уменьшить количество watchers - env переменная `WATCHPACK_WATCHER_LIMIT=20`

Issues по теме:
- https://github.com/webpack/watchpack/issues/222
- https://github.com/vercel/next.js/pull/51826

Второй кейс - потребление памяти.

На этом же приложении development сборки занимала минимум 12 GB оперативной памяти.
При этом для production сборки коллеги не увеличивали `max_old_space_size`, то есть проблема только во время работы dev сервера.

У трамвая изначально есть такая особенность, что в development режиме отключена опция splitChunks, которая отвечает за эффективное разделение кода, и в 5 вебпаке по дефолту уже давно используется отличная стратегия granular chunking.

Выключено как я понимаю по двум причинам:
- в теории может замедлять dev сборку на время работы плагина
- появляется куча рандомных чанков что усложняет тестирование и разработку
- так сделано в Next.js, главный референс при интеграции granular chunking стратегии разделения кода

У меня уже пару недель в голове крутилась мысль, что не используя splitChunks в приложениях с огромным количеством динамических импортов (обычно на каждый роут), мы дублируем все их общие зависимости в каждом чанке страницы.

Ок, включил splitChunks, поправил упавшие тесты, проверили на приложении:
- потребление памяти снизилось с 12 gb до менее чем 3 gb 🚀
- повторная сборка с прогретыми кэшами ускорилась в несколько раз (на m1 примерно с 45 секунд до 10) - оказалось что размер FS кэшей клиентской сборки тоже уменьшился с нескольких гигабайт до сотен мегабайт
- холодный старт ускорился незначительно

То есть теория полностью подтвердилась, оптимизация радикально уменьшает потребление памяти за счет удаления огромного количества дубликатов модулей.

Кстати пытаюсь в issues некста достучаться до мейнтейнеров, что бы аналогично попробовали включить splitChunks в dev режиме - https://github.com/vercel/next.js/issues/49929#issuecomment-1647549143

Вот такие небольшие истории успеха, надеюсь будет полезно для ваших development сборок.

А в следующем посте план рассказать про многочисленные факапы за последнее время)
🔥20👍1
Хороший кейс, что проблема не всегда в ${frameworkName} - https://github.com/vercel/next.js/issues/48748#issuecomment-1647630293
👍4🔥1
Привет!

Свежий инсайт по ускорению сборки.

`--max_semi_space_size=128` отлично работает и для CLI приложений на Node.js!

GC работает реже, в сумме тратит меньше времени, локально ускорило тестовую production сборку с 130 до 110 секунд, теперь интересно как на development повлияет.

Кстати, не знал что у Node.js есть апишка для генерации CPU profile, очень удобно, пробую сейчас решение из Next.js - https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/cpu-profile.ts

Из минусов, нет возможности перейти в исходники, интересно можно ли как-то сурсмапы подружить с этим трейсом..
👍61
Привет!

Заметка про Client Hints API

Переходили на это API на сервере, с фаллбэком на парсинг User-Agent через ua-parser-js, как было раньше.
На сервере это HTTP заголовок sec-ch-ua который легко разобрать.

Также недавно интегрировали на клиенте, там данные доступны в navigator.userAgentData.

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

Столкнулся с проблемой мокирования User-Agent и взаимодействия с этой апишкой в следующих кейсах:
- в Chrome Devtools при эмуляции мобильных устройств
- в Playwright при использовании кастомного User-Agent

Проблема следующая - navigator.userAgentData не синхронизируется с navigator.userAgent:
- для устройств с Chrome и Android, где поддержка Client Hints есть, пишет реальную версию текущего браузера, хотя в UA условно 80й хром
- для IOS + Safari - вместо undefined, как должно быть если апишка не поддержкивается, возвращает бесполезный объект с пустым массивом brands и пустой строкой в platform

Пример проблемы - https://github.com/microsoft/playwright/issues/14361

Такая вот не приятная особенность, усложняет и внедрение использования этой апишки в приложение с существующими тестами, и локальную проверку в браузере, решается только мокированием или использованием реального браузера.

Надеюсь будет полезно!
👍7
Привет!

Не так давно на Github была заблокирована организация Тинькофф, и к сожалению публичное зеркало репозитория tramvai со всеми настройками и звездочками кануло в лету.

С опозданием переехали в новую организацию - https://github.com/tramvaijs/tramvai

Публикация в npm снова работает, и свежая документация доступна на https://tramvai.dev/

Также, периодически поступали вопросы в личку по поводу фреймворка, решил что удобнее будет завести отдельный чат - https://t.me/tramvaijs

Приглашаю вас в чат, обсуждать любые вопросы про tramvai, и общие темы вокруг серверного рендеринга и мета-фреймворков.
😢17🤯9👍1😁1
Привет!

Недавно актуализировали список поддерживаемых браузеров на tinkoff.ru, и немного погрузился в экосистему вокруг, а именно в взаимосвязь следующих инструментов:

- browserslist
- Can I Use
- core-js
- @babel/preset-env

Если коротко, как это устроено:

core-js поставляет полифиллы для различных ECMAScript фич, и начиная с 3 версии поддерживает свою таблицу совместимости фич и браузеров - http://zloirock.github.io/core-js/compat/

@babel/preset-env добавляет нужные полифиллы из core-js при транспиляции кода, до версии 7.3 собирал данные по совместимости из https://kangax.github.io/compat-table/es6/, минусы описаны в “core-js@3, babel and a look into the future”, с 7.4 использует данные core-js.

browserslist позволяет шарить между всеми инструментами общий список браузеров, и генерировать его по различным условиям типа “> 1%” или “not dead”.

Также browserslist использует сервис Can I Use в качестве источника данных о названиях и версиях браузера, актуальный список можно посмотреть тут - https://caniuse.com/usage-table.

Есть кстати не оч подробная и свежая, но неплохая статья про preset-env - https://www.jnielson.com/demystifying-babel-preset-env

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

1. Данные Can I Use ограничены - не знаю как они собираются, но к примеру по популярным в РФ или в Китае браузерам может быть очень мало статистики, и они будут отсутствовать, либо у браузера в списке будет одна из свежих версий. Пример проблемы - https://github.com/babel/babel/issues/8545

2. Compat данные core-js ограничены - к примеру для поддержки браузера Samsung в core-js добавляли таблицу - маппинг версий Samsung на используемый в них движок Chromium. Yandex или UC браузеров там нет, причем достаточно давно https://github.com/zloirock/core-js/issues/721. Пример проблемы в preset-env - https://github.com/browserslist/browserslist/issues/290

Как нас (и возможно вас) задевают эти проблемы - мы используем browserslist для нескольких кейсов:
- поддержка необходимых браузеров, это сам @babel/preset-env и наш механизм генерации полифиллов
- проверка браузера на попадание в наш список современных браузеров, для таких отдадим modern сборку, в которой значительно меньше кода
- проверка на устаревший браузер, который ниже нашего дефолтного списка, для таких отдаем заглушку с просьбой обновить браузер

С modern сборкой все хорошо - если список, есть браузеры выше чем версии из списка, парсим User-Agent или Client Hints и отдаем подходящий код.

Но гарантировать поддержку браузеров мы получается просто не можем!

Допустим нам надо поддержать UC браузер от 11 версии и Samsung от 2 версии, потому что с них есть стабильный поток пользователей.

Но в свежих данных от https://caniuse.com/usage-table таких старый версий просто нет - то есть и подобрать соответствие ES фич не к чему. А в случае с UC браузером у нас и compat данных в core-js нет, для Samsung есть хотя бы маппинг к Chromium.

Также, проблема с проверкой на устаревший браузер. Приходит пользователь с UC браузера 11 версии, которая есть в нашем списке, но после обработки списка через browserslist, отдается минимально известный в Can I Use - 15 версии (я такого даже не нагуглил).

В итоге все пользователи с 11, 12 и 13 версий UC браузера получат редирект на заглушку, и мы об этом просто так не узнаем.

Этот механизм исправил принудительным добавлением минимальных версий из списка при проверке соответствия.

Вот такой дивный мир) Утешает только то, что с достаточно старыми версиями хрома и при наличии Android Browser в списках, preset-env добавит практически все популярные полифиллы.

Пишите в комментарии ваш опыт с этими инструментами, и обязательно поправьте если где-то ошибся.
👍112
Привет!

Не так давно писал про механизм lazy hydration - https://t.me/super_oleg_dev/102, основанный на хаке с dangerouslySetInnerHTML

Встретил интересный баг:
- если LazyRender обернут в Suspense
- и мы ловим ошибку This Suspense boundary received an update before it finished hydrating
- то React очищает HTML в LazyRender, который пришел от сервера

Думаю такая комбинация это большая редкость, но было интересно)

Возможно дело связано с переходом на клиентский рендер вместо гидрации, а хак с пустым HTML в dangerousltSetInnerHTML работает только для гидрации.

Upd.

Проблема в целом кажется при любых ошибках гидрации выше уровнем(
🤔2🔥1