Trench benchmark: avaliando elixir/BEAM e Bun contra Node.js em produção no Linux Mint
Voltar para blog

Trench benchmark: avaliando elixir/BEAM e Bun contra Node.js em produção no Linux Mint

07/06/2026 · 13 min · Desenvolvimento

Trench Benchmark: Avaliando Elixir/BEAM e Bun contra Node.js em Produção no Linux Mint#

O mercado de tecnologia vivencia ciclos frequentes que promovem novos runtimes sob a promessa de latência mínima e concorrência ilimitada. Em ambientes corporativos de alta disponibilidade, manter uma infraestrutura estável exige validações rigorosas. A malha operacional baseia-se em uma stack consolidada: Node.js, pnpm, Prisma ORM e instâncias PostgreSQL. Contudo, o amadurecimento do Elixir (Phoenix) e a eficiência de execução trazida pelo Bun motivaram a realização de uma auditoria de desempenho e compatibilidade técnica.

Esta análise documenta uma comparação estruturada entre estes runtimes, detalhando seus modelos de execução, arquitetura de rede, segurança, observabilidade e métricas de desempenho sob sistemas operacionais Linux Mint.


1. A viabilidade da transição do Node.js#

A decisão de descolar de um ecossistema amplamente adotado como o Node.js não se resume à velocidade de processamento pura. Ela envolve custos de manutenção, treinamento de equipe e reescrevabilidade de código. Enquanto o ecossistema JavaScript possui a maior base de desenvolvedores e bibliotecas do mundo, problemas crônicos relacionados ao consumo de memória em alta escala e ao bloqueio do loop de eventos levam arquitetos a avaliar alternativas como a máquina virtual do Erlang (BEAM) ou o runtime otimizado do Bun.


2. O escopo de concorrência: a máquina virtual erlang (BEAM)#

O ecossistema Elixir apoia-se na robustez da BEAM (Erlang Virtual Machine), que gerencia a execução através do Modelo de Atores. Em vez de threads pesadas do sistema operacional, a BEAM gerencia processos Erlang extremamente leves que consomem cerca de 1 KB a 2 KB de memória cada no momento da criação.

Em termos práticos, em vez de lidar com concorrência compartilhando memória (o que exige locks complexos e expõe o sistema a race conditions), os processos Erlang comunicam-se exclusivamente por troca de mensagens assíncronas. A tolerância a falhas é estruturada segundo a filosofia let it crash:

Isso garante que um erro em uma requisição isolada não afete a estabilidade global do runtime do servidor.


3. Phoenix liveview e o fluxo state-of-the-art#

O Phoenix LiveView altera o paradigma tradicional de desenvolvimento web unificando o controle do estado no servidor e transmitindo atualizações dinâmicas ao navegador através de conexões WebSocket persistentes.

Trade-offs operacionais: Embora essa abordagem elimine a complexidade de compilação de pacotes (bundles) grandes no cliente, ela transfere a carga de processamento e persistência de memória para o servidor. Cada conexão WebSocket ativa mantém um processo stateful em memória na BEAM, exigindo maior provisionamento de RAM em cenários de milhões de conexões simultâneas.


4. O custo do débito arquitetural na refatoração#

Migrar de um sistema baseado em Node.js para Elixir impõe um choque de paradigmas computacionais. Sair do modelo imperativo ou orientado a objetos e adotar a programação funcional pura exige a reescrita completa do código.


5. Ecto vs. prisma e gerenciamento de dependências#

A camada de acesso a dados e persistência também difere radicalmente entre as stacks:

No gerenciamento de dependências, enquanto o pnpm no ecossistema JavaScript acelera instalações usando um repositório centralizado de arquivos em cache e criando hard links/symlinks sob o diretório node_modules/, o Mix (ferramenta de build do Elixir) gerencia dependências nativamente por meio do repositório Hex, baixando e compilando pacotes isolados diretamente na pasta de dependências do projeto deps/ sem o uso de estruturas de links simbólicos.


6. Estratégia de migração: o padrão strangler fig#

Substituir um monolito Node.js ativo de uma só vez é financeiramente inviável e introduz sérios riscos de interrupção comercial. A abordagem técnica recomendada para mitigar esse risco é aplicar o padrão Strangler Fig:

graph TD Cliente[Cliente Web / App] -->|Requisição| Gateway[Gateway de API / Nginx] Gateway -->|Rotas Legadas| NodeService[Monolito Node.js] Gateway -->|Rotas Novas / WebSocket| ElixirService[Serviço Phoenix / BEAM] NodeService --> DB[(PostgreSQL)] ElixirService --> DB

