От създаването на сървърните среди за JavaScript, NodeJS царува като най-популярната среда за изпълнение. Node, заедно със своя мениджър на пакети, NPM, сега се използва широко за проекти, вариращи от малки странични проекти за любители до системи с голям трафик от корпоративен клас. Повечето разработчици не са се осмелили да предизвикат Node поради неговия статус по подразбиране в индустрията и цялостната екосистема на разработка с отворен код. Deno беше първият основен претендент, но така и не успя да се наложи, вероятно поради липсата на силна оперативна съвместимост между Node, NPM пакети с отворен код и CommonJS.

Преди няколко дни, 8 септември 2023 г., нов претендент, Bun, официално обяви стабилна версия на тяхното време за изпълнение и съпътстващи инструменти, включително NPM-съвместим мениджър на пакети, пакет, API и др. „Bun обещава цял набор от предимства“, най-вече около производителността и опита на разработчиците, като същевременно се гордее със силни стандарти за оперативна съвместимост. С други думи, Bun изглежда като незаменим заместител на приложения, базирани на Node, който подобрява всичко относно изживяването при разработването на JS приложения и тяхната производителност.

Като човек, който работи основно върху приложения, базирани на функции без сървър, незабавният ми въпрос беше: всичко това звучи страхотно, но мога ли всъщност да използвам това? По-специално се чудех това, тъй като Lambda оптимизира услугата си за NodeJS, така че как би било честно Bun, когато няма официална поддръжка?За да разбера, създадох набор от показатели, за да го тествам с цел да отговоря на моя въпрос.

Тестовете

Изпълнението на общи сравнителни тестове има ограничения за сравняване между времена на изпълнение и езици и следователно не може да замести тестването на вашите конкретни случаи на употреба. Въпреки това се опитах да се съсредоточа върху няколко ключови области, които смятах, че биха представлявали интерес за разработчиците в контекста на изпълнението на JavaScript в функционална среда без сървър. Измислих следните три бенчмарк теста, които разглеждат теоретични, реални и специфични за сървъра аспекти:

Обща производителност на обработка

Bun твърди, че може да обработва логика със скорост 3–4 пъти по-висока от NodeJS. Въпреки че много приложения може да са по-обвързани с I/O (заявки към база данни, мрежови повиквания, четене/запис на файлова система и т.н.), този тип общо подобрение, ако е точно, помага на всяка система и особено на тези с обвързана с процесора и паметта обвързани натоварвания. Като такъв исках да видя дали претенциите на Bun за чиста производителност ще се преведат в среда без сървър.

Тест: Генерирайте и след това сортирайте (с вградения Array.sort) 100K произволни числа 10 пъти едно след друго.

CRUD API

Тестването на тежки работни натоварвания на процесора е интересно, но е вероятно да раздуе ползите от Bun в реалния свят, тъй като реалността е, че подобните на Lambda среди много често се използват за много по-прости приложения, които са по-обвързани с I/O (мрежа, файлова система и т.н.). Много често срещан модел е използването на API Gateway, Lambda и DynamoDB заедно за създаване на прости CRUD API, където изчисленията са прости. Исках да тествам тези по-реални сценарии, за да видя дали Bun все още може да осигури полза дори при липса на тежка обвързана с процесора логика.

Тест: Внедрете функция за актуализиране на CRUD, която валидира входа, извлича обект от DynamoDB, плитко обединява исканите модификации в съществуващия обект и го връща обратно в DynamoDB.

Време за студен старт

Функциите без сървър са склонни да страдат от това, което е известно като проблем със „студения старт“. Функциите на AWS Lambda работят в контейнеризирана среда, като всеки контейнер обикновено трае между 10–30 минути. Когато постъпи заявка и няма контейнер, който е завъртян и наличен, Lambda ще завърти нов контейнер, за да обработи заявката, и това време за завъртане е времето за студен старт, за което имаме предвид. Официално поддържаните среди за изпълнение като NodeJS обикновено ще бъдат по-оптимизирани, за да се развият бързо, така че основният въпрос, на който трябва да се отговори, е как ще се сравни Bun, неоптимизирана среда за изпълнение?

Тест: Функция Hello world с умишлено предизвикани студени стартове.

Конфигурация на средата

За тези тестове е използвана следната конфигурация:

  • Функция REST API Gateway-to-Lambda (времената за реакция, измерени от API Gateway, за да получите пълен обхват от край до край)
  • 1024MB памет за ламбда функции
  • x86_64 архитектура на Amazon Linux 2 за Bun
  • Базова конфигурация по време на изпълнение на x86_64 архитектура за Node.js 18.x
  • Осигурена паралелност от 5 (с изключение на теста при студен старт)

Обща обработка на резултатите от теста

Този тест беше проведен 1000 пъти за всяко време на изпълнение, като данните за времето за реакция бяха събрани от страната на сървъра и събрани в CloudWatch и X-Ray.

Възел

  • Средно време за реакция: 3736ms
  • Минимално време за реакция: 3391ms
  • Максимално време за реакция: 4580ms
  • p95 Време за реакция: 3989ms
  • p99 Време за реакция: 4404ms

кок

  • Средно време за реакция: 1836ms (-50,9%)
  • Минимално време за реакция: 1564ms (-53,9%)
  • Максимално време за реакция: 3571ms (-22,0%)
  • p95 Време за реакция: 2027ms (-49,2%)
  • p99 Време за реакция: 2117ms (-51,9%)

Резултати от теста на CRUD API

Този тест беше проведен 1000 пъти за всяко време на изпълнение, като данните за времето за реакция бяха събрани от страната на сървъра и събрани в CloudWatch и X-Ray.

