KoderKoder.ai
PreçosEnterpriseEducaçãoPara investidores
EntrarComeçar

Produto

PreçosEnterprisePara investidores

Recursos

Fale conoscoSuporteEducaçãoBlog

Jurídico

Política de privacidadeTermos de usoSegurançaPolítica de uso aceitávelDenunciar abuso

Social

LinkedInTwitter
Koder.ai
Idioma

© 2026 Koder.ai. Todos os direitos reservados.

Início›Blog›PostgreSQL LISTEN/NOTIFY: quando é suficiente para atualizações em tempo real
21 de ago. de 2025·8 min

PostgreSQL LISTEN/NOTIFY: quando é suficiente para atualizações em tempo real

PostgreSQL LISTEN/NOTIFY pode alimentar dashboards e alertas em tempo real com configuração mínima. Saiba onde ele se encaixa, seus limites e quando adicionar um broker.

PostgreSQL LISTEN/NOTIFY: quando é suficiente para atualizações em tempo real

Qual problema o LISTEN/NOTIFY tenta resolver

“Atualizações em tempo real” na interface de um produto normalmente significa que a tela muda logo depois que algo acontece, sem o usuário precisar atualizar a página. Um número aumenta no dashboard, um badge vermelho aparece na caixa de entrada, um administrador vê um pedido novo, ou aparece um toast dizendo “Build finished” ou “Payment failed”. O importante é o timing: parece instantâneo, mesmo que leve um ou dois segundos.

Muitas equipes começam com polling: o navegador pergunta ao servidor “tem algo novo?” a cada poucos segundos. Polling funciona, mas tem duas desvantagens comuns.

Primeiro, parece lento porque o usuário só vê mudanças no próximo poll.

Segundo, pode ficar caro porque você faz verificações repetidas mesmo quando nada mudou. Multiplique isso por milhares de usuários e vira ruído.

PostgreSQL LISTEN/NOTIFY existe para um caso mais simples: “me avise quando algo mudou.” Em vez de perguntar repetidamente, sua aplicação pode esperar e reagir quando o banco envia um pequeno sinal.

É uma boa opção para UIs onde um empurrão é suficiente. Por exemplo:

  • Um tile do dashboard deve atualizar porque os totais mudaram
  • A contagem do badge deve mudar porque chegou um item novo
  • Um alerta interno deve aparecer porque o status de um job mudou

A troca é simplicidade versus garantias. LISTEN/NOTIFY é fácil de adicionar porque já está no Postgres, mas não é um sistema completo de mensageria. A notificação é uma dica, não um registro durável. Se um listener estiver desconectado, pode perder o sinal.

Uma forma prática de usar: deixe o NOTIFY acordar sua aplicação e, então, faça sua aplicação ler a verdade das tabelas.

Como PostgreSQL LISTEN/NOTIFY funciona em termos simples

Pense no PostgreSQL LISTEN/NOTIFY como uma campainha simples embutida no seu banco de dados. Sua aplicação pode esperar pela campainha tocar, e outra parte do sistema pode tocá-la quando algo mudar.

Uma notificação tem duas partes: um nome de canal e um payload opcional. O canal é como um rótulo de tópico (por exemplo, orders_changed). O payload é uma pequena mensagem de texto que você anexa (por exemplo, um id de pedido). O PostgreSQL não impõe estrutura, então equipes frequentemente enviam strings JSON pequenas.

Quem toca a campainha?

Uma notificação pode ser disparada a partir do código da aplicação (seu servidor API executa NOTIFY) ou a partir do próprio banco usando um trigger (um trigger executa NOTIFY após um insert/update/delete).

  • Código da aplicação é mais fácil de seguir e testar.
  • Triggers ajudam quando vários escritores tocam as mesmas tabelas e você quer comportamento consistente.

No lado receptor, seu servidor de aplicação abre uma conexão com o banco e executa LISTEN channel_name. Essa conexão fica aberta. Quando um NOTIFY channel_name, 'payload' acontece, o PostgreSQL envia uma mensagem para todas as conexões que escutam naquele canal. Sua aplicação então reage (atualiza cache, busca a linha alterada, envia um evento via WebSocket para o navegador, e assim por diante).

O que o NOTIFY faz (e não faz)

