AndreyPechkurov

15 июня 2020, Voronezh

# Понедельник 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…

9:25

Кроме того, на 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:41

FE разработчики, пишущие на 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

8:35

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…

8:35

Про WeakMap и WeakSet, думаю, и говорить не нужно. Они используются в нескольких модулях.

Кстати, любопытно то, чт… twitter.com/i/web/status/1…

8:35

WeakMap построен на механизме 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-…

8:35

Другие примеры из этого документа я бы назвал хорошими с большой натяжкой. Кэш с 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

8:26

Думаю, что вы уже догадались, что аллокация буфера процесс относительно "дорогой" и задались вопросом, почему помим… 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:23

HTTP/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-…

11:48

И еще немного на тему диагностики 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…

8:30

Наконец, если у вас есть идея по улучшению core APIs, советую сначала поискать в старых issue и PR'ах, а потом созд… twitter.com/i/web/status/1…

8:30

# Воскресенье 2 твита

Неделя заканчивается и я предлагаю внести чуть больше интерактива в наше общение. Что вы хотели бы изменить или добавить в Node.js core?

7:06

Неделя пролетела быстро. Надеюсь, вам общение показалось таким же интересным, как и мне. Всем пока!

17:32

github.com