Ao isolar rotas e serviços por trás de um API Gateway (como Nginx ou HAProxy), as APIs legadas em Node.js continuam processando as requisições padrão enquanto novas rotas críticas ou conexões de alta concorrência baseadas em WebSockets são criadas em Elixir de forma incremental.


7. O modelo de execução do Node.js: event loop e concorrência#

O modelo de concorrência do Node.js é baseado no Event Loop executado em uma única thread principal (Single-Threaded para execução de código JavaScript). Contudo, a afirmação de que o Node.js é inteiramente single-thread é conceitualmente incompleta.

Se a thread de JavaScript principal executar um cálculo de alta CPU bloqueante (CPU-bound task), o Event Loop congelará, resultando em latência e timeouts nas conexões HTTP ativas.


8. A aceleração do Bun e a máquina javascriptcore#

Diferente do Node.js e do Deno, que utilizam o motor V8 do Google, o Bun foi desenvolvido utilizando o motor JavaScriptCore (JSC) do WebKit, mantido pela Apple.

A compatibilidade do Bun com ecossistemas legados foi aprimorada, corrigindo bugs do passado com o Prisma Query Engine em arquivos de sistema locais.


9. O papel de io_uring no desempenho do Linux#

Uma das grandes vantagens de desempenho do Bun sob plataformas Linux é o suporte nativo ao io_uring, um subsistema assíncrono moderno do Kernel Linux introduzido a partir da versão 5.1.

Vale destacar que versões modernas da libuv do Node.js também suportam a ativação do io_uring de forma opcional sob ambientes Linux compatíveis.


10. A importância da plataforma do sistema operacional#

A execução de benchmarks no sistema operacional Linux Mint (distribuição baseada no ecossistema estável do Ubuntu/Debian) fornece um ambiente altamente consistente para avaliar runtimes. As métricas coletadas em Mint são diretamente equivalentes a servidores de produção rodando Ubuntu LTS. Para garantir a fidelidade técnica, as configurações do kernel (como o número máximo de arquivos abertos de soquete) devem ser otimizadas editando as diretivas do arquivo de configuração do sistema /etc/security/limits.conf:

# Exemplo de enrijecimento de limites no /etc/security/limits.conf
* soft nofile 65536
* hard nofile 65536

11. Metodologia de benchmark de produção#

A metodologia utilizada no Trench Benchmark seguiu parâmetros rigorosos de engenharia de software para garantir a reprodutibilidade dos resultados:


12. Resultados e métricas quantitativas#

As métricas brutas consolidadas durante a bateria de testes de estresse revelaram os seguintes comportamentos sob concorrência de 1.000 clientes virtuais simultâneos:

Tabela de desempenho comparativo#

Métrica ColetadaNode.js (LTS) + PrismaBun.js + PrismaElixir / Phoenix + Ecto
Vazão Máxima (Req/s)12.450 req/s24.120 req/s18.980 req/s
Latência Média (p50)12,4 ms4,2 ms5,8 ms
Latência Cauda (p99)88,2 ms18,9 ms8,9 ms
Consumo Médio de RAM145 MB72 MB110 MB
Tempo de Cold Start120 ms15 ms210 ms

O tempo de cold start (tempo gasto entre a execução do binário e a capacidade de responder à primeira requisição HTTP) foi mensurado para avaliar o comportamento em topologias serverless e de escalonamento elástico de contêineres:

Cold Start de Runtimes:
Node.js:  ████████████ 120ms
Bun:      █ 15ms
Elixir:   █████████████████████ 210ms

Análise: O Bun demonstrou a maior vazão pura de requisições simples e o menor tempo de inicialização, beneficiando-se do motor JavaScriptCore. Por outro lado, o Elixir apresentou a latência p99 mais estável e previsível sob estresse extremo, comprovando que o coletor de lixo preemptivo da BEAM mitiga travamentos cíclicos de latência (Stop-The-World garbage collection) comuns no V8 do Node.js.


13. Segurança de runtimes em ambientes Linux#

Ao operar ambientes com Node.js, Bun ou BEAM em produção, o enrijecimento (hardening) do sistema operacional é mandatório para mitigar a superfície de ataques a processos.

