<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Vinicios "ViniDev" Coelho</title>
    <description>The latest articles on Forem by Vinicios "ViniDev" Coelho (@vinidev).</description>
    <link>https://forem.com/vinidev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1192393%2F8f5f726e-c979-48ca-9c65-06fe26b70151.jpeg</url>
      <title>Forem: Vinicios "ViniDev" Coelho</title>
      <link>https://forem.com/vinidev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/vinidev"/>
    <language>en</language>
    <item>
      <title>Você sabe de fato como SSR funciona — e o que ele resolve?</title>
      <dc:creator>Vinicios "ViniDev" Coelho</dc:creator>
      <pubDate>Tue, 27 Jan 2026 11:35:43 +0000</pubDate>
      <link>https://forem.com/vinidev/voce-sabe-de-fato-como-ssr-funciona-e-o-que-ele-resolve-den</link>
      <guid>https://forem.com/vinidev/voce-sabe-de-fato-como-ssr-funciona-e-o-que-ele-resolve-den</guid>
      <description>&lt;p&gt;Server Side Rendering virou quase um “default” em muitas discussões sobre frontend moderno. Em algum momento, todo projeto grande passa pela mesma frase:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“A gente precisa de SSR por causa de SEO e performance.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;O problema é que, na prática, essa decisão costuma ser tomada sem que todos entendam &lt;strong&gt;o que realmente muda quando você adota SSR&lt;/strong&gt; — e, principalmente, &lt;strong&gt;o que você passa a pagar por isso&lt;/strong&gt;, em complexidade, custo e manutenção.&lt;/p&gt;

&lt;p&gt;Este artigo não é uma defesa do SSR, nem um ataque. A proposta aqui é mais simples e mais difícil ao mesmo tempo: &lt;strong&gt;entender SSR como decisão arquitetural&lt;/strong&gt;, não como feature de framework.&lt;/p&gt;




&lt;h2&gt;
  
  
  O que realmente acontece quando você usa SSR
&lt;/h2&gt;

