CTO no Integrar Plus • Publicado em 18 de September de 2026
Quem já operou um pipeline de replicação lógica entre PostgreSQL e ClickHouse em produção conhece o desespero de acordar de madrugada com o disco do servidor principal quase lotado. Basta um consumidor do Debezium travar por trinta minutos para o slot de replicação segurar todos os arquivos de WAL na máquina de origem. Em servidores próprios, sem a elasticidade infinita de discos em nuvem, isso significa interrupção total do sistema se você não intervir a tempo.
Por causa dessa dor, a ideia por trás do WalShadow chamou tanta atenção nos últimos dias. A proposta é ousada: em vez de usar wal_level = logical e depender do processo de decodificação lógica do PostgreSQL para gerar mensagens compreensíveis, a ferramenta se conecta como se fosse uma réplica física comum, lendo diretamente o fluxo do WAL gerado em wal_level = replica e montando os blocos no ClickHouse com latência abaixo de um segundo.
A discussão técnica que se abriu na comunidade revelou dois pontos de vista bem definidos, e a maioria das pessoas está olhando para o lado errado da moeda.
Por um lado, entusiastas do baixo nível comemoraram a quebra de um dogma. A leitura dominante entre eles é que a replicação física sempre foi o caminho superior por ser mais rápida e consumir menos CPU no banco de origem. Muitos argumentam que decodificação lógica sempre foi uma camada gorda de abstração, pesada demais, e que ler os bytes brutos do WAL só não virou padrão da indústria antes porque decodificar registros físicos era um pesadelo de engenharia que ninguém queria encarar na mão.
Por outro lado, desenvolvedores que operam bancos em plataformas gerenciadas, como Supabase ou instâncias com acesso limitado, trouxeram a objeção prática mais imediata: eles não conseguem usar isso. Para plugar um leitor de replicação física, você precisa de privilégios elevados, acesso irrestrito de rede à porta do PostgreSQL e permissão para abrir conexões de streaming replication. Quem depende de bancos com permissões restritas ou soluções baseadas em polling contínuo fica completamente de fora.
Eu discordo do otimismo do primeiro grupo. Acredito que a visão que comemora a morte do CDC lógico ignora o motivo pelo qual ele foi inventado. Decodificação lógica nunca foi um capricho estético dos desenvolvedores do PostgreSQL; ela existe para proteger quem consome os dados da volatilidade interna do motor de armazenamento.
Quando você decide ler o WAL físico em nível de réplica para alimentar um banco colunar heterogêneo, você ganha velocidade na ingestão, mas assume uma dívida operacional assustadora.
PostgreSQL Primário (wal_level = replica)
│
▼ [Stream binário de páginas e tuplas]
WalShadow / Consumidor Físico
│ (Exige parser de heap, catálogo espelhado e controle de TOAST)
▼
ClickHouse (Tabelas analíticas colunares)
O primeiro choque de realidade acontece no catálogo do banco. Registros físicos de WAL não contêm metadados sobre nomes de colunas, tipos de dados ou alterações de schema. Eles contêm ponteiros para blocos de disco, deslocamentos de bytes e identificadores internos de tuplas. Para transformar um punhado de bytes em uma linha que o ClickHouse entenda, o leitor precisa manter uma réplica exata das tabelas de catálogo do próprio PostgreSQL (pg_class, pg_attribute, pg_type) sincronizada exatamente no mesmo LSN da alteração. Se alguém rodar um ALTER TABLE na sua aplicação Rails durante um deploy na sexta-feira, o seu consumidor precisa saber a ordem exata em que aquela mudança física de esquema ocorreu antes de interpretar os registros seguintes.
O segundo problema, levantado com muita precisão por quem já sofreu com os intestinos do Postgres, é o mecanismo de TOAST. Quando um registro possui colunas grandes, como textos longos ou campos JSONB volumosos, o PostgreSQL fatia esses dados em pedaços e armazena em tabelas auxiliares separadas em disco. Em uma atualização onde esse campo específico não é modificado, o WAL físico sequer toca nos blocos do TOAST; ele apenas grava a tupla da tabela principal com um ponteiro apontando para os blocos antigos.
Na replicação lógica tradicional, se você configura a tabela com identidade de réplica padrão, o consumidor recebe uma notificação de que a coluna não mudou, ou você força REPLICA IDENTITY FULL para que a linha inteira viaje no evento (o que gera um volume cavalar de dados). No modelo físico, o leitor não tem essa escolha: para projetar o registro completo dentro do ClickHouse sem perder dados antigos, ele precisa ou consultar o disco local do banco, ou manter um estado intermediário monstruoso em memória para juntar o passado ao presente.
A tabela abaixo resume os trade-offs práticos entre as duas abordagens quando você gerencia a própria infraestrutura:
| Critério operacional | CDC Lógico tradicional | Replicação via WAL Físico |
|---|---|---|
| Nível de WAL exigido | wal_level = logical |
wal_level = replica |
| Impacto de CPU no primário | Médio a alto (decodificação de catálogo) | Mínimo (envio direto de páginas) |
| Independência de versão | Alta (protocolo agnóstico) | Baixa (acoplado ao layout binário) |
| Fragilidade com migrações | Baixa (DDL não quebra o leitor) | Alta (demanda sincronização de catálogo) |
| Tratamento de dados TOAST | Resolvido pelo motor do Postgres | Exige remontagem manual pelo consumidor |
| Privilégios necessários | Leitura lógica e replication slot | Acesso de replicação física e superuser |
Existe ainda a ilusão de que o WAL físico elimina o risco de espaço em disco. Isso não é verdade. Se o seu serviço de replicação física cair ou não conseguir processar o fluxo na velocidade das escritas, o slot de replicação física vai reter os arquivos no diretório pg_wal com a mesma frieza que um slot lógico faria. Se você configurar limites estritos de retenção com max_slot_wal_keep_size para salvar o disco da sua VPS, o PostgreSQL vai simplesmente invalidar o slot no momento do pico de tráfego, forçando você a reconstruir toda a tabela analítica do zero.
Eu opero sistemas onde o PostgreSQL vive em servidores dedicados e nós precisamos de consultas analíticas pesadas rodando no ClickHouse sem derrubar o banco de produção. Nós já sofremos com o consumo de CPU da decodificação lógica quando o volume de escrita dispara, e sei como é tentador querer contornar essa camada.
Ainda assim, trocar o isolamento semântico do CDC lógico pela fragilidade binária do WAL físico é uma troca perigosa para a maioria das empresas. Se o seu sistema consiste quase exclusivamente em inserções puras de séries temporais ou eventos de auditoria, onde não existem mutações em registros grandes e o schema quase nunca muda, essa abordagem pode entregar um ganho de vazão brutal.
Mas se você roda uma aplicação web típica, com atualizações frequentes em tabelas largas, migrações regulares de banco e colunas JSONB, o ganho de latência de quinhentos milissegundos não compensa o custo de manter um parser de páginas de disco rodando na sua tubulação de dados. Para mim, a estabilidade de saber que uma atualização de versão secundária do PostgreSQL não vai corromper o meu pipeline analítico ainda vale cada ciclo de CPU gasto pela replicação lógica.
CTO e arquiteto full stack especializado em Ruby on Rails, Vue.js, sistemas multi-tenant e infraestrutura distribuída.