NOTIFY é melhor entendido como um sinal, não um serviço de entrega:

  • Diz aos listeners que “algo aconteceu” agora.
  • Não garante que toda mensagem será recebida (um listener desconectado perde a mensagem).
  • Não é uma fila (mensagens não são armazenadas para depois).
  • O payload tem limite de tamanho e deve permanecer pequeno.
  • Deve apontar para dados, não substituí-los (envie ids e depois consulte os detalhes).

Usado dessa forma, PostgreSQL LISTEN/NOTIFY pode alimentar atualizações de UI em tempo real sem adicionar infraestrutura extra.

Quando LISTEN/NOTIFY é suficiente para atualizações em tempo real na UI

LISTEN/NOTIFY brilha quando sua UI só precisa de um empurrão indicando que algo mudou, não de um fluxo completo de eventos. Pense em “recarregue esse widget” ou “há um item novo” em vez de “processe cada clique em ordem”.

Funciona melhor quando o banco de dados já é sua fonte de verdade e você quer que a UI permaneça em sincronia com ele. Um padrão comum é: escreva a linha, envie uma notificação pequena com um ID, e faça a UI (ou uma API) buscar o estado mais recente.

LISTEN/NOTIFY normalmente é suficiente quando a maioria destes pontos é verdadeira:

  • A mensagem é um pequeno sinal “algo mudou”, não um payload completo.
  • O volume de eventos é baixo a moderado (rajadas são aceitáveis, throughput sustentado alto não).
  • Se um usuário perder uma notificação, a UI pode se recuperar recarregando ou fazendo polling breve.
  • Você valoriza simplicidade mais do que garantias de entrega perfeitas (comum em produtos iniciais e ferramentas internas).
  • Você tem um banco de dados primário único e quer menos componentes móveis.

Um exemplo concreto: um painel interno de suporte mostra “tickets abertos” e um badge para “notas novas”. Quando um agente adiciona uma nota, seu backend grava no Postgres e NOTIFY em ticket_changed com o ID do ticket. O navegador recebe via WebSocket e busca aquele cartão de ticket. Sem infraestrutura extra, a UI parece em tempo real.

Onde LISTEN/NOTIFY começa a falhar

LISTEN/NOTIFY pode parecer ótimo no começo, mas tem limites rígidos. Esses limites surgem quando você trata notificações como um sistema de mensagens em vez de um leve “toque no ombro”.

A maior lacuna é durabilidade. Um NOTIFY não é um job enfileirado. Se ninguém estiver ouvindo naquele momento, a mensagem é perdida. Mesmo quando um listener está conectado, uma queda, deploy, problema de rede ou reinício do banco pode fechar a conexão. Você não recebe automaticamente as notificações “perdidas”.

Desconexões são especialmente dolorosas para funcionalidades voltadas ao usuário. Imagine um dashboard que mostra novos pedidos. Uma aba do navegador entra em modo de suspensão, o WebSocket reconecta e a UI fica “travada” porque perdeu alguns eventos. Dá para contornar, mas o workaround deixa de ser “apenas LISTEN/NOTIFY”: você reconstrói o estado consultando o banco e usando NOTIFY só como um lembrete para atualizar.

Fan-out é outro problema comum. Um evento pode acordar centenas ou milhares de listeners (muitos servidores, muitos usuários). Se você usa um canal ruidoso como orders, todo listener acorda mesmo se só um usuário se importa. Isso pode criar picos de CPU e pressão nas conexões no pior momento.

Tamanho do payload e frequência são as armadilhas finais. Payloads do NOTIFY são pequenos, e eventos em alta frequência podem se acumular mais rápido do que os clientes conseguem processar.

Fique atento a estes sinais:

  • Você precisa de entrega garantida, retries ou garantias de ordenação.
  • Clientes reconectam frequentemente e não podem perder atualizações.
  • Um canal acorda muitos listeners que na maior parte ignoram o evento.
  • Você está enviando payloads grandes ou eventos muitas vezes por segundo.

Nesse ponto, mantenha o NOTIFY como um “cutucão” e leve a confiabilidade para uma tabela ou para um message broker apropriado.

Passo a passo: um padrão simples que funciona

Construa um centro de notificações
Crie badges, toasts e atualizações via SSE ou WebSocket a partir de instruções simples.
Construir notificações

