Стоимость абстракций P1

Всю неделю был сконцентрирован на решении проблемы утекания памяти в node.js скрипте. Немного контекста:

У нас есть нагрузочный скрипт написанный на K6. И мы хотим протестировать функционал, который работает как с сокетом, так и запросами. Одним K6 тут не обойтись, поскольку работа с сокетом в нем очень ограничена, и как раз наш случай не покрывается самим K6. Сейчас мы делаем так: 1. K6 отправляет запрос на создание сделки пользователем. В системе создается позиция(или несколько позиций) в статусе "открытая". 2. Эти открытые позии мы и можем слушать по сокету в node.js скрипте. Но на один запрос может же быть открыто несколько позиций. Следовательно надо еще и следить за тем, чтобы закрыть все позиции. 3. Закрываем позиции(не забываем, что их может быть несколько), которые превышают определенный лимит в node.js скрипте (выбрали тут значение открытых позиций равным 5, поскольку в реальных сценариях их не больше 7 на одну сделку), так как мы уже подключены к сокету и знаем id этих позиции.

В данном подходе есть несколько проблем: 1. Сокет должен быть подключен всегда и для всех пользователей, потому что открытые позиции создаются для всех пользователей существующих в окружении. 2. Большие данные (10к+ пользователей) дают большую нагрузку как на инфраструктуру, так и на сам node.js скрипт - в связи с чем мы и дозируем нагрузку на скрипт, но сам gitlabCI дает гарантировано 2Гб ОЗУ. А далее как повезет - сама задача в gitlabCI может попросить довыделить память, но не факт, что это будет сделано. 3. В случае, если gitlabCI выделит нам мало памяти, скрипт node.js упадет с out of memory. 4. Использование сторонних библиотек для подключению к сокету и отправки запросов вносит определенные удобства читаемости кода, но не его производительности. 5. Использование ts-node для запуска TS фаила.

Итого, после определенных замеров памяти и игр с ночным запуском и танцев с бубном пришли к выводу, что нужно смотреть сам скрипт и оптимизировать его. Но для начала узнали, что node.js перед выполнением JS скрипта преобразовывает код в байткод (инструкции очень похожие на ASM, только более читаемые). Кстати команда trace-gc также будет вашим другом во время отладки инспектирования памяти. Подробно про trace-gc и как с ним работать описано в блоге самой node.js. А про print-bytecode -- здесь, здесь и здесь

Сняли байткод с помощью команды node --trace-gc --print-bytecode script.js > script.bytecode.log. На выходе получили 825к строк (с учетом компиляции в JS).

Решили отказаться от всех прослоек и сделать все нативными средствами: - axios -> fetch - signalR -> нативный websocket - логгер(надстройка на пакетом debug) -> console.log - TS фаил -> mjs фаил - nodejs 18(websocket не доступен в 18 версии) -> node.js 22 (решили сразу перепрыгнуть через LTS версию до последней)

В результате снятые замеры после оптимизации получились следующими: - print-bytecode - 35к строк против вышеупомянутых 825к - память стала выделяться заметно меньше (счет на мегабайты, а не гигабайты)

В следующей части я расскажу, какие практики оптимизации кода мы использовали.

🙏🙏🙏🙏

#js #javascript #nodejs #k6 #load_testing #load #performance @haradkou_sdet

Стоимость абстракций P1
Всю неделю был сконцентрирован на решении проблемы утекания памяти в node.js скрипте. 
Немного контекста:
У нас есть нагрузочный скрипт написанный на K6 | Сетка — социальная сеть от hh.ru