# Понедельник 25 твитов
Привет из Воронежа. Меня зовут Андрей (@AndreyPechkurov) и на этой неделе мы поговорим про Node.js. Надесь, что тем… twitter.com/i/web/status/1…
7:27Сегодня мы поговорим про модуль async_hooks и, в частности, класс AsyncLocalStorage (ALS), который недавно в нем по… twitter.com/i/web/status/1…
8:31Начнем мы с простого примера использования ALS. Предположим, что мы хотим добавить id запроса во все сообщения журн… twitter.com/i/web/status/1…
8:31Но программисты народ ленивый (в хорошем смысле) и всегда пытаются уменьшить связность модулей и кол-во кода на сфе… twitter.com/i/web/status/1…
8:31Думаю те, кто работал с ThreadLocal в Java и AsyncLocal в .NET, увидели сходство. То, что было давно доступно в дру… twitter.com/i/web/status/1…
8:31Что же было в экосистеме до появления ALS? Это были разнообразные user-land модули, самыми известными из которых бы… twitter.com/i/web/status/1…
8:31Любопытно, что оригинальный CLS был основан на древнем process.addAsyncListener API, который доступен только в Node… twitter.com/i/web/status/1…
8:31Продолжим тему ALS. Не секрет, что у async_hooks есть "накладные расходы", связанные с трекингом ЖЦ объектов-ресурс… twitter.com/i/web/status/1…
8:59Например, в отличие от user-land библиотек, ALS не использует destroy хуков (благодаря функции executionAsyncResour… twitter.com/i/web/status/1…
8:59Особенно, "дорого" это в случае нативных промисов. Но в недавних коммитах это отслеживание было отключено для Async… twitter.com/i/web/status/1…
8:59Недавно я портировал cls-rtracer, маленькую библиотеку, основанную на CLS API, с cls-hooked на ALS и сделал замер п… twitter.com/i/web/status/1…
8:59Этот бенчмарк наглядно показывает снижение расходов на CLS. Эти расходы, конечно, в любом случае остаются ненулевым… twitter.com/i/web/status/1…
8:59Конечно, логи это далеко не единственный пример использования CLS API. Для начала, давайте поговорим про внутрянку… twitter.com/i/web/status/1…
9:25Эти агенты в первую очередь нужны для отслеживания производительности веб-приложений. А это значит, что им нужно от… twitter.com/i/web/status/1…
9:25Во-первых, они monkey patch'ат веб-фреймворки (включая, модуль http), драйверы БД и прочие популярные библиотеки, в… twitter.com/i/web/status/1…
9:25Поэтому, во-вторых, они используют CLS API в том или ином виде. Иногда он основан на диких monkey patch'ах core API… twitter.com/i/web/status/1…
9:25Впрочем, есть надежда, что некоторые из них вскоре переедут на ALS, а популярные библиотеки начнут вести себя приличнее.
9:25Если интересно посмотреть на исходный код одного из APM агентов, то вот пример:
github.com/googleapis/clo…
Кроме того, на async_hooks можно построить полезные диагностические инструменты. Такие как, например, bubbleprof ви… twitter.com/i/web/status/1…
9:25Наконец, async_hooks можно использовать для наблюдения за необработанными исключениями. Пример - модуль domain, кот… twitter.com/i/web/status/1…
9:25Давайте теперь поговорим про перспективы ALS в рамках всей JS экосистемы.
Последняя ветка про этот API на сегодня, обещаю. :)
9:41FE разработчики, пишущие на Angular, знакомы с Zone.js. Его API включает в себя концепцию CLS. Более того, реализац… twitter.com/i/web/status/1…
9:41В TC39 даже вносили на рассмотрение черновик стандарта, основанного на Zone.js. Но тема заглохла и его реализации мы уже вряд ли увидим.
9:41Но есть идея повторить попытку и внести в TC39 на рассмотрение более простой стандарт, вдохновленный ALS подмножест… twitter.com/i/web/status/1…
9:41Как обещал, завершаю эту тему. Если кому-то интересно поговорить про "внутрянку" async_hooks/ALS, спрашивайте. Пост… twitter.com/i/web/status/1…
9:41# Вторник 13 твитов
Сегодня мы пообщаемся на тему WeakRef + FinalizationRegistry. Эти API находятся на Stage 3 (Candidate) в TC39 и име… twitter.com/i/web/status/1…
8:35Слабые ссылки, они же WeakRef, просты, как песня, и многим хорошо знакомы по другим runtime. pic.twitter.com/dUlPcFmUfb
FinalizationRegistry немного сложнее и, как следует из названия, реализует механизм финализации объектов. Простейши… twitter.com/i/web/status/1…
8:35Эти API можно пощупать в Node.js v12+, указав флаг --harmony-weak-refs.
Но в core слабые ссылки уже давно использу… twitter.com/i/web/status/1…
8:35Есть и собственный утилитный класс WeakReference, который используется в domain.
github.com/nodejs/node/bl…
Про WeakMap и WeakSet, думаю, и говорить не нужно. Они используются в нескольких модулях.
Кстати, любопытно то, чт… twitter.com/i/web/status/1…
8:35WeakMap построен на механизме ephemerons, позволяющем GC корректно обрабатывать циклы. Его семантика такова: пока н… twitter.com/i/web/status/1…
8:35Думаю, многих интересуют хорошие примеры применений новых API.
Примеры использования слабых ссылок в core подсказы… twitter.com/i/web/status/1…
8:35Другой неплохой пример - очистка ресурсов WebAssembly при сборке JS объектов. Он хорошо расписан тут.
github.com/tc39/proposal-…
Другие примеры из этого документа я бы назвал хорошими с большой натяжкой. Кэш с WeakRef не дает вам гарантий по вр… twitter.com/i/web/status/1…
8:35Дело в том, что оба API, ожидаемо, не дают никаких гарантий по детерменистичности своей работы, поскольку они завяз… twitter.com/i/web/status/1…
8:35Поэтому точно не стоит освобождать системные ресурсы (скажем, fd в Linux) или соединения с БД через финализаторы. И… twitter.com/i/web/status/1…
8:35Если вы знаете еще какие-то хорошие use case'ы для этих экспериментальных API, пишите. Думаю, это будет очень полезно для сообщества.
8:35# Среда 15 твитов
Сегодня немного пройдемся по внутренней кухне Node.js и моим, связанным с ней (странным) экспериментам.
8:26Если вы работали с бинарными данными в Node.js или, например, пытались реализовать драйвер БД с бинарным протоколом… twitter.com/i/web/status/1…
8:26Любопытно то, что сами бинарные данные Buffer хранит off-heap, а в куче хранится только объект с метаданными. Соотв… twitter.com/i/web/status/1…
8:26Аллоцировать буфер можно многими способами и вот некоторые из них. pic.twitter.com/2RKdETLoY3
Думаю, что вы уже догадались, что аллокация буфера процесс относительно "дорогой" и задались вопросом, почему помим… twitter.com/i/web/status/1…
8:26Дело в том, что для небольших буферов в Node.js предусмотрена оптимизация. Системная библиотека сначала аллоцирует… twitter.com/i/web/status/1…
8:26Затем при вызове allocUnsafe() проверяется запрошенный размер и, если он достаточно мал, буфер создается как slice… twitter.com/i/web/status/1…
8:26Это существенно уменьшает накладные расходы для allocUnsafe(). А, соответственно, allocUnsafeSlow() лишен этой опти… twitter.com/i/web/status/1…
8:26Значение Buffer.poolSize по умолчанию это 8КБ, что совсем немного для современного серверного железа. Если его увел… twitter.com/i/web/status/1…
8:26Поэтому тут напрашивается идея использования "честного" пула, т.е. API, которое позволит переиспользовать уже ненуж… twitter.com/i/web/status/1…
8:26Лирическое отступление. В core есть класс FreeList, который реализует простейший пул. Он используется в модуле http… twitter.com/i/web/status/1…
8:26Но есть и альтернатива такому "ручному" подходу. Что если использовать финализаторы для возврата буферов в пул? Иде… twitter.com/i/web/status/1…
8:26Сказано - сделано. Исходники и результаты эксперимента можно посмотреть тут.
github.com/puzpuzpuz/nbuf…
P.S. Finaliza… twitter.com/i/web/status/1…
8:26Вкратце можно сказать следующее. Ожидаемо, экспериментальный пул оказался быстрее. Однако, из-за особенностей аллок… twitter.com/i/web/status/1…
8:26Надеюсь, что такое обсуждение внутрянки Node.js не показалось вам излишним. Завтра мы пройдемся по небольшой выборк… twitter.com/i/web/status/1…
8:26# Четверг 18 твитов
Как договаривались, сегодня пройдемся по экспериментам и инициативам, которые могут повлиять на core и экосистему.… twitter.com/i/web/status/1…
7:28Начнем мы с эксперимента по поддержке io_uring, свежего механизма для асинхронного ввода/вывода в ядре Linux, в lib… twitter.com/i/web/status/1…
7:39Если кто-то не слышал про libuv, то это кросс-платформенная библиотека для асинхронного I/O (и не только), на которой основывается Node.js.
7:39Сейчас libuv в Linux для сетевого I/O использует epoll, механизм мультиплексирования, позволяющий работать сразу со… twitter.com/i/web/status/1…
7:39Использование более эффективного механизма может дать прирост производительности нагруженных сетевых приложений.
В… twitter.com/i/web/status/1…
7:39Идем дальше. Недавно в V8 добавили очень классную фичу, а именно, pointer compression. Подробности в этой классной… twitter.com/i/web/status/1…
8:51Идея заключается в том, чтобы хранить ссылки на объекты в куче в 32 битах на 64-битных архитектурах, а затем получа… twitter.com/i/web/status/1…
8:51Логично, что это ведет к ощутимой экономии памяти. К тому же, как ни удивительно, эта фича еще и дала прирост производительности.
8:51Конечно, уже есть инициатива по интеграции pointer compression в Node.js. Там есть нюансы с нативными модулями, но… twitter.com/i/web/status/1…
8:51Вы могли слышать, что в Node.js 12.5.0 ускорили время старта за счет так называемых V8 snapshots. Подробности можно… twitter.com/i/web/status/1…
11:23Однако, интеграция была частичная и заметного сокращения времени старта это не дало. На данный момент ведутся работ… twitter.com/i/web/status/1…
11:23HTTP/3 еще находится в стадии черновика и активно идет работа по совершенствованию стандарта. И похоже, что Node.js… twitter.com/i/web/status/1…
12:56Об этом можно судить хотя бы по PR'ам, реализующим QUIC, транспортный протокол поверх UDP, поверх которого работает… twitter.com/i/web/status/1…
12:56Если вы хотите провести какой-то эксперимент с Node.js, то лучше сначала поискать в GH issues. Например, я как-то з… twitter.com/i/web/status/1…
13:20Если кто-то не знаком с jemalloc, то это аллокатор памяти, изначально реализованный для FreeBSD, а теперь активно п… twitter.com/i/web/status/1…
13:20В момент появления jemalloc хорошо выглядел в сравнении с libc аллокатором, но сейчас уже все не так однозначно. Чт… twitter.com/i/web/status/1…
13:20Небольшой офтопик для знакомых с Java. Дизайн jemalloc был использован для off-heap аллокатора в сетевом фреймворке… twitter.com/i/web/status/1…
13:20Предлагаю на сегодня на этом остановиться. Завтра мы пообщаемся на тему инструментов анализа производительности и д… twitter.com/i/web/status/1…
13:20# Пятница 18 твитов
Как договаривались, сегодня обсуждаем инструменты анализа производительности и диагностики Node.js приложений. Мой… twitter.com/i/web/status/1…
8:22Все знаю мантру "don't block the event loop (or libuv worker pool)", но в Node.js приложениях, как правило, куча за… twitter.com/i/web/status/1…
8:29Поэтому бывает полезно прогнать приложение с флагом --trace-sync-io. Он выводит в консоль предупреждение, если вызо… twitter.com/i/web/status/1…
8:29Так, например, можно узнать, что uuid/v4 из популярной библиотеки содержит синхронный вызов. Один из пользователей… twitter.com/i/web/status/1…
8:29В Node.js есть встроенный профилировщик (на самом деле, это V8 profiler). Запустить его можно через флаг --prof.
9:19Он сэмплирующий, т.е. он делает сэмплы стека вызовов с равномерным интервалом и пишет результат в отчет. Соответств… twitter.com/i/web/status/1…
9:19Классно то, что профилировщик корректно работает с нативными вызовами, а не только с JS кодом. При этом, к сожалени… twitter.com/i/web/status/1…
9:19Из человекочитаемого представления можно почерпнуть, какие вызовы выглядят подозрительно (они, как правило, будут г… twitter.com/i/web/status/1…
9:19На flame graph нужно смотреть на так называемые плато, т.е. вызовы, оказавшиеся во многих сэмплах. Но в реальных бе… twitter.com/i/web/status/1…
9:19Вообще, прежде чем браться за профилировщик, советую изолировать анализируемую часть функционала в бенчмарк и уже е… twitter.com/i/web/status/1…
9:19И разумеется, если у вас проблемы с памятью, то этот профилировщик вам не поможет.
9:19Что касается анализа потребления памяти и поиска mem leak'ов, то тут все банально. Локально можно пользоваться проф… twitter.com/i/web/status/1…
11:39В production можно делать heap dump, если уверены, что есть примерно x2 памяти для этого. Если хочется делать snaps… twitter.com/i/web/status/1…
11:39Еще сложно выявляемая одна проблема в приложениях, это unhandled promise rejection. Боль в том, что они делаю ваш… twitter.com/i/web/status/1…
11:48Лично я склоняюсь к тому, что fail fast поведение в ответ на unhandled promise rejection - это наиболее разумное ре… twitter.com/i/web/status/1…
11:48Такую логику несложно написать самому. Ну, или использовать готовый модуль навроде этого:
github.com/mcollina/make-…
И еще немного на тему диагностики production приложений. В Node.js есть классные диагностические отчеты, сбор котор… twitter.com/i/web/status/1…
12:36На этом на сегодня все. Ну, и как всегда, если есть чем поделиться, пишите, пожалуйста.
12:36# Суббота 5 твитов
Сегодня поговорим, как можно помочь Node.js, как open source проекту.
8:30Во-первых, можно улучшить тесты. Если видите, что в каком-то модуле нет 100% покрытия по веткам, это повод для улуч… twitter.com/i/web/status/1…
8:30Во-вторых, документация всегда требует внимания и совершенствования. Заметили неточность или недостаток информации,… twitter.com/i/web/status/1…
8:30В-третьих, можно взять одну из good first issues:
github.com/nodejs/node/is…
Наконец, если у вас есть идея по улучшению core APIs, советую сначала поискать в старых issue и PR'ах, а потом созд… twitter.com/i/web/status/1…
8:30# Воскресенье 2 твита
Неделя заканчивается и я предлагаю внести чуть больше интерактива в наше общение. Что вы хотели бы изменить или добавить в Node.js core?
7:06Неделя пролетела быстро. Надеюсь, вам общение показалось таким же интересным, как и мне. Всем пока!
17:32# Ссылки
github.com
- https://github.com/googleapis/cloud-trace-nodejs
- https://github.com/nodejs/node/blob/9085c03806dbc9eb48e14c2afa49080deee0ee3c/lib/domain.js#L194
- https://github.com/tc39/proposal-weakrefs#exposing-webassembly-memory-to-javascript
- https://github.com/puzpuzpuz/nbufpool/tree/experiment/fg-api-based-pool
- https://github.com/mcollina/make-promises-safe
- https://github.com/nodejs/node/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22
- https://github.com/nodejs/node/issues/19393