Um padrão confiável com LISTEN/NOTIFY é tratar o NOTIFY como um empurrão, não como a fonte de verdade. A linha na base é a verdade; a notificação diz à aplicação quando olhar.

1) Escreva, comite, depois notifique

Faça a escrita dentro de uma transação e só envie a notificação após a mudança de dados ser comitada. Se você notificar cedo demais, clientes podem acordar e não encontrar os dados.

Uma configuração comum é um trigger que dispara no INSERT/UPDATE e envia uma pequena mensagem.

NOTIFY dashboard_updates, '{"type":"order_changed","order_id":123}'::text;

2) Escolha nomes de canal simples e payloads mínimos

Nomear canais funciona melhor quando corresponde a como as pessoas pensam sobre o sistema. Exemplos: dashboard_updates, user_notifications ou por tenant, como tenant_42_updates.

Mantenha o payload pequeno. Coloque identificadores e um tipo, não registros completos. Uma forma útil padrão é:

  • type (o que aconteceu)
  • id (o que mudou)
  • opcional tenant_id ou user_id

Isso mantém a largura de banda baixa e evita vazar dados sensíveis nos logs de notificação.

3) Lide com reconexões e re-subscreva em cada conexão

Conexões caem. Planeje isso.

Ao conectar, execute LISTEN para todos os canais necessários. Ao desconectar, reconecte com um short backoff. Ao reconectar, execute LISTEN novamente (as subscrições não persistem). Depois da reconexão, faça um refetch rápido de “mudanças recentes” para cobrir eventos perdidos.

4) Atualize a UI: refetch primeiro, aplicar depois

Para a maioria das atualizações em tempo real, refazer a busca é a opção mais segura: o cliente recebe {type, id} e então pede ao servidor o estado mais recente.

Patch incremental pode ser mais rápido, mas é mais fácil errar (eventos fora de ordem, falhas parciais). Um bom meio-termo é: refetch de fatias pequenas (uma linha de pedido, um cartão de ticket, a contagem de um badge) e deixar agregados pesados em um timer curto.

Padrões de escala para dashboards e notificações

Quando você passa de um dashboard de admin para muitos usuários assistindo os mesmos números, bons hábitos importam mais do que SQL esperto. LISTEN/NOTIFY ainda pode funcionar bem, mas você precisa modelar como eventos fluem do banco para os browsers.

Uma base comum é: cada instância de app abre uma conexão de longa duração que faz LISTEN, e então empurra atualizações para os clientes conectados. Esse “um listener por instância” é simples e costuma dar conta se você tem poucas instâncias e tolera reconexões ocasionais.

Se você tem muitas instâncias (ou workers serverless), um serviço de listener compartilhado pode ser mais fácil. Um processo pequeno escuta uma vez e faz fan-out para o restante da stack. Também é um lugar único para adicionar batching, métricas e backpressure.

Para navegadores, normalmente você envia via WebSockets (bidirecional, ótimo para UIs interativas) ou Server-Sent Events (SSE) (unidirecional, mais simples para dashboards). De qualquer forma, evite enviar “recarregue tudo.” Envie sinais compactos como “order 123 mudou” para que a UI refaça apenas o que precisa.

Para evitar thrashing na UI, adicione guardrails:

  • Debounce de rajadas (por exemplo, 100 a 500 ms) antes de empurrar para os clientes
  • Coalesce duplicatas (mesmo registro mudado 10 vezes, envie 1 atualização)
  • Use “flags sujas” por widget em vez de refresh completo da página

O design dos canais também importa. Em vez de um canal global, particione por tenant, time ou feature para que clientes só recebam eventos relevantes. Exemplo: notify:tenant_42:billing e notify:tenant_42:ops.

Erros comuns e como evitá-los

LISTEN/NOTIFY parece simples, por isso equipes entregam rápido e depois se surpreendem em produção. A maioria dos problemas vem de tratá-lo como uma fila de mensagens durável.

1) Tratar notificações como mensagens duráveis

Se sua aplicação reconectar (deploy, queda de rede, failover), qualquer NOTIFY enviado enquanto você estava desconectado se perde. A correção é tratar a notificação como um sinal e então re-checar o banco.

Um padrão prático: armazene o evento real em uma tabela (com id e created_at) e, ao reconectar, busque tudo mais novo que seu último id visto.

