14 pontos por GN⁺ 2025-01-09 | 5 comentários | Compartilhar no WhatsApp
  • Era necessário lidar com atualizações em tempo real em larga escala em um backend baseado em Node.js/TypeScript
  • Usando PostgreSQL como backend, centenas de nós workers precisavam verificar continuamente novos jobs, e os agentes precisavam receber atualizações de status de execução e chat
  • A investigação começou com WebSocket, mas acabou chegando a uma solução "à moda antiga" surpreendentemente eficaz
    → "HTTP Long Polling com Postgres"

O problema: atualizações em tempo real em larga escala

  • Atualizações dos nós workers :
    • Havia centenas de nós workers executando SDKs de Node.js/Golang/C#
    • Eles precisavam saber assim que um novo job estivesse disponível, então era necessária uma estratégia de consulta que não derrubasse o banco de dados Postgres
  • Sincronização de estado dos agentes :
    • Os agentes precisavam de atualizações em tempo real sobre status de execução e chat, e isso precisava ser transmitido de forma eficiente

Comparação entre long polling e WebSocket

  • Short polling é como um trem que parte rigidamente no horário, saindo em intervalos fixos independentemente de haver passageiros ou não
  • Long polling faz o servidor esperar para responder e, quando surgem dados, devolve a resposta imediatamente; se passar um certo tempo, responde por timeout
    • Ou seja, é como um trem que "espera até aparecer passageiro para partir". Só sai vazio quando ninguém aparece dentro de um tempo específico (TTL)
    • Quando há dados (passageiros), parte imediatamente; quando não há, permite usar os recursos de forma eficiente, reunindo as duas vantagens
  • WebSocket mantém a conexão aberta continuamente para troca de dados bidirecional
    • Em ambientes corporativos, por questões de infraestrutura e firewall, o long polling era mais simples e mais compatível do que configurar WebSocket

Detalhes da implementação de long polling

  • A função getJobStatusSync desempenha um papel importante
    • Ela recebe parâmetros como jobId, owner e ttl e consulta repetidamente o estado de um job específico durante um período determinado
  • A consulta repetida continua até que uma das condições abaixo seja atendida
    • O status do job se torne success ou failure
    • O ttl (timeout) expire
  • O banco de dados é consultado em intervalos de 500ms e, se o resultado ainda não estiver definido, espera-se e consulta-se novamente
  • Se o timeout for excedido, um erro é lançado; em caso de sucesso, o resultado é retornado

Otimização do banco de dados

  • Índices apropriados foram criados no Postgres para minimizar o custo das consultas
  • Exemplo: CREATE INDEX idx_jobs_status ON jobs(id, cluster_id);

Vantagens do long polling

  • Facilidade para manter o monitoramento : é possível reutilizar a pilha existente de logging e monitoramento baseada em HTTP
  • Simplicidade na autenticação : é possível usar a autenticação HTTP existente sem implementar um novo método
  • Compatibilidade com a infraestrutura : não são necessárias configurações especiais em firewalls ou load balancers, pois é tratado como tráfego HTTP comum
  • Simplicidade operacional : mesmo em reinícios do servidor, não é preciso tratar o estado da conexão separadamente, e o debugging fica mais fácil
  • Facilidade de implementação no cliente : funciona apenas adicionando lógica de retry sobre a estrutura padrão de requisição-resposta HTTP

Comparação com o ElectricSQL

  • ElectricSQL é uma solução para sincronizar dados do Postgres com o frontend
  • Ele tem uma estrutura que garante comportamento em tempo real usando HTTP em vez de WebSocket
  • Na prática, quando não é necessário um controle extremo ou uma estrutura de baixo nível para lidar com atualizações em tempo real, o ElectricSQL é recomendado

Por que escolhemos raw long polling

  • O mecanismo de entrega de mensagens não é um simples detalhe de implementação, mas um elemento central do produto
  • Não era possível depender de uma biblioteca de terceiros para uma funcionalidade central (por melhor que fosse a biblioteca)
  • Requisitos
    • Controle do produto central : era necessário controlar completamente o mecanismo de entrega de mensagens. Não é algo de nível de infraestrutura, e sim do próprio produto
    • Eliminação de dependências externas : minimizar dependências externas para simplificar o self-hosting
    • Controle de baixo nível : controlar diretamente o mecanismo de polling e o gerenciamento de conexões
    • Máximo nível de controle : era preciso poder ajustar finamente detalhes como intervalos de polling dinâmicos
    • Simplicidade do código : projetar de forma simples para que os usuários possam entender e modificar facilmente a base de código
  • Em conclusão, ao escolher uma implementação simples de HTTP Long Polling, garantimos controle direto e simplicidade

Cuidados ao implementar long polling

  • Configuração de TTL : o servidor deve impor obrigatoriamente um TTL máximo, garantindo que o TTL solicitado pelo cliente não o ultrapasse
  • Considerar timeouts da infraestrutura : o TTL deve ser suficientemente menor do que os timeouts configurados em load balancers, edge servers e proxies
  • Intervalo de polling no BD : usar um atraso de cerca de 500ms para reduzir a carga no banco
  • Estratégia de backoff (opcional) : aumentar gradualmente o intervalo de polling pode usar os recursos do sistema com mais eficiência

Situações em que vale considerar WebSocket

  • O WebSocket em si não está errado, e pode ser útil em outros aspectos
    • Quando é necessário monitorar conexões com muito estado e trocar eventos complexos continuamente
    • Quando há recursos e tempo suficientes para resolver questões de autenticação, infraestrutura e observabilidade
  • Existe a complexidade de precisar construir manualmente operação e logging, tratamento de reconexão, mecanismos de autenticação etc.