&lt;p&gt;Antes de falar de vantagens ou desvantagens, precisamos alinhar uma coisa importante:&lt;br&gt;&lt;br&gt;
SSR &lt;strong&gt;não elimina JavaScript&lt;/strong&gt;, &lt;strong&gt;não elimina o browser&lt;/strong&gt; e &lt;strong&gt;não elimina o client-side rendering&lt;/strong&gt;. Ele apenas muda &lt;em&gt;quando&lt;/em&gt; a renderização acontece.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client Side Rendering (CSR), na prática
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc9htkwyzzuu7n6d2ov5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc9htkwyzzuu7n6d2ov5.png" alt="csr imagem" width="795" height="646"&gt;&lt;/a&gt;&lt;br&gt;
Em um SPA clássico:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O navegador solicita a página
&lt;/li&gt;
&lt;li&gt;Recebe um HTML mínimo (basicamente uma &lt;code&gt;&amp;lt;div id="root"&amp;gt;&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;Baixa o bundle JavaScript
&lt;/li&gt;
&lt;li&gt;Executa o código
&lt;/li&gt;
&lt;li&gt;Faz chamadas de API
&lt;/li&gt;
&lt;li&gt;Renderiza a UI
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do ponto de vista do usuário (e do bot), &lt;strong&gt;o conteúdo não existe até o JavaScript rodar&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Esse modelo é simples, escalável e barato de servir, mas cria dois problemas bem conhecidos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conteúdo não aparece imediatamente&lt;/li&gt;
&lt;li&gt;Bots e crawlers têm dificuldade para indexar corretamente&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Server Side Rendering (SSR), sem abstrações
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpfodiz9ngi8ivq73mdc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpfodiz9ngi8ivq73mdc2.png" alt="ssr image" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quando você usa SSR, o fluxo muda:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O navegador faz a requisição
&lt;/li&gt;
&lt;li&gt;O servidor:

&lt;ul&gt;
&lt;li&gt;executa o código do frontend
&lt;/li&gt;
&lt;li&gt;resolve dependências
&lt;/li&gt;
&lt;li&gt;busca dados
&lt;/li&gt;
&lt;li&gt;monta o HTML final
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Envia HTML já renderizado
&lt;/li&gt;
&lt;li&gt;O navegador exibe o conteúdo imediatamente
&lt;/li&gt;
&lt;li&gt;O JavaScript é carregado e ocorre a &lt;em&gt;hydration&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O ponto crítico aqui é este:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;o servidor agora executa código que antes só rodava no browser&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Isso muda completamente o perfil do sistema.&lt;/p&gt;




&lt;h2&gt;
  
  
  O que o SSR resolve de verdade (em cenários reais)
&lt;/h2&gt;

&lt;p&gt;Vamos falar apenas do que SSR &lt;strong&gt;de fato resolve&lt;/strong&gt;, sem promessas exageradas.&lt;/p&gt;

&lt;h3&gt;
  
  
  SEO: o problema real, não o genérico
&lt;/h3&gt;

&lt;p&gt;SSR resolve SEO &lt;strong&gt;quando o problema é a ausência de HTML indexável&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Isso acontece em cenários muito específicos, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blogs e portais de conteúdo
&lt;/li&gt;
&lt;li&gt;Marketplaces
&lt;/li&gt;
&lt;li&gt;Landing pages de tráfego pago
&lt;/li&gt;
&lt;li&gt;Páginas públicas de produto ou serviço
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nesses casos, bots de busca:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recebem HTML completo
&lt;/li&gt;
&lt;li&gt;Leem conteúdo sem executar JS
&lt;/li&gt;
&lt;li&gt;Conseguem indexar corretamente títulos, descrições e headings
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso não é teoria. O próprio Google documenta que, embora consiga executar JavaScript, &lt;strong&gt;o processamento é atrasado e menos confiável&lt;/strong&gt;, especialmente para SPAs complexas.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SSR não melhora SEO porque “é melhor”,&lt;br&gt;&lt;br&gt;
melhora porque &lt;strong&gt;remove a dependência de execução de JavaScript para indexação&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Performance percebida (e por que isso importa mais que benchmarks)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4t8avjym2ykk9zrhzphg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4t8avjym2ykk9zrhzphg.png" alt="performance image" width="611" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Um erro comum em discussões sobre SSR é focar apenas em métricas técnicas isoladas.&lt;/p&gt;

&lt;p&gt;Na prática, o que SSR melhora é:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;First Contentful Paint (FCP)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Largest Contentful Paint (LCP)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ou seja: &lt;strong&gt;o momento em que o usuário vê algo útil na tela&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Em projetos reais isso aparece assim:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O cliente diz que “o site parece lento”
&lt;/li&gt;
&lt;li&gt;Mesmo quando o tempo total de carregamento não é alto
&lt;/li&gt;
&lt;li&gt;Porque a tela fica vazia por muito tempo
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SSR antecipa o HTML e melhora a percepção.&lt;br&gt;&lt;br&gt;
Isso é especialmente relevante em:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conexões móveis
&lt;/li&gt;
&lt;li&gt;Dispositivos mais fracos
&lt;/li&gt;
&lt;li&gt;Países com infraestrutura de rede instável
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mas é importante deixar claro:&lt;br&gt;&lt;br&gt;
SSR &lt;strong&gt;não elimina o custo de hidratação&lt;/strong&gt;, nem reduz automaticamente o tempo total de execução do JavaScript.&lt;/p&gt;




&lt;h3&gt;
  
  
  Previews sociais e metadados dinâmicos
&lt;/h3&gt;

&lt;p&gt;Outro problema extremamente comum em SPAs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preview errado no WhatsApp
&lt;/li&gt;
&lt;li&gt;LinkedIn puxando título genérico
&lt;/li&gt;
&lt;li&gt;Facebook sem imagem
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso acontece porque crawlers de redes sociais &lt;strong&gt;não executam JavaScript&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;SSR resolve esse problema de forma direta:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OG tags vêm prontas
&lt;/li&gt;
&lt;li&gt;Metadados corretos por rota
&lt;/li&gt;
&lt;li&gt;Preview consistente
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esse é um ganho real, simples e mensurável.&lt;/p&gt;




&lt;h2&gt;
  
  
  Onde o SSR começa a cobrar a conta
&lt;/h2&gt;

&lt;p&gt;Até aqui, o SSR parece ótimo.&lt;br&gt;&lt;br&gt;
O problema é que &lt;strong&gt;ele muda a natureza do sistema&lt;/strong&gt; — e isso tem consequências.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custo de infraestrutura (o impacto invisível)
&lt;/h3&gt;

&lt;p&gt;Antes do SSR:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML estático
&lt;/li&gt;
&lt;li&gt;CDN agressivo
&lt;/li&gt;
&lt;li&gt;Escala quase infinita a baixo custo
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depois do SSR:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cada request executa código
&lt;/li&gt;
&lt;li&gt;Node (ou runtime equivalente) precisa estar sempre disponível
&lt;/li&gt;
&lt;li&gt;Escalar custa dinheiro
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Em ambientes de alto tráfego, isso se traduz em:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mais instâncias
&lt;/li&gt;
&lt;li&gt;Mais memória
&lt;/li&gt;
&lt;li&gt;Mais cold starts (em ambientes serverless)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SSR não é “mais rápido” por definição.&lt;br&gt;&lt;br&gt;
Ele é &lt;strong&gt;mais caro de servir&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Cache deixa de ser trivial
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubbnuibq95ai7p4in3lk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubbnuibq95ai7p4in3lk.jpg" alt="cache image" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cache em SSR raramente é simples.&lt;/p&gt;

&lt;p&gt;Em projetos reais, páginas dependem de:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usuário autenticado
&lt;/li&gt;
&lt;li&gt;Localização
&lt;/li&gt;
&lt;li&gt;Feature flags
&lt;/li&gt;
&lt;li&gt;A/B tests
&lt;/li&gt;
&lt;li&gt;Permissões
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cache por URL deixa de funcionar.&lt;br&gt;&lt;br&gt;
Cache por usuário explode a cardinalidade.&lt;/p&gt;

&lt;p&gt;O resultado costuma ser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cache inconsistente
&lt;/li&gt;
&lt;li&gt;Bugs difíceis de reproduzir
&lt;/li&gt;
&lt;li&gt;Comportamentos diferentes entre usuários
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Ambientes diferentes, bugs diferentes
&lt;/h3&gt;

&lt;p&gt;Quando você executa frontend no servidor, surgem problemas clássicos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;window&lt;/code&gt; não existe
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;document&lt;/code&gt; não existe
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;localStorage&lt;/code&gt; não existe
&lt;/li&gt;
&lt;li&gt;APIs do browser simplesmente não estão lá
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso gera:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Condicionais espalhadas pelo código
&lt;/li&gt;
&lt;li&gt;Lógicas duplicadas
&lt;/li&gt;
&lt;li&gt;Bugs que só aparecem em produção
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Debug de SSR é, por natureza, mais complexo.&lt;/p&gt;




&lt;h3&gt;
  
  
  Hydration mismatch: o problema silencioso
&lt;/h3&gt;

&lt;p&gt;Um dos problemas mais traiçoeiros do SSR.&lt;/p&gt;

&lt;p&gt;Se o HTML gerado no servidor &lt;strong&gt;não for exatamente igual&lt;/strong&gt; ao que o client espera:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Warnings aparecem
&lt;/li&gt;
&lt;li&gt;Componentes quebram
&lt;/li&gt;
&lt;li&gt;Estado fica inconsistente
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso costuma acontecer por:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Datas e timezones
&lt;/li&gt;
&lt;li&gt;Valores aleatórios
&lt;/li&gt;
&lt;li&gt;Dependência de ambiente
&lt;/li&gt;
&lt;li&gt;Dados que mudam entre server e client
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;É um tipo de bug que não aparece em ambientes simples, mas surge com força em aplicações reais.&lt;/p&gt;




&lt;h2&gt;
  
  
  SSR não é tudo ou nada: maturidade arquitetural
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fub8ps7r3fujc2d1c2i01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fub8ps7r3fujc2d1c2i01.png" alt="architecture image" width="800" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Um erro comum em times experientes — e não apenas iniciantes — é tratar SSR como uma decisão binária:&lt;br&gt;&lt;br&gt;
ou a aplicação é SSR, ou não é.&lt;/p&gt;

&lt;p&gt;Na prática, &lt;strong&gt;os sistemas mais bem-sucedidos usam SSR de forma seletiva&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSR por tipo de página, não por projeto
&lt;/h3&gt;

&lt;p&gt;Em cenários reais de mercado, o padrão mais comum é:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SSR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Home pública
&lt;/li&gt;
&lt;li&gt;Landing pages
&lt;/li&gt;
&lt;li&gt;Páginas de produto
&lt;/li&gt;
&lt;li&gt;Conteúdo indexável
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;CSR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboards
&lt;/li&gt;
&lt;li&gt;Áreas autenticadas
&lt;/li&gt;
&lt;li&gt;Fluxos internos
&lt;/li&gt;
&lt;li&gt;Telas altamente interativas
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Isso não é acaso.&lt;br&gt;&lt;br&gt;
São páginas com &lt;strong&gt;objetivos diferentes&lt;/strong&gt;, usuários diferentes e restrições diferentes.&lt;/p&gt;




&lt;h3&gt;
  
  
  Static Generation, revalidação e estratégias híbridas
&lt;/h3&gt;

&lt;p&gt;Grande parte dos problemas atribuídos ao “CSR puro” na verdade são resolvidos com &lt;strong&gt;geração estática bem feita&lt;/strong&gt;, não necessariamente com SSR por request.&lt;/p&gt;

&lt;p&gt;Na prática, muitos projetos poderiam:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gerar HTML no build
&lt;/li&gt;
&lt;li&gt;Revalidar sob demanda
&lt;/li&gt;
&lt;li&gt;Atualizar conteúdo periodicamente
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso reduz drasticamente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custo de infraestrutura
&lt;/li&gt;
&lt;li&gt;Complexidade de cache
&lt;/li&gt;
&lt;li&gt;Superfície de bugs
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SSR por request deve ser reservado para quando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O conteúdo realmente depende do request
&lt;/li&gt;
&lt;li&gt;O dado não pode ser pré-gerado
&lt;/li&gt;
&lt;li&gt;O ganho justifica o custo
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Como decidir se SSR faz sentido no seu projeto
&lt;/h2&gt;

&lt;p&gt;Antes de escolher SSR, perguntas como estas precisam ser respondidas com clareza:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Essa página precisa ser indexada por buscadores?&lt;/li&gt;
&lt;li&gt;O conteúdo muda a cada request ou poderia ser gerado antes?&lt;/li&gt;
&lt;li&gt;O ganho de FCP/LCP justifica o custo de execução no servidor?&lt;/li&gt;
&lt;li&gt;O time está preparado para lidar com cache complexo?&lt;/li&gt;
&lt;li&gt;O orçamento de infraestrutura comporta SSR em escala?&lt;/li&gt;
&lt;li&gt;Esse código realmente precisa rodar no servidor?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se a maioria dessas respostas for “não”, SSR provavelmente está sendo usado como solução genérica para um problema específico.&lt;/p&gt;




&lt;h2&gt;
  
  
  SSR como decisão de engenharia, não como tendência
&lt;/h2&gt;

&lt;p&gt;SSR não é um avanço natural sobre CSR.&lt;br&gt;&lt;br&gt;
Ele é uma &lt;strong&gt;troca consciente&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Você troca:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simplicidade → por controle
&lt;/li&gt;
&lt;li&gt;Custo baixo → por performance percebida
&lt;/li&gt;
&lt;li&gt;Arquitetura simples → por flexibilidade
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Em times maduros, SSR não é adotado porque “o framework recomenda”, mas porque:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Existe um problema claro
&lt;/li&gt;
&lt;li&gt;O custo é conhecido
&lt;/li&gt;
&lt;li&gt;O time sabe o que está comprando
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Entender SSR não é saber configurá-lo em um framework moderno.&lt;br&gt;&lt;br&gt;
É entender &lt;strong&gt;quando ele resolve um problema real — e quando apenas adiciona complexidade&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Os melhores sistemas não são aqueles que usam SSR em tudo, mas aqueles que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sabem onde usar
&lt;/li&gt;
&lt;li&gt;Sabem onde evitar
&lt;/li&gt;
&lt;li&gt;Sabem justificar a escolha
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;SSR não é uma melhoria automática.&lt;br&gt;&lt;br&gt;
É uma decisão arquitetural que cobra juros.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Referências e leituras recomendadas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Google Search Central — JavaScript SEO Basics
&lt;/li&gt;
&lt;li&gt;W3C — Rendering on the Web
&lt;/li&gt;
&lt;li&gt;Martin Fowler — Patterns of Enterprise Application Architecture
&lt;/li&gt;
&lt;li&gt;Vercel — Rendering Patterns
&lt;/li&gt;
&lt;li&gt;Web.dev — Rendering on the Web
&lt;/li&gt;
&lt;li&gt;Addy Osmani — The Cost of JavaScript
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>performance</category>
    </item>
    <item>
      <title>Concorrência, paralelismo e IA em produção: como reduzir a latência de endpoints de 40s para 8s</title>
      <dc:creator>Vinicios "ViniDev" Coelho</dc:creator>
      <pubDate>Wed, 03 Dec 2025 23:05:33 +0000</pubDate>
      <link>https://forem.com/vinidev/concorrencia-paralelismo-e-ia-em-producao-como-reduzir-a-latencia-de-endpoints-de-40s-para-8s-2ge2</link>
      <guid>https://forem.com/vinidev/concorrencia-paralelismo-e-ia-em-producao-como-reduzir-a-latencia-de-endpoints-de-40s-para-8s-2ge2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: Se você está integrando IA (LLMs, etc.) no backend e fazendo várias chamadas externas de forma sequencial, está jogando tempo fora. Ao aplicar noções básicas de concorrência e paralelismo (Promise.all, limitação de concorrência, separação entre ingestão externa e leitura paginada), é perfeitamente plausível reduzir um endpoint de ~40s para ~8s sem trocar de stack — só usando fundamentos de computação.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Concorrência, paralelismo e IA em produção: como reduzir a latência de endpoints de 40s para 8s
&lt;/h2&gt;

&lt;p&gt;Nos últimos anos, ficou relativamente fácil “plugar” IA generativa em qualquer backend: basta chamar uma API, mandar o texto e receber a resposta. O problema é que, em produção, esse “basta chamar” rapidamente vira &lt;em&gt;40 segundos de espera&lt;/em&gt; num endpoint que deveria responder em poucos segundos.&lt;/p&gt;

&lt;p&gt;O que separa um endpoint lento e frágil de um endpoint robusto e rápido, especialmente quando envolve IA externa, não é “misticismo de prompt engineering”. É &lt;strong&gt;fundamento de computação&lt;/strong&gt;: processos, threads, concorrência, paralelismo, latência e throughput — conceitos que aparecem em livros clássicos de Sistemas Operacionais e Programação Concorrente, como Tanenbaum, Herlihy &amp;amp; Shavit, Goetz, Herb Sutter, etc.&lt;/p&gt;

&lt;p&gt;Este artigo cria um cenário hipotético (não ligado a nenhum domínio específico) para mostrar, passo a passo, como aplicar esses conceitos em um backend Node.js/TypeScript que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;faz múltiplas chamadas a &lt;strong&gt;APIs externas de IA&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;consulta &lt;strong&gt;serviços legados internos&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;e reduz a latência de um endpoint de ~40s para ~8s, &lt;strong&gt;sem mudar de linguagem&lt;/strong&gt;, apenas usando melhor concorrência, paralelismo e arquitetura.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A ideia é que você consiga reaproveitar os princípios em qualquer projeto com IA.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Cenário hipotético: agregador de relatórios inteligentes
&lt;/h2&gt;

&lt;p&gt;Imagine um SaaS chamado &lt;strong&gt;ReportX&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Um cliente chama o endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/v1/reports/user/:userId?limit=5&amp;amp;page=1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse endpoint precisa:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buscar os últimos &lt;strong&gt;N&lt;/strong&gt; registros de atividade do usuário em sistemas internos (banco próprio, serviço legado, etc.).&lt;/li&gt;
&lt;li&gt;Para cada registro, &lt;strong&gt;chamar uma API de IA externa (LLM)&lt;/strong&gt; para gerar um resumo inteligente.&lt;/li&gt;
&lt;li&gt;Agregar tudo num JSON de resposta paginado.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Versão ingênua:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;buscar registros internos de forma sequencial;&lt;/li&gt;
&lt;li&gt;para cada registro, chamar a IA sequencialmente;&lt;/li&gt;
&lt;li&gt;só então responder.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se cada chamada de IA leva &lt;strong&gt;~2s&lt;/strong&gt;, e você faz 5–10 chamadas em sequência, qualquer coisa que envolva IO adicional (DB, HTTP interno, etc.) empurra o tempo total facilmente para &lt;strong&gt;30–40s&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Nosso objetivo: manter a mesma lógica de negócio, mas reduzir drasticamente a latência usando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;concorrência controlada (várias chamadas IO em paralelo);&lt;/li&gt;
&lt;li&gt;paralelismo real quando necessário (trabalho CPU-bound, se houver);&lt;/li&gt;
&lt;li&gt;e algumas estratégias arquiteturais simples.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. &lt;strong&gt;Concorrência vs paralelismo&lt;/strong&gt; (e por que isso importa para IA)
&lt;/h2&gt;

&lt;p&gt;Uma forma clássica de separar os conceitos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Concorrência:&lt;/strong&gt; lidar com muitas coisas acontecendo ao mesmo tempo (estrutura e coordenação de tarefas).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paralelismo:&lt;/strong&gt; executar várias coisas literalmente em paralelo (ao mesmo tempo em múltiplos núcleos).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Em aplicações web típicas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;chamadas de banco, HTTP, IA externa → são tarefas I/O-bound ⇒ se beneficiam de concorrência (não bloquear enquanto esperam resposta);&lt;/li&gt;
&lt;li&gt;operações intensivas de CPU (compressão, criptografia, parsing pesado, análise de grandes estruturas) → são CPU-bound ⇒ podem se beneficiar de paralelismo (worker threads, outros processos).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Herb Sutter cunhou o famoso “The free lunch is over” para explicar que simplesmente esperar por CPUs mais rápidas já não resolve; aproveitamento de múltiplos núcleos e modelos de concorrência tornam-se obrigatórios.&lt;/p&gt;

&lt;p&gt;Em um endpoint que fala com IA:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;o gargalo principal costuma ser IO externo (latência de rede até o provedor de IA);&lt;/li&gt;
&lt;li&gt;portanto, concorrência bem usada (várias chamadas em paralelo, com limites) quase sempre gera ganhos gigantes de performance.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Versão ingênua: loop sequencial com IA externa
&lt;/h2&gt;

&lt;p&gt;Comecemos com uma implementação simplificada (em TypeScript/Node) do endpoint do ReportX:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// services/aiClient.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;summarizeWithAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Chamada genérica a um provedor de IA (LLM)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.ia-externa.com/v1/summarize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IA_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;awesome-llm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`IA API error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// services/activityService.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;findUserActivities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Imagine um SELECT simples no banco interno&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;activities&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;created_at&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;desc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;activities&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;* as count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E o endpoint (versão “ruim”):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// controllers/reportController.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;summarizeWithAI&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../services/aiClient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;findUserActivities&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../services/activityService&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserReportsHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;findUserActivities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activity&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;summarizeWithAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;raw_text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- SEQUENCIAL&lt;/span&gt;
    &lt;span class="nx"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;activityId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;totalPages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;findUserActivities demora ~200–400ms;&lt;/li&gt;
&lt;li&gt;cada summarizeWithAI leva ~2s;&lt;/li&gt;
&lt;li&gt;e você traz 5 atividades por página…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Você tem: ~2s × 5 + overhead ≈ 10–12s.&lt;br&gt;&lt;br&gt;
Se subir para 10 atividades, facilmente chega perto de 20–25s. Com mais chamadas internas, outra API, ou IA mais lenta, esse “budget” de tempo explode.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Primeiro passo: concorrer chamadas de IA com Promise.all
&lt;/h2&gt;

&lt;p&gt;A primeira melhoria óbvia é fazer as chamadas de IA em paralelo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// controllers/reportController.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserReportsHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;findUserActivities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Dispara todas as chamadas ao mesmo tempo&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;summaries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;summarizeWithAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;raw_text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;activityId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;summaries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;totalPages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se cada chamada de IA ainda leva ~2s, mas você faz 5 em paralelo, o tempo total passa a ser ~2–3s para todas (mais o tempo de DB). Em projetos reais, é comum ver endpoints caindo de ~40s para ~8s apenas com esse tipo de abordagem — desde que a API externa tolere essa concorrência e seu backend esteja configurado para isso.&lt;/p&gt;

&lt;p&gt;Mas não é só sair aumentando concorrência.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Controlando a concorrência: limitar “fan-out” para não derrubar nada
&lt;/h2&gt;

&lt;p&gt;Se você simplesmente fizer Promise.all em 50 itens, pode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;estourar limite de conexões HTTP;&lt;/li&gt;
&lt;li&gt;violar rate limits da API de IA;&lt;/li&gt;
&lt;li&gt;ou saturar algum recurso interno.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por isso, um padrão muito útil é um “limiter” de concorrência: em vez de disparar todas as promessas de uma vez, você impõe um máximo de tarefas rodando simultaneamente.&lt;/p&gt;

&lt;p&gt;Um exemplo simples de “pool” genérico:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// utils/runWithConcurrency.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runWithConcurrency&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="nx"&gt;concurrency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;workers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runWorker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;workerCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;concurrency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;workerCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;workers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;runWorker&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;workers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aplicando no endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;runWithConcurrency&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../utils/runWithConcurrency&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserReportsHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;concurrency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// por exemplo&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;findUserActivities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runWithConcurrency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;concurrency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;summarizeWithAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;raw_text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;activityId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Se a ordenação for importante, você pode ordenar depois por createdAt ou id&lt;/span&gt;
  &lt;span class="nx"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;totalPages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora você:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ganha concorrência (várias chamadas acontecendo ao mesmo tempo);&lt;/li&gt;
&lt;li&gt;mas mantém controle (no máximo concurrency chamadas de IA em paralelo).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esse é o padrão que, na prática, costuma trazer ganhos do tipo “40 segundos → ~8 segundos” de forma relativamente simples.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Separando ingestão externa de leitura paginada
&lt;/h2&gt;

&lt;p&gt;Outro ponto que ajuda muito (e que quase sempre aparece em sistemas com IA + integrações externas):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ingestão/sincronização de dados externos,&lt;/li&gt;
&lt;li&gt;Consulta/leitura desses dados via endpoints com paginação.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Em vez de chamar o sistema externo toda vez que você muda de página, é mais saudável:&lt;/p&gt;

&lt;p&gt;Ter um serviço de importação que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;acessa o sistema externo,&lt;/li&gt;
&lt;li&gt;normaliza e salva no seu banco,&lt;/li&gt;
&lt;li&gt;aplica concorrência e paralelismo onde fizer sentido,&lt;/li&gt;
&lt;li&gt;garante idempotência (reprocessar o mesmo input deixa o sistema no mesmo estado, por exemplo, sem duplicar dados).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ter um endpoint paginado “limpo”, que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;só lê do seu banco local,&lt;/li&gt;
&lt;li&gt;ordena e pagina normalmente,&lt;/li&gt;
&lt;li&gt;não fala mais com o sistema externo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essa separação reduz a latência percebida pelo usuário e facilita muito o raciocínio sobre performance e cache.&lt;/p&gt;

&lt;p&gt;Em código, ficaria algo como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// services/externalIngestService.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExternalIngestService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;externalApiClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExternalApiClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;syncUserData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;externalApiClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchFullHistory&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Concorrência controlada para normalizar e salvar:&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runWithConcurrency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;externalItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fingerprint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;computeFingerprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;externalItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alreadyExists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsByFingerprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alreadyExists&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normalizeExternalItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;externalItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveNormalized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// ... computeFingerprint, existsByFingerprint, normalizeExternalItem, saveNormalized&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E o endpoint paginado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// controllers/reportController.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserReportsHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 1) Tenta sincronizar com o sistema externo (mas não falha o endpoint se der erro)&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;externalIngestService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;syncUserData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;External sync failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 2) Lê somente do seu banco (já sincronizado)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;reportsRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByUserPaginated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;totalPages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O ganho aqui não é só de latência, mas de arquitetura:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;você consegue melhorar paralelismo/concorrência na ingestão sem tocar nos endpoints de leitura;&lt;/li&gt;
&lt;li&gt;pode mover a ingestão para jobs assíncronos, filas, cron, etc.;&lt;/li&gt;
&lt;li&gt;e o endpoint em si fica muito mais previsível.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. Onde entra paralelismo “de verdade”?
&lt;/h2&gt;

&lt;p&gt;Até agora, tudo era concorrência I/O-bound em um processo único (Node usando event loop e non-blocking IO).&lt;/p&gt;

&lt;p&gt;Mas em cenários de IA você pode ter situações CPU-bound:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pós-processamento pesado de respostas (análises estatísticas, embeddings locais, etc.);&lt;/li&gt;
&lt;li&gt;parsing de arquivos grandes;&lt;/li&gt;
&lt;li&gt;compressão, criptografia, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nesses casos, colocar tudo em Promise.all não ajuda, porque o gargalo é CPU. A solução é usar paralelismo real:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Worker Threads em Node;&lt;/li&gt;
&lt;li&gt;múltiplos processos (cluster, containers);&lt;/li&gt;
&lt;li&gt;ou até serviços separados.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A literatura de programação concorrente fala bastante sobre estruturas de dados lock-free, algoritmos para multiprocessadores e modelos de memória, especialmente em livros como &lt;strong&gt;The Art of Multiprocessor Programming&lt;/strong&gt; (Herlihy &amp;amp; Shavit) e &lt;strong&gt;Java Concurrency in Practice&lt;/strong&gt; (Goetz et al.).&lt;br&gt;&lt;br&gt;
Mesmo que você esteja em Node, entender esses conceitos ajuda a decidir quando vale a pena paralelizar CPU e quando só precisa de concorrência I/O.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Medindo: mais importante do que “achar” que ficou rápido
&lt;/h2&gt;

&lt;p&gt;Antes e depois de qualquer refatoração de concorrência/paralelismo, meça:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;latência média e p95/p99 do endpoint (por página, por tipo de usuário, etc.);&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;distribuição de tempo de:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;consulta ao banco;&lt;/li&gt;
&lt;li&gt;chamadas de IA;&lt;/li&gt;
&lt;li&gt;outros serviços externos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ferramentas simples que já ajudam muito:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;logs com startTime/endTime por etapa;&lt;/li&gt;
&lt;li&gt;métricas em Prometheus / Grafana;&lt;/li&gt;
&lt;li&gt;tracing distribuído (OpenTelemetry) quando seu sistema começa a crescer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Só com métricas você consegue dizer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“estava ~40 segundos em média, agora está ~8 segundos no p95 para páginas de 5 itens”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sem isso, é fácil cair em autoengano: paralelizar um trecho que não era gargalo ou, pior, aumentar a concorrência de forma que piore tudo (mais context switching, mais carga em APIs externas, mais erros intermitentes).&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Checklist prático para endpoints com IA
&lt;/h2&gt;

&lt;p&gt;Se você está construindo ou refatorando um endpoint que chama IA externa, aqui vai um checklist resumido:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identifique o tipo de carga&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A lógica é majoritariamente I/O-bound (HTTP, DB, IA)?&lt;br&gt;&lt;br&gt;
→ foque em concorrência (Promises, pools, limiters).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Possui trechos CPU-bound?&lt;br&gt;&lt;br&gt;
→ considere worker threads ou processos separados.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Evite loops sequenciais com IO lento&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Troque loops &lt;code&gt;for&lt;/code&gt; com &lt;code&gt;await&lt;/code&gt; sequencial por &lt;code&gt;Promise.all&lt;/code&gt; ou por um pool de concorrência.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Limite a concorrência&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use um parâmetro de &lt;code&gt;concurrency&lt;/code&gt; configurável (por ambiente).&lt;/li&gt;
&lt;li&gt;Respeite rate limits e capacidades do provedor de IA.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Separe ingestão externa de leitura&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tenha serviços de ingestão/sync que materializam dados no seu banco.&lt;/li&gt;
&lt;li&gt;Deixe os endpoints de leitura o mais “puros” possível, apenas paginando e ordenando.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use idempotência ao integrar sistemas externos&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Idempotência aqui = chamar a mesma operação com o mesmo input N vezes e o sistema &lt;strong&gt;permanecer no mesmo estado&lt;/strong&gt; depois da primeira execução.&lt;/li&gt;
&lt;li&gt;Crie uma forma de fingerprint (hash) dos dados externos por entidade/usuário.&lt;/li&gt;
&lt;li&gt;Antes de inserir, verifique se aquele fingerprint já existe.

&lt;ul&gt;
&lt;li&gt;Assim, reprocessar o mesmo lote de dados não altera mais nada (efeito prático: você evita duplicidade de registros).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Meça sempre&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logue tempos por etapa e por página.&lt;/li&gt;
&lt;li&gt;Monitore p95/p99, não só a média.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Estude fundamentos&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conceitos de processos, threads, sincronização, deadlocks, starvation (livros de SO).&lt;/li&gt;
&lt;li&gt;Estruturas de dados e algoritmos concorrentes (Herlihy &amp;amp; Shavit).&lt;/li&gt;
&lt;li&gt;Impacto da “revolução da concorrência” no software moderno (Herb Sutter).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  10. Conclusão
&lt;/h2&gt;

&lt;p&gt;A onda atual de IA deixou muita gente com a impressão de que o grande diferencial está apenas em “saber falar com modelos”. Na prática, quando você vai para produção, os velhos fundamentos de computação voltam com força total:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;entender o modelo de concorrência da linguagem/plataforma (Node, JVM, etc.);&lt;/li&gt;
&lt;li&gt;saber a diferença entre tarefas I/O-bound e CPU-bound;&lt;/li&gt;
&lt;li&gt;estruturar pipelines com fan-out/fan-in, limites de concorrência, ingestão assíncrona e leitura paginada.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No nosso cenário hipotético, sair de um loop sequencial para um desenho concorrente e bem limitado reduziu latência na casa de 40s para ≈8s — sem trocar de stack, sem usar mágica, apenas aplicando princípios de Sistemas Operacionais e Programação Concorrente que já existem há décadas.&lt;/p&gt;

&lt;p&gt;IA muda o que você pode fazer com os dados.&lt;br&gt;&lt;br&gt;
Concorrência, paralelismo e arquitetura ainda determinam se isso vai rodar bem em produção.&lt;/p&gt;




&lt;h2&gt;
  
  
  Referências recomendadas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Andrew S. Tanenbaum, Herbert Bos. &lt;strong&gt;Modern Operating Systems&lt;/strong&gt;. 4ª ed.&lt;/li&gt;
&lt;li&gt;Maurice Herlihy, Nir Shavit. &lt;strong&gt;The Art of Multiprocessor Programming&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Brian Goetz et al. &lt;strong&gt;Java Concurrency in Practice&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Herb Sutter. “&lt;strong&gt;The Free Lunch Is Over: A Fundamental Turn Toward Concurrency in Software&lt;/strong&gt;”. Dr. Dobb’s Journal, 2005.&lt;/li&gt;
&lt;li&gt;Martin Kleppmann. &lt;strong&gt;Designing Data-Intensive Applications&lt;/strong&gt; (capítulos sobre sistemas distribuídos, concorrência e tolerância a falhas).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>node</category>
      <category>typescript</category>
      <category>backend</category>
      <category>ai</category>
    </item>
    <item>
      <title>Como teoria da computação escalou uma aplicação com uma infra custo beneficio.</title>
      <dc:creator>Vinicios "ViniDev" Coelho</dc:creator>
      <pubDate>Wed, 26 Feb 2025 12:57:05 +0000</pubDate>
      <link>https://forem.com/vinidev/de-20k-de-requisicos-a-500k-com-uma-infra-de-custo-beneficio-5bho</link>
      <guid>https://forem.com/vinidev/de-20k-de-requisicos-a-500k-com-uma-infra-de-custo-beneficio-5bho</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introdução&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Quando entrei neste projeto, o maior desafio era a alcançar um determinado nivel de escalabilidade para nos prepararmos para uma Proof of Concept que seriamos submetidos. A aplicação, construída em &lt;strong&gt;Ruby on Rails&lt;/strong&gt;, processava consultas médicas transcrevendo-as em anamneses estruturadas via &lt;strong&gt;OpenAI&lt;/strong&gt;. No entanto, ao rodarmos um teste de carga, percebemos que o sistema não aguentava mais de &lt;strong&gt;20k requisições simultâneas.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Diante disso, precisei entender os gargalos e otimizar ao máximo a aplicação para fazer um uso mais eficiente de nossa infraestutura custo benefício, rodando Sidekiq, Puma e Redis.&lt;/p&gt;

&lt;p&gt;Este artigo detalha como aumentamos a capacidade para suportar &lt;strong&gt;500k requisições simultâneas&lt;/strong&gt;, aproveitando 100% dos recursos disponíveis, sem precisar de usar de recursos para melhorar a infraestrutura.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;O Diagnóstico: Identificando os Gargalos&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxbmveqababs37xa3id1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxbmveqababs37xa3id1.jpg" alt="programming meme" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bom, antes de mais nada, pra quem ja vivenciou a experiencia de passar por uma POC sendo um desenvolvedor, sabe do que estou falando. É uma montanha russa! A todo momento alterações e novas features são solicitadas, mas nessa em específico, tínhamos uma grande barreira, a famigerada &lt;strong&gt;Escalabilidade&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Como ja foi citado, nossa aplicação que até o momento era apenas um MVP, estava enfrentando grandes problemas quanto a esse quesito, e seriamos massivamentes testados especificamente nesse nele, foi ai que por em prática conceitos básicos de &lt;strong&gt;Sistemas Operacionais&lt;/strong&gt; fizeram com que nossa aplicação alcancasse 500k de requisições sem gargalar nossa infraestrutura.&lt;/p&gt;

&lt;p&gt;Para entender os problemas da aplicação, criamos um teste de carga usando &lt;strong&gt;Locust,&lt;/strong&gt; simulando o fluxo real do usuário no sistema. Nosso objetivo era medir a resistência da aplicação sob um grande volume de requisições simultâneas.&lt;/p&gt;

&lt;p&gt;Pra quem ainda não conhece o Locust para testes de sobrecarga, vale conferir! É extremamente friendly e a curva de aprendizado para utilização é realmente muito rapida!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Segue link: &lt;a href="https://locust.io/" rel="noopener noreferrer"&gt;https://locust.io/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Resultados do teste com Locust:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inicialmente, com um número moderado de requisições, o sistema respondia bem.&lt;/li&gt;
&lt;li&gt;À medida que a carga aumentava, os tempos de resposta começaram a crescer exponencialmente.&lt;/li&gt;
&lt;li&gt;Ao atingir 20k requisições simultâneas, o servidor não conseguia mais lidar com a demanda, resultando em timeouts, erros de conexão resetada e, por fim, uma falha completa da aplicação.&lt;/li&gt;
&lt;li&gt;A imagem anexada ilustra bem os principais erros encontrados durante o teste, como timeouts, erros de conexão fechada remotamente e falhas SSL, indicando que a aplicação não estava conseguindo manter conexões abertas ou responder dentro do tempo esperado.&lt;/li&gt;
&lt;li&gt;O efeito cascata desses problemas gerava uma sobrecarga ainda maior no sistema, tornando a aplicação completamente indisponível.&lt;/li&gt;
&lt;li&gt;Esse diagnóstico confirmou que nossa arquitetura não estava preparada para lidar com um volume massivo de acessos simultâneos, exigindo ajustes para melhorar o gerenciamento de conexões, fila de processamento e paralelismo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Froabshxqjlk80hkis85z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Froabshxqjlk80hkis85z.png" alt="Locust results test" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Foi aí que entender um conceito básico de S.O salvou nossas vidas&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Diante do cenário caótico que enfrentávamos, percebemos que a solução não estava apenas em otimizar código ou adicionar mais servidores indiscriminadamente. Precisávamos repensar como nossa aplicação lidava com múltiplas requisições simultâneas. Foi aí que os conceitos de concorrência, paralelismo, processos e threads se tornaram nossas armas principais para escalar sem comprometer a infraestrutura.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Concorrência: O Jogo de Equilibrar as Execuções&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A concorrência é um dos conceitos fundamentais de Sistemas Operacionais. Ela permite que diferentes partes de um programa compartilhem recursos e se revezem na execução, gerenciando a carga de maneira mais eficiente.&lt;/p&gt;

&lt;p&gt;Para ilustrar, imagine que você está em um restaurante pequeno e há um único garçom anotando pedidos, levando pratos e cobrando os clientes. O garçom não consegue fazer todas as tarefas ao mesmo tempo, então ele precisa se organizar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ele anota um pedido e pausa essa tarefa para levar um prato pronto até a mesa.&lt;/li&gt;
&lt;li&gt;Depois de entregar o prato, ele volta para continuar anotando o pedido de outro cliente.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso é concorrência – as tarefas não acontecem simultaneamente, mas sim de forma intercalada, aproveitando os momentos em que o garçom pode realizar uma nova ação sem ficar parado esperando outra tarefa terminar.&lt;/p&gt;

&lt;p&gt;Agora, imagine que o garçom começa a ficar sobrecarregado porque precisa lidar com muitos pedidos ao mesmo tempo. O que acontece?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O atendimento começa a ficar lento.&lt;/li&gt;
&lt;li&gt;Os pedidos começam a acumular na cozinha.&lt;/li&gt;
&lt;li&gt;Alguns clientes vão embora sem serem atendidos (timeouts na API ).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E foi exatamente isso que aconteceu com a nossa aplicação. Nossa API estava lidando com as requisições de forma sequencial, sem tirar proveito da concorrência real. O resultado? Um efeito cascata de travamentos e falhas de conexão.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Concorrência vs. Paralelismo: O Ponto de Virada&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Agora, vamos imaginar que o restaurante cresce e decide contratar mais garçons.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Se os garçons continuarem compartilhando a mesma bandeja para levar pratos, eles ainda precisarão esperar uns pelos outros. Isso ainda é concorrência.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mas se cada garçom tiver sua própria bandeja e puder atender clientes simultaneamente, agora estamos falando de paralelismo real!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Na nossa aplicação, o grande erro era tratar processos bloqueantes como concorrentes, quando, na verdade, eles poderiam ser paralelos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O que isso significa na prática?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Algumas tarefas estavam competindo pelo mesmo recurso, quando poderiam ser distribuídas de forma independente.&lt;/li&gt;
&lt;li&gt;Nossa API estava funcionando como um único garçom, lidando com várias requisições de maneira intercalada, mas sem processá-las ao mesmo tempo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A solução? Transformar processos concorrentes em paralelos, eliminando bloqueios desnecessários!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Processos e Threads: Quem Faz o Trabalho?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Agora que entendemos que precisávamos de concorrência, como implementá-la na prática? Para isso, precisamos entender dois conceitos-chave: processos e threads.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;O que são processos?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Um processo é uma instância independente de um programa em execução. Ele tem seu próprio espaço de memória e recursos exclusivos. No contexto de um servidor web, cada processo pode lidar com uma ou mais requisições, mas como cada processo consome uma quantidade significativa de memória, escalá-los diretamente pode ser caro e ineficiente.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;O que são threads?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As threads são unidades menores de execução dentro de um processo. Diferente dos processos, threads compartilham o mesmo espaço de memória, tornando sua criação e gerenciamento mais leve. Isso permite que uma aplicação possa lidar com múltiplas tarefas ao mesmo tempo sem precisar iniciar novos processos do zero.&lt;/p&gt;

&lt;p&gt;Podemos pensar nos processos como cozinhas separadas em um restaurante e nas threads como os chefs dentro de cada cozinha. Se cada cozinha trabalha de forma isolada, a comunicação pode ser lenta. Mas se dentro de cada cozinha há vários chefs compartilhando ingredientes e espaço, os pedidos são preparados de forma mais eficiente.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Ajustando a Concorrrência com Base na Quantidade de Núcleos do Servidor&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Para garantir que o sistema aproveitasse 100% da capacidade disponível sem sobrecarregar os recursos, utilizamos um cálculo baseado na quantidade de núcleos da CPU.&lt;/p&gt;

&lt;p&gt;Cada núcleo pode processar um número limitado de tarefas simultaneamente. Para definir quantos processos e threads usar, utilizamos a seguinte fórmula padrão:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fórmula para Processos e Threads:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Número de processos = Número de núcleos físicos&lt;br&gt;
Número de threads por processo = 2 a 4 threads por núcleo&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Isso significa que, se o servidor possui 8 núcleos físicos, podemos configurar:&lt;/p&gt;

&lt;p&gt;8 processos Puma (1 por núcleo)&lt;br&gt;
Entre 16 e 32 threads no total (2 a 4 por núcleo)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Por que esse cálculo funciona?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Se usarmos menos processos do que núcleos, não aproveitamos toda a CPU.&lt;br&gt;
Se usarmos muito mais threads do que o recomendado, corremos o risco de aumentar a latência e criar contenção de recursos.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Como configurar isso no Puma?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;No arquivo &lt;strong&gt;config/puma.rb&lt;/strong&gt;, podemos definir a configuração dinamicamente:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;workers ENV.fetch("WEB_CONCURRENCY") { 8 } # Número de processos baseado nos núcleos&lt;br&gt;
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 16 } # Threads por processo&lt;br&gt;
threads threads_count, threads_count&lt;br&gt;
preload_app!&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Ponto importante!&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Teste diferentes configurações para encontrar o ponto ideal, pois a performance pode variar dependendo da carga de trabalho e do banco de dados.&lt;/li&gt;
&lt;li&gt;Utilize ferramentas como htop ou top no Linux para monitorar o uso da CPU e ajustar conforme necessário.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Ajustando o NGINX para Lidar com Alto Volume de Requisições&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Com o Puma e Sidekiq otimizados, ainda enfrentamos um problema: o NGINX não estava configurado para lidar com um alto número de conexões simultâneas. Como ele atua como um proxy reverso, precisávamos ajustá-lo para permitir mais conexões e evitar erros de 502 Bad Gateway e timeouts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Principais ajustes que fizemos:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Aumentamos o limite de conexões simultâneas&lt;/strong&gt;
No arquivo &lt;strong&gt;/etc/nginx/nginx.conf&lt;/strong&gt;, ajustamos os valores para permitir mais conexões concorrentes:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;worker_processes auto;  # Define automaticamente a quantidade de processos com base nos núcleos da CPU&lt;br&gt;
worker_connections 8192;  # Define quantas conexões cada worker pode lidar simultaneamente&lt;br&gt;
multi_accept on;  # Permite aceitar múltiplas conexões ao mesmo tempo&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;O que isso faz?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;worker_processes auto → Define um número de processos igual ao número de núcleos da CPU.&lt;/li&gt;
&lt;li&gt;worker_connections 8192 → Permite que cada processo gerencie até 8192 conexões simultâneas.&lt;/li&gt;
&lt;li&gt;multi_accept on → Permite que o NGINX aceite várias conexões ao mesmo tempo, melhorando a latência.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Aumentamos o tempo limite das requisições&lt;/strong&gt;&lt;br&gt;
Com muitas requisições passando pelo NGINX, algumas podiam demorar mais tempo para serem processadas, especialmente as que envolviam a transcrição de consultas médicas via OpenAI. Ajustamos os timeouts para evitar encerramentos prematuros:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;proxy_connect_timeout 60s;&lt;br&gt;
proxy_send_timeout 60s;&lt;br&gt;
proxy_read_timeout 60s;&lt;br&gt;
send_timeout 60s;&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;O que isso faz?&lt;/strong&gt;
Evita que conexões sejam fechadas prematuramente durante requisições mais demoradas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ajustamos o buffer de resposta&lt;/strong&gt;&lt;br&gt;
Como algumas respostas da API podiam ser grandes, aumentamos o buffer para evitar truncamento ou erros de payload.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;proxy_buffer_size 128k;&lt;br&gt;
proxy_buffers 4 256k;&lt;br&gt;
proxy_busy_buffers_size 256k;&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;O que isso faz?&lt;/strong&gt;
Impede que respostas grandes sejam cortadas e melhora a eficiência da comunicação entre NGINX e Puma.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Como aplicamos isso para resolver nosso problema?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Nosso servidor original estava lidando com cada requisição de forma bloqueante, ou seja, cada requisição ocupava um recurso até ser completamente processada, sem permitir que outras requisições fossem tratadas enquanto isso. Esse comportamento causava um efeito cascata de timeouts e falhas, derrubando o sistema sob alta carga.&lt;/p&gt;