2) Sobrecarregar o payload

Payloads do LISTEN/NOTIFY não são para JSONs grandes. Payloads grandes criam trabalho extra, parse adicional e mais chance de atingir limites.

Use payloads como dicas pequenas, tipo "order:123". Depois a aplicação lê o estado mais recente no banco.

3) Misturar “sinal” com “fetch de dados”

Um erro comum é desenhar a UI em torno do conteúdo do payload, como se ele fosse a fonte de verdade. Isso dificulta mudanças de esquema e versões de cliente.

Mantenha uma divisão clara: notifique que algo mudou e depois busque os dados atuais com uma query normal.

4) Triggers que disparam demais

Triggers que NOTIFY em toda mudança de linha podem inundar seu sistema, especialmente em tabelas muito ativas.

Notifique só em transições significativas (por exemplo, mudança de status). Se tiver updates muito ruidosos, agrupe mudanças (um notify por transação ou por janela de tempo) ou tire essas atualizações do caminho de notify.

5) Ignorar backpressure na UI

Mesmo que o banco consiga enviar notificações, sua UI pode travar. Um dashboard que re-renderiza em todo evento pode congelar.

Debounce no cliente, colapse rajadas em um único refresh e prefira “invalidar e refazer” a “aplicar todo delta”. Por exemplo: o ícone de notificação pode atualizar instantaneamente, mas a lista detalhada pode atualizar no máximo a cada poucos segundos.

Checklist rápido: decida se LISTEN/NOTIFY se encaixa

Adicione segurança com rollback
Capture snapshots enquanto você itera nas notificações e na lógica de atualização de UI.
Usar Snapshots

LISTEN/NOTIFY é ótimo quando você quer um pequeno sinal “algo mudou” para que a aplicação busque dados frescos. Não é um sistema de mensageria completo.

Antes de construir a UI em torno dele, responda:

  • Se um listener ficar offline por um minuto, ele pode perder eventos sem quebrar algo? Se perder for inaceitável, você precisa de entrega durável e replay.
  • “Quase em tempo real” é suficiente? Se os usuários toleram um breve atraso (ou um refresh manual) durante deploys ou falhas, LISTEN/NOTIFY costuma bastar.
  • Qual é sua taxa máxima de pico? Alguns eventos por segundo ou picos ocasionais são pontos ideais. Volume sustentado alto torna canais ruidosos e difíceis de gerenciar.
  • Quantos consumidores vão escutar ao mesmo tempo? Uma ou poucas workers de backend é fácil. Centenas ou milhares de clientes conectados geralmente significam que você quer uma camada de fan-out (seu servidor) em vez de todo mundo escutar direto.
  • Você precisa de ordenação estrita entre vários tipos de evento? Se seu dashboard depende de “A deve ser processado antes de B” entre streams diferentes, complica rápido.

Uma regra prática: se você puder tratar NOTIFY como um empurrão (“vá reler a linha”) em vez de como o payload em si, você está na zona segura.

Exemplo: um dashboard de admin mostra novos pedidos. Se uma notificação for perdida, o próximo poll ou refresh ainda mostra a contagem correta. Isso é um bom uso. Mas se você envia eventos como “cobrar este cartão” ou “enviar este pacote”, perder um evento é um incidente grave.

Exemplo: dashboard em tempo real + notificações para usuários

Imagine um pequeno app de vendas: um dashboard mostra a receita do dia, total de pedidos e uma lista de “pedidos recentes”. Ao mesmo tempo, cada vendedor deve receber uma notificação rápida quando um pedido que ele gerencia for pago ou enviado.

Uma abordagem simples é tratar o PostgreSQL como fonte de verdade e usar LISTEN/NOTIFY apenas como um toque no ombro indicando que algo mudou.

Quando um pedido é criado ou seu status muda, seu backend faz duas coisas na mesma requisição: grava (ou atualiza) a linha e então envia um NOTIFY com um payload pequeno (geralmente apenas o ID do pedido e o tipo de evento). A UI não depende do payload do NOTIFY para os dados completos.