WebSockets: falando de outra opção

  • Embora o long polling tenha sido adequado para nossas necessidades, WebSockets também merecem consideração
  • WebSockets em si não são ruins; apenas exigem muita atenção e gestão
  • Principais desafios dos WebSockets e direções de solução
    • Visibilidade : como WebSockets são baseados em estado, é preciso adicionar logging e monitoramento para conexões persistentes
    • Autenticação : é necessário implementar um novo mecanismo de autenticação para conexões WebSocket
    • Infraestrutura : é preciso configurar adequadamente load balancers, firewalls e outros componentes para suportar WebSocket
    • Gestão operacional : gerenciamento de conexões e reconexões WebSocket, além de tratamento de timeout e erros
    • Implementação no cliente : implementação de uma biblioteca WebSocket no cliente, incluindo reconexão e gerenciamento de estado

5 comentários

 
jhj0517 2025-01-10

Estamos usando a estrutura de “short polling” mencionada aqui para servir modelos de ML, e tenho pensado bastante sobre o que seria mais eficiente. Pelo que pesquisei aqui e ali por conta própria, ouvi dizer que, por causa do alto custo do tratamento de reconexão em WebSockets ou SSE, o short polling costuma ser mais seguro no geral, então acabei escolhendo short polling.. 😭

 
bbulbum 2025-01-10

Parece que muita gente evita long polling porque ele soa meio hacky. No navegador, provavelmente vai parecer que a requisição nunca foi concluída. Às vezes existem sites em que o carregamento nunca termina, e eu fico pensando: será que nem todo o conteúdo foi carregado? Acho isso bem ruim.
No aplicativo, no fim das contas também vai acabar existindo algum ponto em que fica em hang aguardando a resposta, então isso parece um pouco estranho.

 
joyfui 2025-01-09

"o agente precisa receber atualizações de status de execução e do chat"
Ao ver isso, pensei imediatamente em SSE, e de fato há muitas menções a SSE nas opiniões do Hacker News.

 
GN⁺ 2025-01-09
Comentários do Hacker News
  • Long polling tem seus próprios problemas

    • O Second Life usa um canal de long polling via HTTPS entre cliente e servidor
    • No lado do cliente, usa libcurl, e timeouts podem acontecer
    • Se o servidor tentar enviar uma mensagem entre o timeout e a próxima requisição, pode ocorrer uma condição de corrida e a mensagem ser perdida
    • Há um servidor Apache na frente bloqueando requisições desnecessárias, mas timeouts ainda podem acontecer
    • Middleboxes e servidores proxy podem não gostar de long polling
    • Há muitos elementos que não gostam de manter conexões HTTP abertas por muito tempo
    • No fim, isso vira um canal de mensagens não confiável, exigindo números de sequência para detectar duplicatas, e mensagens podem se perder
    • A seção do gráfico marcada como "loop" no artigo original não menciona o tratamento de timeout
    • Ao usar long polling, é preciso enviar dados a cada poucos segundos para manter a conexão viva
  • É um prazer usar Phoenix e LiveView todos os dias

    • Com WebSockets, não é preciso se preocupar com isso
  • Fico curioso se há alguma vantagem técnica em relação ao uso de Server-Sent Events (SSE)

    • Ambos mantêm uma conexão HTTP aberta e têm a vantagem de serem HTTP simples
    • SSE parece mais adequado quando é possível transmitir atualizações ou resultados em streaming
    • Um caso de uso adequado pode ser monitorar todos os IDs de trabalho em nome de um cliente específico
  • Este artigo conecta "Websocket" e "Long-polling" como se fossem decisões independentes

    • Um servidor de long-polling pode lidar com clientes websocket com um pouco de trabalho extra
    • Se a arquitetura existente for baseada em websocket, dar suporte a clientes long-polling exige duas camadas de servidor
  • Uma forma mais fácil de usar setTimeout no Node.js

    • usar import { setTimeout } from "node:timers/promises"; await setTimeout(500);
  • Gosto de long polling, é fácil de entender e, do ponto de vista do cliente, funciona como uma conexão muito lenta

    • É preciso rastrear novas tentativas e conexões canceladas pelo cliente
    • No exemplo de código, o loop que consulta dados repetidamente parece estranho
  • Server-Sent Events ou WebSockets não substituem todos os casos de uso de long polling

    • O limite de conexões do SSE aparece com frequência como problema
    • WebSockets não são confiáveis na maioria dos ambientes
    • O problema de detectar mudanças no backend e propagá-las ao cliente certo continua sem solução
  • É melhor usar o recurso de notificações assíncronas do Postgres

    • O servidor pode dar LISTEN em um canal e, quando os dados mudarem, o PG pode acionar TRIGGER e NOTIFY
  • Não sei se long polling ainda faz sentido com timeouts curtos e requisições encerradas de forma elegante

    • Se HTTP/2 ou QUIC não estiverem em uso, esse truque ainda pode fazer sentido
  • É revigorante ser lembrado de uma alternativa relativamente simples aos WebSockets

    • Já trabalhei em uma startup que escolheu WebSockets, e era difícil testar em Wi‑Fi de hotéis e restaurantes
 
luminance 2025-01-10

Quero experimentar usar WebSockets com Elixir, o framework Phoenix e o LiveView.