Възел

  • Средно време за реакция: 24ms
  • Минимално време за реакция: 17ms
  • Максимално време за реакция: 135ms
  • p95 Време за реакция: 29ms
  • p99 Време за реакция: 51ms

кок

  • Средно време за реакция: 25ms (+4,2%)
  • Минимално време за реакция: 16ms (-5,9%))
  • Максимално време за реакция: 157ms (+16,3%))
  • p95 Време за реакция: 33ms (+13,8%)
  • p99 Време за реакция: 44ms (-13,7%)

Време за студен старт

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

Възел

  • Средно време за реакция: 302ms
  • Минимално време за реакция: 252ms
  • Максимално време за реакция: 361ms
  • p95 Време за реакция: 312ms
  • p99 Време за реакция: 361ms

кок

  • Средно време за реакция: 775ms (+156%)
  • Минимално време за реакция: 717ms (+184%)
  • Максимално време за реакция: 1382ms (+282%)
  • p95 Време за реакция: 1174ms (+276%)
  • p99 Време за реакция: 1382ms (+282%)

Тълкуване на резултатите

За моя изненада, въпреки че Node е специално оптимизиран за Lambda и официалният слой Bun Lambda е недостатъчно тестван (и следователно не е оптимизиран), Bun успя да излезе напред по измерими и значими начини за задачи, свързани с процесора. Освен това беше безпроблемно за по-прости и I/O-обвързани задачи, което означава, че е малко вероятно да видите понижаване на производителността от използването на Bun. Мисля, че ако AWS Lambda добави Bun като официална среда за изпълнение, ще видим, че Bun се представя поне малко по-добре от Node, ако не и със значителни подобрения. И ако управлявате логиката си с рамки и библиотеки като Express, Nest, Apollo или NextJS, няма да се изненадам, ако видите постоянно по-бърза производителност (това е нещо, което бих искал да тествам след това).

Готов ли е за производство? Е, все още не бих стигнал до това заключение, защото се натъкнах на някои груби петна по пътя с Bun Lambda Layer и той не е особено тестван в битки на този етап. Освен това все още има някои NodeJS API, които все още не са оперативно съвместими с Bun, а самият Bun все още е много нов, което означава, че все още не знаем всички странности с него. Въпреки това това са разрешими проблеми и тези тестове показват, че с известно усилие има реален потенциал това да бъде легитимно време за изпълнение за разработка на производствени приложения без сървър.

Борба с проблеми със стартиране на студен двигател

Очевидно проблемът със студеното стартиране е нещо, за което трябва да сте наясно, но не бих го нарекъл прекъсвач на сделката, тъй като има две популярни решения за превръщането на този проблем в почти остарял:

  1. Официалното решение на AWS за осигурена едновременност, което поддържа конфигуриран брой контейнери винаги топъл срещу заплащане. В зависимост от натоварването на вашето приложение, това може или да е много разумно, или да е по-скъп като подход, отколкото си струва.
  2. Персонализирано решение за затопляне, което пингва вашата Lambda функция от време на време, за да гарантира, че определен брой контейнери са винаги налични. Това е добро решение за приложения с по-ниска производителност, тъй като е евтино и поддържа контейнера готов за употреба, когато имате нужда от него.

Предимства на производителността

В обичайните случаи на използване на приложения, просто замяната на NodeJS за Bun може да доведе до леко подобрение на латентността, като придаде на вашите приложения по-бързо усещане. Колкото повече прави вашето приложение в самото изчисление, толкова по-голяма полза ще видите, така че наистина ще зависи от конкретните ви случаи на употреба. Това каза, съчетано със стратегия за студен старт, изглежда, че Bun поне ще бъде наравно с Node, като същевременно предлага всичките си други предимства при разработката.

Разходни ползи

Намаляването на времето за изпълнение на функциите Lambda има пряко положително въздействие върху разходите за изпълнение на вашето приложение, тъй като функциите Lambda се таксуват на милисекунда използване. Ако правите много свързана с процесора логика във вашето приложение, можете да видите огромни спестявания на разходи, като преминете към Bun, но ако просто изпълнявате някои прости функции, които извикват бази данни или други API и не правят много с тези данни в самата функция Lambda, тогава вероятно няма да видите огромни разлики, освен това ще трябва да платите за каквато и стратегия за студен старт да изберете.

Едно друго предупреждение, което трябва да имате предвид, е, че използването на вграденото време за изпълнение на Node означава, че не се таксуват за разходи за инициализация (студен старт). От друга страна, вие сте таксувани за инициализация на ламбда контейнери за Bun, така че в допълнение към борбата с проблемите при студен старт от съображения за производителност, вероятно бихте искали да ги намалите и с оглед на ползите от разходите.

Последни мисли

Бях силно скептичен относно Bun като цяло и особено по отношение на потенциала за използването му в работен процес на приложение без сървър. Бях приятно изненадан, че този скептицизъм беше премахнат от тези резултати. Въпреки че все още няма да използвам Bun за разработка на Lambda поради неговата новост, ще го следя отблизо, докато екосистемата му узрява и инструментите за Bun on Lambda се заздравяват и оптимизират допълнително.

Като странична бележка, тази статия не обсъжда опита от разработката на Bun, но tl;dr е, че това беше освежаващо изживяване с изключително бърза инсталация на пакети и време за компилация и ефективна собствена поддръжка за тестване на Lambda функции локално - нещо, което иначе е по-болезнено и изисква допълнителни инструменти от AWS SAM. С по-голяма зрялост смятам, че Bun има реален шанс да изпревари Node като среда за изпълнение по подразбиране в индустрията за разработка на JavaScript и TypeScript и аз лично ще го подкрепям. Също така, AWS Lambda, ако случайно прочетете тази статия, моля, помислете дали да не поддържате Bun като естествена среда за изпълнение на Lambda.