Um fluxo prático fica assim:

  • Grave a mudança do pedido em uma transação.
  • Após o commit, NOTIFY orders_events com {"type":"status_changed","order_id":123}.
  • Um listener backend recebe eventos e os empurra para navegadores conectados (WebSocket ou SSE).
  • O dashboard refaz o que precisa: busca a linha do pedido por ID e recalcula totais num intervalo curto (por exemplo a cada 2 a 5 segundos) em vez de recalcular a cada evento.
  • Notificações de usuário são direcionadas: só o vendedor associado recebe o toast de “pago” ou “enviado”.

Isso mantém o NOTIFY leve e limita queries caras.

Quando o tráfego cresce, aparecem falhas: um pico de eventos pode sobrecarregar um listener, notificações podem ser perdidas na reconexão e você passa a precisar de entrega garantida e replay. É aí que normalmente se adiciona uma camada mais confiável (tabela outbox + worker, depois um broker se necessário), mantendo o Postgres como fonte de verdade.

Quando migrar para um broker dedicado

Ganhe créditos enquanto constrói
Compartilhe o que criar ou convide amigos e ganhe créditos no Koder.ai.
Ganhar créditos

LISTEN/NOTIFY é ótimo para um sinal rápido “algo mudou”. Não foi construído para ser um sistema de mensageria completo. Quando você começa a depender de eventos como fonte de verdade, está na hora de um broker.

Sinais claros de que você superou o LISTEN/NOTIFY

Se algum destes ocorrer, um broker vai poupar dores:

  • Você precisa de durabilidade: eventos não podem ser perdidos se a aplicação reiniciar ou o banco fizer failover.
  • Você precisa de retries e dead-letter handling: se um consumidor falhar, a mensagem deve ser reenviada e rastreada.
  • Você precisa de consumer groups: múltiplos workers dividem carga sem processar duas vezes.
  • Você precisa de auditoria e replay: “mostre tudo o que aconteceu na última hora” ou “reconstrua essa view a partir dos eventos”.
  • Você precisa de backpressure control: produtores não devem sobrecarregar consumidores lentos.

LISTEN/NOTIFY não armazena mensagens para depois. É um sinal push, não um log persistido. Perfeito para “recarregue este widget do dashboard”, arriscado para “dispare cobrança” ou “envie pacotes”.

O que um broker adiciona (em termos simples)

Um broker oferece um modelo real de fluxo de mensagens: filas (trabalho a ser feito), tópicos (broadcast para muitos), retenção (guardar mensagens por minutos ou dias) e acknowledgments (o consumidor confirma o processamento). Isso separa “o banco mudou” de “tudo que deve acontecer por causa disso”.

Você não precisa escolher a ferramenta mais complexa. Opções comuns são Redis (pub/sub ou streams), NATS, RabbitMQ e Kafka. A escolha depende se você precisa de filas simples, fan-out para muitos serviços ou habilidade de replay de histórico.

Um plano gradual de migração

É possível migrar sem grande refatoração. Um padrão prático é manter NOTIFY como sinal enquanto o broker vira a fonte de entrega.

Comece escrevendo uma “linha de evento” em uma tabela dentro da mesma transação da mudança de negócio e então tenha um worker que publica esse evento no broker. Durante a transição, NOTIFY pode continuar informando a camada de UI para “verificar novos eventos”, enquanto workers de background consomem do broker com retries e auditoria.

Assim dashboards continuam rápidos e workflows críticos param de depender de notificações best-effort.

Próximos passos: lance uma versão pequena e itere com segurança

Escolha uma tela (um tile do dashboard, a contagem de um badge, um toast de “nova notificação”) e conecte tudo ponta a ponta. Com LISTEN/NOTIFY você consegue um resultado útil rapidamente, desde que mantenha o escopo limitado e meça o que acontece com tráfego real.

Comece com o padrão mais simples e confiável: escreva a linha, comite e então emita um pequeno sinal de que algo mudou. Na UI, reaja ao sinal buscando o estado mais recente (ou a fatia necessária). Isso mantém payloads pequenos e evita bugs sutis quando mensagens chegam fora de ordem.

Adicione observabilidade básica cedo. Não precisa de ferramentas sofisticadas para começar, mas precisa de respostas quando o sistema ficar barulhento:

  • Logue reconexões e inícios de subscrição (com motivo)
  • Monitore a taxa de NOTIFY e picos de rajada
  • Monitore a taxa de fetchs disparados por notificações
  • Observe sintomas de “atualização perdida” (usuários precisando atualizar)