&lt;p&gt;Para escalar corretamente e garantir 500k requisições simultâneas, aplicamos quatro estratégias principais:&lt;/p&gt;

&lt;h3&gt;
  
  
  Ajustamos a Concorrência com Base nos Núcleos do Servidor
&lt;/h3&gt;

&lt;p&gt;Antes de qualquer mudança, entendemos que a chave para escalar nossa aplicação era aproveitar melhor os recursos do servidor, evitando desperdício de CPU e memória.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Como fizemos isso?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Calculamos o número ideal de processos e threads com base nos núcleos físicos do servidor&lt;br&gt;
Definimos 1 processo Puma por núcleo e 2 a 4 threads por núcleo para garantir um melhor aproveitamento sem sobrecarregar o sistema&lt;br&gt;
Monitoramos o desempenho com ferramentas como htop e top para ajustar os valores de acordo com o perfil da aplicação&lt;/p&gt;

&lt;h3&gt;
  
  
  Adotamos o servidor Puma com múltiplas threads
&lt;/h3&gt;

&lt;p&gt;O Puma é um servidor web otimizado para concorrência baseada em threads. Diferente de servidores tradicionais que criam um novo processo para cada requisição, ele mantém um número fixo de processos e cria múltiplas threads dentro de cada um para atender às requisições simultaneamente. Isso permitiu que nossa aplicação Rails processasse muito mais requisições sem consumir memória excessivamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementamos filas de background com Sidekiq e Redis
&lt;/h3&gt;

