O que é o LISTEN/NOTIFY do PostgreSQL
O PostgreSQL tem um recurso pouco conhecido que permite enviar e receber mensagens em tempo real diretamente pelo banco de dados: o LISTEN/NOTIFY. Ele existe desde versões antigas do Postgres, mas ganhou atenção recentemente quando um artigo no Hacker News mostrou que o mecanismo escala muito melhor do que a maioria dos desenvolvedores imagina.
A ideia e simples: um cliente do banco pode se inscrever em um canal (LISTEN canal) e outro cliente pode enviar notificações para esse canal (NOTIFY canal, 'mensagem'). Todos os inscritos recebem a mensagem em tempo real, sem precisar de polling.
Isso abre possibilidades interessantes para sistemas que já usam PostgreSQL como banco principal e querem evitar a complexidade de adicionar Redis, RabbitMQ ou Kafka apenas para casos simples de comunicação entre serviços.
Como funciona por dentro
Quando você executa NOTIFY, o PostgreSQL envia a notificação para todos os backends conectados que estão escutando aquele canal. O mecanismo usa a infraestrutura interna de comunicação entre processos do Postgres, sem criar tabelas temporárias ou gravar nada em disco por padrão.
A notificação carrega três informações: o nome do canal, o payload (uma string de até 8000 bytes) e o PID do processo que enviou. Do lado do listener, o cliente fica com a conexão aberta e aguarda eventos de forma eficiente, sem fazer polling no banco.
O NOTIFY dentro de uma transação só dispara efetivamente quando a transação e comitada. Isso garante consistência: se o INSERT falhar e der rollback, a notificação também não vai.
O payload e uma string simples, mas você pode serializar JSON nela para enviar dados estruturados. Exemplo: NOTIFY pedidos_novos, '{"id": 42, "status": "pago"}'.
Principais recursos e capacidades
O LISTEN/NOTIFY do Postgres oferece vários recursos úteis:
- Pub/sub nativo: sem dependência externa, sem nova infra para gerenciar.
- Múltiplos canais: você pode ter quantos canais quiser, cada um com seus próprios inscritos.
- Payload JSON: até 8000 bytes de dados estruturados por notificação.
- Transacional: notificações só disparam após commit, garantindo consistência de dados.
- Sem persistência por padrão: se nenhum listener estiver conectado no momento do NOTIFY, a mensagem e perdida.
- Escalabilidade testada: o artigo da DBOS demonstrou throughput de dezenas de milhares de notificações por segundo em configurações modernas.
Uma integração muito útil e com os triggers do PostgreSQL: você cria um trigger que chama NOTIFY automaticamente após INSERT ou UPDATE em uma tabela. Assim, qualquer aplicação conectada recebe atualizações em tempo real sem precisar modificar o código que faz a escrita.
Como começar: exemplos práticos de uso
Vamos ver o fluxo básico em SQL e Python. Primeiro instale o driver:
pip install psycopg2-binaryAgora crie um listener em Python:
import psycopg2
import select
import json
conn = psycopg2.connect("host=localhost dbname=meu_banco user=postgres")
conn.set_isolation_level(0) # AUTOCOMMIT obrigatório para LISTEN
cur = conn.cursor()
cur.execute("LISTEN pedidos_novos;")
print("Aguardando notificações...")
while True:
if select.select([conn], [], [], 5) != ([], [], []):
conn.poll()
while conn.notifies:
notify = conn.notifies.pop(0)
dados = json.loads(notify.payload)
print(f"Pedido recebido: {dados}")Para enviar uma notificação de outra conexão ou via SQL:
-- Via psql ou qualquer cliente SQL
NOTIFY pedidos_novos, '{"id": 42, "cliente": "Maria", "valor": 150.00}';Para disparar automaticamente após INSERT, use um trigger:
CREATE OR REPLACE FUNCTION notificar_pedido()
RETURNS trigger AS $$
BEGIN
PERFORM pg_notify(
'pedidos_novos',
json_build_object(
'id', NEW.id,
'status', NEW.status,
'valor', NEW.valor
)::text
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_pedido_insert
AFTER INSERT ON pedidos
FOR EACH ROW EXECUTE FUNCTION notificar_pedido();Exemplo prático completo
Imagine um sistema de e-commerce onde o backend precisa notificar um serviço de envio de emails sempre que um pedido muda de status. Sem LISTEN/NOTIFY, você teria polling ou uma fila separada.
Com o trigger acima, qualquer UPDATE na tabela pedidos dispara um NOTIFY automático. O serviço de email tem uma conexão aberta ouvindo o canal pedidos_novos e processa cada evento assim que chega, sem delay de polling e sem infra extra.
Combine LISTEN/NOTIFY com um worker assíncrono em Python usando asyncpg e asyncio para processar notificações em paralelo sem bloquear a thread principal. Ideal para sistemas com alto volume de eventos.
Comparação com alternativas
vs. Redis Pub/Sub: o Redis e mais rápido em throughput puro e não depende de uma conexão persistente com o banco de dados. Porém, exige uma instância Redis separada, mais configuração e mais custo. Para quem já tem Postgres, LISTEN/NOTIFY elimina essa dependência para casos simples.
vs. RabbitMQ / Kafka: filas completas como RabbitMQ oferecem persistência, retentativas, dead letter queues e muito mais. Para sistemas críticos onde mensagens não podem ser perdidas, ainda são a escolha certa. O LISTEN/NOTIFY não persiste mensagens se nenhum listener estiver conectado.
vs. Polling no banco: qualquer solução baseada em SELECT periódico no banco gera carga desnecessária. O LISTEN/NOTIFY e muito mais eficiente porque usa conexões ociosas e só consome CPU quando ha uma notificação de verdade.
Pontos positivos e limitações
Pontos positivos: zero dependência externa, consistência transacional garantida, simples de implementar, funciona em qualquer linguagem que tenha driver PostgreSQL, e adequado para throughput de dezenas de milhares de mensagens por segundo em hardware moderno.
Limitações reais: mensagens são perdidas se nenhum listener estiver conectado no momento do NOTIFY; o payload e limitado a 8000 bytes; não tem suporte nativo a retentativas, dead letter queue ou prioridade de mensagens; escalar para muitos canais simultâneos pode aumentar overhead de conexões abertas.
Se seu sistema precisa garantir que nenhuma mensagem seja perdida (mesmo que o listener caia e volte depois), use uma fila com persistência como RabbitMQ ou implemente o outbox pattern no próprio Postgres.
Casos de uso reais
Dashboard em tempo real: atualizar contadores e gráficos no frontend sem polling. O servidor web escuta o canal e faz push via WebSocket para o browser quando chega uma notificação.
Cache invalidation: quando um registro e atualizado no banco, notificar todos os servidores de aplicação para limpar o cache daquele item específico. Muito mais eficiente do que TTL fixo.
Coordenação entre workers: em um sistema com múltiplos processos Python processando filas, usar NOTIFY para acordar workers ociosos assim que um novo item e inserido, sem polling.
Monitoramento interno: enviar alertas em tempo real quando métricas críticas ultrapassam limites, tudo via triggers no banco sem precisar de um sistema de monitoramento separado para essa funcionalidade específica.
Dicas e boas práticas
Sempre use AUTOCOMMIT na conexão do listener (conn.set_isolation_level(0) no psycopg2). Uma conexão em transação aberta não recebe notificações enquanto a transação não terminar.
Mantenha o payload pequeno. Em vez de enviar o objeto completo na notificação, envie apenas o ID e deixe o listener buscar os dados completos no banco. Isso evita problemas com o limite de 8000 bytes.
Não use LISTEN/NOTIFY para casos onde a perda de mensagens e inaceitável (pagamentos, eventos financeiros). Para esses cenários, implemente o outbox pattern ou use uma fila com persistência garantida.
Implemente reconexao automática no listener. Conexões de longa duração podem cair por timeout de rede ou reinicio do banco. Use um loop com tratamento de exceção que tenta reconectar com backoff exponencial.
Vale a pena?
Para equipes que já usam PostgreSQL e precisam de comunicação em tempo real entre serviços para casos simples, o LISTEN/NOTIFY e uma boa opcao. Elimina a necessidade de adicionar Redis ou outra infra apenas para pub/sub básico, simplifica o stack e aproveita algo que o banco já oferece.
Não e substituto para Kafka em sistemas de alta disponibilidade com garantia de entrega ou para casos onde as mensagens precisam ser persistidas. Mas para notificações internas, invalidação de cache e coordenação entre workers, funciona muito bem e com muito menos complexidade operacional.
Próximo passo: se você tem um sistema com PostgreSQL e esta pensando em adicionar Redis só para pub/sub, experimente o LISTEN/NOTIFY primeiro. Pode ser que resolva seu problema sem adicionar mais uma peca na sua arquitetura.
Comentários
Deixar um comentárioVocê precisa ter uma conta no CuritibaBlog para comentar.