Mantenha contratos simples e documentados. Decida nomes de canais, nomes de eventos e o formato do payload (mesmo que seja só um ID). Um curto “catálogo de eventos” no repositório evita divergências.

Se você está construindo rápido e quer manter a stack simples, uma plataforma como Koder.ai (koder.ai) pode ajudar a lançar a primeira versão com UI em React, backend em Go e PostgreSQL, e então iterar conforme suas necessidades ficarem mais claras.

Perguntas frequentes

Para que o PostgreSQL LISTEN/NOTIFY é realmente útil?

Use LISTEN/NOTIFY quando você precisa apenas de um sinal rápido de que algo mudou, como atualizar a contagem de um badge ou um bloco do dashboard. Trate a notificação como um empurrão para refazer a leitura dos dados nas tabelas, não como a fonte de verdade em si.

Por que eu usaria LISTEN/NOTIFY em vez de polling?

O polling verifica mudanças em intervalos definidos, então os usuários geralmente veem atualizações com atraso e seu servidor faz trabalho mesmo quando nada mudou. LISTEN/NOTIFY empurra um pequeno sinal no momento da mudança, o que costuma parecer mais rápido e evita muitas requisições vazias.

O LISTEN/NOTIFY garante entrega?

Não, é best-effort. Se o listener estiver desconectado durante um NOTIFY, ele pode perder o sinal porque as notificações não são armazenadas para reprodução posterior.

O que eu devo colocar no payload do NOTIFY?

Mantenha pequeno e trate como uma dica. Um padrão prático é uma pequena string JSON com type e id, e então sua aplicação consulta o Postgres para o estado atual.

Devo notificar antes ou depois de commitar uma transação?

Um padrão comum é enviar a notificação só depois que a escrita for comitada. Se você notificar cedo demais, o cliente pode acordar e não encontrar a nova linha ainda.

Devo enviar NOTIFY do código da aplicação ou de um trigger?

Código de aplicação costuma ser mais fácil de entender e testar, porque é explícito. Triggers são úteis quando múltiplos escritores tocam a mesma tabela e você quer comportamento consistente independentemente de quem fez a mudança.

Como eu lido com reconexões sem perder atualizações?

Planeje reconexões como comportamento normal. Ao reconectar, execute LISTEN para os canais necessários e faça um refetch rápido do estado recente para cobrir o que você pode ter perdido enquanto esteve offline.

Como levo notificações do banco de dados para o navegador?

Não faça com que cada navegador conecte ao Postgres. Uma configuração típica é uma conexão de listener por instância de backend, que então encaminha eventos para os navegadores via WebSockets ou SSE; a UI refaz o que precisa ao receber o sinal.

Como evito acordar muitos listeners ou sobrecarregar a UI?

Use canais mais estreitos para que só os consumidores relevantes acordem e agrupe rajadas ruidosas. Debounce por algumas centenas de milissegundos e coalesce atualizações duplicadas para evitar que UI e backend travem.

Quando devo parar de usar LISTEN/NOTIFY e migrar para um broker?

Pare de usar quando você precisar de durabilidade, retries, grupos de consumidores, garantias de ordenação ou auditoria/replay. Se perder um evento causar um incidente real (cobrança, envio, workflows irreversíveis), use uma tabela outbox + worker ou um broker dedicado em vez de confiar só no NOTIFY.

Sumário
Qual problema o LISTEN/NOTIFY tenta resolverComo PostgreSQL LISTEN/NOTIFY funciona em termos simplesQuando LISTEN/NOTIFY é suficiente para atualizações em tempo real na UIOnde LISTEN/NOTIFY começa a falharPasso a passo: um padrão simples que funcionaPadrões de escala para dashboards e notificaçõesErros comuns e como evitá-losChecklist rápido: decida se LISTEN/NOTIFY se encaixaExemplo: dashboard em tempo real + notificações para usuáriosQuando migrar para um broker dedicadoPróximos passos: lance uma versão pequena e itere com segurançaPerguntas frequentes
Compartilhar
Koder.ai
Crie seu próprio app com Koder hoje!

A melhor maneira de entender o poder do Koder é experimentar você mesmo.

Comece GrátisAgendar Demo