&lt;p&gt;Algumas operações, como a transcrição de consultas médicas via OpenAI, eram naturalmente demoradas e não precisavam ser processadas imediatamente na resposta da requisição. Utilizando o Sidekiq, conseguimos delegar essas tarefas para workers, que as executavam em segundo plano sem bloquear as requisições principais.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ajustamos a concorrência da base de dados
&lt;/h3&gt;

&lt;p&gt;O banco de dados também era um gargalo crítico. Otimizar conexões e garantir que as queries fossem eficientes foi essencial para evitar travamentos causados pelo excesso de conexões concorrentes.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;O Resultado&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Após essas mudanças, rodamos novamente os testes de carga com o Locust. O impacto foi impressionante:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conseguimos aumentar a capacidade de 20k para 500k requisições simultâneas, aproveitando melhor a infraestrutura existente.&lt;/li&gt;
&lt;li&gt;A latência média das requisições caiu significativamente, pois o servidor conseguia processar várias ao mesmo tempo sem sobrecarregar os recursos.&lt;/li&gt;
&lt;li&gt;A estabilidade da aplicação foi garantida, mesmo sob cargas intensas.&lt;/li&gt;
&lt;li&gt;Ao final, conseguimos alcançar a escalabilidade necessária sem precisar gastar com novos servidores, apenas utilizando conceitos básicos de concorrência, processos e threads a nosso favor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E foi dessa forma que conseguimos sair de &lt;strong&gt;20k de Requisiçõs a 500k+ com uma infra de custo benefício!!!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deixo um agradecimento a toda equipe que esteve durante todo esse processo também!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
      <category>rails</category>
    </item>
  </channel>
</rss>