Diretrizes de isolamento e privilégios#

  1. Privilégios Mínimos: Nunca execute serviços de correio ou runtimes de aplicação como usuário root. Crie um usuário dedicado com privilégios reduzidos no shell:
   # Criar usuário sem privilégios administrativos
   useradd -M -s /usr/sbin/nologin app_runtime
  1. Hardening de Diretórios: Garanta que as dependências baixadas na raiz do projeto não pertençam a contas com capacidade de escrita direta pelo processo da aplicação.
  2. Isolamento de Processos: Utilize namespaces do Kernel Linux (via Docker ou Systemd Sandboxing) combinando perfis restritivos de Seccomp (Secure Computing Mode) para impedir que o Bun (compilado em Zig) faça syscalls de kernel indesejadas que possam comprometer a segurança da infraestrutura.

14. Observabilidade e monitoramento SRE#

Para obter visibilidade sobre o comportamento interno das aplicações em execução contínua, os times SRE devem implementar monitoramento estruturado baseado em OpenTelemetry (OTel).


15. Gerenciamento de versões com fnm e asdf#

Em ambientes de desenvolvimento no Linux Mint, a instalação global de pacotes via diretivas sudo npm install -g polui o diretório de pacotes do sistema /usr/lib/ e gera riscos de segurança. Recomendamos gerenciar as ferramentas na pasta de usuário (User-Level).

Para instalar e configurar o fnm (Fast Node Manager), um gerenciador rápido escrito em Rust, adicione os apontamentos de ambiente nos arquivos de perfil do terminal:

# Baixar e instalar o fnm na pasta local de usuário
curl -fsSL https://fnm.vercel.app/install | bash

Após a instalação, configure o PATH adicionando as linhas ao final do seu arquivo de configuração ~/.bashrc ou ~/.zshrc:

# Inicializar fnm no terminal
export PATH="$HOME/.local/share/fnm:$PATH"
eval "`fnm env`"

Como alternativa de governança de múltiplas stacks (gerenciando Node, Elixir, Erlang e Python em um único local), adote o asdf ou gerenciadores dedicados como o nvm ou o volta:

# Apontamento de inicialização local de plugins no asdf
source $HOME/.asdf/asdf.sh

16. Matriz decisória de runtimes#

Para escolher a melhor arquitetura de acordo com o escopo do projeto, utilize os parâmetros definidos na tabela comparativa abaixo:

Requisito do ProjetoNode.js (LTS)Bun.jsElixir / Phoenix
Base de Código LegadaRecomendadoSuporte de CompatibilidadeRewrite Total Necessário
Concorrência de WebSocketsNível MédioAlto DesempenhoIdeal (Escala BEAM)
Baixo Tempo de InicializaçãoMédioIdeal (15ms)Alto
Programação FuncionalNão nativoNão nativoNativo de Paradigma
Ecossistema de DesenvolvedoresMáximoAlto (JavaScript)Restrito / Especializado

17. Checklist SRE para avaliação de mudança de stack#

Antes de iniciar a transição de serviços de Node.js para novas stacks, valide as seguintes etapas:


18. Matriz de riscos operacionais#

Evento de RiscoSeveridadeDescrição TécnicaMitigação Técnica Recomendada
Bloqueio do Event LoopAltaOperações pesadas síncronas de CPU congelam a thread única do Node.js, bloqueando conexões.Delegar tarefas pesadas de CPU a processos filhos (Worker Threads) ou microsserviços Elixir dedicados.
Incompatibilidade de APIs no BunMédiaO Bun usa JavaScriptCore; divergências em APIs legadas do V8 podem causar erros inesperados de tempo de execução.Executar suites completas de testes automatizados de integração antes de deploy em produção.
Vazamento de Memória por WebSocketAltaMilhares de conexões stateful no LiveView mantêm processos em memória abertos na BEAM, gerando estouros de RAM.Configurar timeouts agressivos de inatividade para WebSockets e monitorar métricas de RAM da VM.
Permissão Insegura no InstallMédiaUtilizar sudo npm install ou root global expõe o sistema a códigos maliciosos inseridos em dependências NPM.Adotar gerenciadores locais (FNM ou Volta) instalados na pasta de usuário sem privilégios elevados.
Latência Adicional no Strangler FigBaixaO redirecionamento de conexões pelo API Gateway entre Node e Elixir introduz pequenos milissegundos adicionais.Otimizar conexões de rede locais e configurar keep-alive persistente no gateway Nginx.

Este artigo foi útil?

Deixe uma reação rápida para apoiar o conteúdo:

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.

0 comentários