Sync por cursor vs Change Data Capture: o que sua ferramenta de movimentação de dados está deixando passar
Sync por cursor perde mudanças entre um ciclo e outro. Entenda como Change Data Capture baseado em log resolve isso sem depender de streaming



Toda ferramenta de movimentação de dados promete a mesma coisa: manter seu warehouse atualizado com o que acontece no banco de produção. A forma como isso é feito por baixo dos panos, porém, muda completamente o que você consegue confiar no dado que chega do outro lado.
A maioria das ferramentas do mercado, incluindo versões antigas de conectores que você provavelmente já usou, resolve isso com sync por cursor. Funciona, até parar de funcionar, e o motivo de parar de funcionar quase nunca aparece como erro.
O que é sync por cursor, na prática
A lógica é simples de explicar. A ferramenta olha uma coluna da sua tabela, normalmente algo como updated_at, guarda o maior valor que já viu numa execução anterior (o cursor), e na próxima execução busca só os registros com valor maior que esse. Algo como:
sql
SELECT * FROM leads WHERE updated_at > '2026-07-27 09:00:00'
Encontrou registros, sincroniza e avança o cursor para o novo valor máximo. Não encontrou, não faz nada até o próximo ciclo. É barato de rodar, simples de implementar, e funciona bem numa condição específica: a tabela precisa ter sido desenhada pensando nisso, e o padrão de mudança dos dados precisa ser simples o suficiente pra caber numa única coluna de timestamp.
Na prática, três coisas quebram essa condição com frequência maior do que se imagina.
Furo 1: nem toda tabela tem uma coluna confiável de atualização
Boa parte das tabelas em produção não tem uma coluna de atualização confiável, ou não tem nenhuma. Alguém desenhou aquele schema pensando na aplicação que vive dele, não em quem ia consumir esses dados depois para análise, e isso é absolutamente razoável do ponto de vista de quem construiu o sistema.
O problema aparece do outro lado. Sem uma coluna que marque com segurança quando um registro mudou, não existe forma segura de saber o que sincronizar de forma incremental. A saída que sobra é o full refresh: reler a tabela inteira a cada execução, para garantir que nada ficou de fora. Isso custa tempo de processamento, custo de infraestrutura no destino, e em tabelas grandes pode simplesmente não caber na janela de tempo que você tem disponível. É o motivo mais comum de um time de dados desistir de sincronizar uma tabela específica, não porque ela não importa, mas porque o método escolhido não dá conta dela.
Furo 2: deletes são invisíveis pra sync por cursor
Esse é o furo que menos aparece em discussão, e é estrutural, não uma questão de configuração. Sync por cursor busca registros com updated_at maior que o último valor visto. Um DELETE não atualiza nenhuma coluna, ele remove a linha. Não existe timestamp de "isso foi deletado às 14h32" pra uma query de cursor encontrar, porque a linha simplesmente não está mais lá pra ser lida.
Na prática, isso significa que registros excluídos na origem continuam existindo, para sempre, no seu warehouse, a menos que alguém rode uma reconciliação completa periodicamente, comparando o total de linhas na origem com o destino. Times que não sabem disso descobrem isso do jeito mais caro: um relatório que conta clientes ativos, ou pedidos válidos, incluindo registros que já foram cancelados ou removidos há meses na origem.
Furo 3: estados intermediários somem entre um ciclo e outro, se você precisa de histórico
Mesmo quando a coluna existe, está correta, e não há deletes envolvidos, o cursor só enxerga uma coisa: o estado do registro no exato momento em que a leitura aconteceu. Se esse mesmo registro mudou duas ou três vezes entre uma execução e a próxima, a leitura seguinte só vê o valor mais recente.
Isso importa de verdade quando o seu caso de uso exige modo append no warehouse, guardando cada mudança como uma linha de histórico, não só sobrescrevendo o estado atual (overwrite). Se você só precisa saber o estado mais recente de um registro, sync por cursor ainda entrega isso corretamente. O problema aparece quando você precisa reconstruir a jornada completa, e tudo que aconteceu entre um ciclo e outro simplesmente não fica registrado em lugar nenhum.
Não é um bug, é a mecânica do método. Uma foto tirada a cada ciclo só mostra o que existia na hora da foto. Isso normalmente não aparece como um erro de sistema. Aparece como uma pergunta sem resposta fácil: por que esse número não bate com o que o time de vendas ou de suporte está vendo ao vivo na ferramenta de origem. E quem recebe essa pergunta, na maioria das vezes, não foi quem desenhou o pipeline.
O que muda com Change Data Capture
Change Data Capture (CDC) ataca o problema de um lugar diferente. Em vez de perguntar ao banco "o que mudou desde a última vez", ele lê diretamente o log de transação que o próprio banco já mantém para garantir sua própria integridade e permitir recuperação de falhas. No PostgreSQL, esse log é o WAL, o Write Ahead Log: antes de qualquer alteração ser aplicada nas tabelas, ela é registrada nesse log, em ordem, com o detalhe completo da operação, incluindo inserts, updates e deletes.
Um processo de CDC lê esse log continuamente através de um slot de replicação lógica, e sabe, operação por operação, exatamente o que mudou e quando, sem depender de nenhuma coluna especial na tabela e sem precisar comparar fotos do antes e do depois. Isso resolve os três furos ao mesmo tempo:
Tabelas sem coluna de atualização deixam de ser um bloqueio, porque o método não depende dela.
Deletes deixam de ser invisíveis, porque a remoção também é uma operação registrada no log, não a ausência de uma linha.
Nenhum estado intermediário se perde entre um ciclo e outro, porque cada mudança já foi capturada individualmente no momento em que aconteceu, não inferida por comparação posterior. Isso vale tanto pra quem quer manter só o estado mais recente no warehouse (overwrite) quanto pra quem precisa do histórico completo linha a linha (append).
O que isso exige do seu banco
Vale ser honesto sobre o que entra em troca dessa confiabilidade, porque não é configuração zero.
No PostgreSQL, o wal_level precisa estar como logical, é preciso reservar slots de replicação e remetentes de WAL suficientes (max_replication_slots e max_wal_senders), e as tabelas que você quer capturar precisam ter a replica identity configurada como FULL, para que updates e deletes carreguem o estado completo do registro afetado, não só a chave primária. Isso aumenta o volume de WAL gerado pelo banco, então vale configurar um teto de retenção no slot de replicação (max_slot_wal_keep_size), para que uma falha temporária de conexão não acabe enchendo o disco do banco com WAL acumulado esperando ser consumido.
A conexão inicial também precisa decidir entre dois modos: um snapshot inicial completo da tabela seguido do streaming de mudanças a partir dali (initial), ou começar direto a partir do que está disponível no WAL, sem trazer o histórico anterior à conexão (no_data). A escolha certa depende de você precisar ou não do estado atual completo da tabela como ponto de partida.
Nada disso é motivo pra evitar CDC, mas é motivo pra não tratar como uma troca de configuração trivial. Vale planejar com o time responsável pelo banco antes de habilitar em produção.
CDC não é sinônimo de streaming, e isso é proposital
Existe uma confusão comum no mercado: como CDC captura mudanças em tempo real dentro do banco, muita gente assume que o objetivo final também precisa ser entrega em tempo real, com infraestrutura de streaming completa. Não é bem assim, e vale separar as duas coisas.
Capturar cada mudança no momento em que ela acontece, na origem, é uma questão de completude do dado, não de velocidade de entrega. O que você faz com essas mudanças depois é uma decisão separada, e ela deveria ser guiada por quem realmente consome o dado no fim da linha, não pela capacidade técnica da ferramenta.
Pense em quantos dos seus consumidores de dado realmente processam uma mudança no segundo em que ela chega. Um dashboard de Power BI, por exemplo, atualiza no máximo algumas vezes por dia, e na prática costuma ser olhado com frequência bem menor que isso. Modelos de BI, relatórios executivos e boa parte das análises operacionais funcionam perfeitamente bem em ciclos de minutos, não de milissegundos. Pagar por uma infraestrutura de streaming completa para alimentar algo que só vai ser lido algumas vezes ao dia é pagar por uma complexidade, e um custo, que ninguém no seu time vai efetivamente usar.
A escolha de arquitetura mais sensata para a maioria dos times de dados não é entre rápido e lento. É entre confiável e não confiável. CDC baseado em log resolve a parte de confiabilidade, garantindo que nenhuma mudança se perde na origem, incluindo deletes e estados intermediários. A frequência de entrega para o destino pode, e na maioria dos casos deve, continuar sendo um ciclo previsível e barato, não streaming forçado.
Quando sync por cursor ainda faz sentido
Vale dizer com a mesma honestidade: nem toda tabela precisa de CDC. Se a tabela é pequena, tem uma coluna de atualização confiável, nunca sofre delete físico (só soft delete com uma flag, por exemplo), e você só precisa do estado mais recente (overwrite), sync por cursor continua sendo uma opção mais leve, com menos configuração no banco. CDC compensa exatamente onde cursor falha: tabelas grandes, com deletes reais, com necessidade de histórico completo (append), ou onde a ausência de uma coluna de atualização já é, hoje, o motivo de você não conseguir sincronizar aquela tabela de forma incremental.
Fechamento
Se a sua ferramenta de movimentação de dados atual depende de uma coluna de atualização que sua tabela não tem, se deletes na origem continuam vivos no seu warehouse, ou se você já teve dificuldade em explicar por que um número não batia entre sistemas, o problema provavelmente não está no seu pipeline. Está no método.
O conector PostgreSQL da Erathos já suporta CDC baseado em WAL, tanto em overwrite quanto em append, com o passo a passo completo de configuração, incluindo os ajustes necessários no banco, disponível na documentação técnica: docs.erathos.com/connectors/databases/postgresql
Se preferir testar direto no seu próprio banco antes de decidir, você pode criar uma conta gratuita e conectar uma tabela Postgres com CDC habilitado em poucos minutos, sem cartão de crédito.
Toda ferramenta de movimentação de dados promete a mesma coisa: manter seu warehouse atualizado com o que acontece no banco de produção. A forma como isso é feito por baixo dos panos, porém, muda completamente o que você consegue confiar no dado que chega do outro lado.
A maioria das ferramentas do mercado, incluindo versões antigas de conectores que você provavelmente já usou, resolve isso com sync por cursor. Funciona, até parar de funcionar, e o motivo de parar de funcionar quase nunca aparece como erro.
O que é sync por cursor, na prática
A lógica é simples de explicar. A ferramenta olha uma coluna da sua tabela, normalmente algo como updated_at, guarda o maior valor que já viu numa execução anterior (o cursor), e na próxima execução busca só os registros com valor maior que esse. Algo como:
sql
SELECT * FROM leads WHERE updated_at > '2026-07-27 09:00:00'
Encontrou registros, sincroniza e avança o cursor para o novo valor máximo. Não encontrou, não faz nada até o próximo ciclo. É barato de rodar, simples de implementar, e funciona bem numa condição específica: a tabela precisa ter sido desenhada pensando nisso, e o padrão de mudança dos dados precisa ser simples o suficiente pra caber numa única coluna de timestamp.
Na prática, três coisas quebram essa condição com frequência maior do que se imagina.
Furo 1: nem toda tabela tem uma coluna confiável de atualização
Boa parte das tabelas em produção não tem uma coluna de atualização confiável, ou não tem nenhuma. Alguém desenhou aquele schema pensando na aplicação que vive dele, não em quem ia consumir esses dados depois para análise, e isso é absolutamente razoável do ponto de vista de quem construiu o sistema.
O problema aparece do outro lado. Sem uma coluna que marque com segurança quando um registro mudou, não existe forma segura de saber o que sincronizar de forma incremental. A saída que sobra é o full refresh: reler a tabela inteira a cada execução, para garantir que nada ficou de fora. Isso custa tempo de processamento, custo de infraestrutura no destino, e em tabelas grandes pode simplesmente não caber na janela de tempo que você tem disponível. É o motivo mais comum de um time de dados desistir de sincronizar uma tabela específica, não porque ela não importa, mas porque o método escolhido não dá conta dela.
Furo 2: deletes são invisíveis pra sync por cursor
Esse é o furo que menos aparece em discussão, e é estrutural, não uma questão de configuração. Sync por cursor busca registros com updated_at maior que o último valor visto. Um DELETE não atualiza nenhuma coluna, ele remove a linha. Não existe timestamp de "isso foi deletado às 14h32" pra uma query de cursor encontrar, porque a linha simplesmente não está mais lá pra ser lida.
Na prática, isso significa que registros excluídos na origem continuam existindo, para sempre, no seu warehouse, a menos que alguém rode uma reconciliação completa periodicamente, comparando o total de linhas na origem com o destino. Times que não sabem disso descobrem isso do jeito mais caro: um relatório que conta clientes ativos, ou pedidos válidos, incluindo registros que já foram cancelados ou removidos há meses na origem.
Furo 3: estados intermediários somem entre um ciclo e outro, se você precisa de histórico
Mesmo quando a coluna existe, está correta, e não há deletes envolvidos, o cursor só enxerga uma coisa: o estado do registro no exato momento em que a leitura aconteceu. Se esse mesmo registro mudou duas ou três vezes entre uma execução e a próxima, a leitura seguinte só vê o valor mais recente.
Isso importa de verdade quando o seu caso de uso exige modo append no warehouse, guardando cada mudança como uma linha de histórico, não só sobrescrevendo o estado atual (overwrite). Se você só precisa saber o estado mais recente de um registro, sync por cursor ainda entrega isso corretamente. O problema aparece quando você precisa reconstruir a jornada completa, e tudo que aconteceu entre um ciclo e outro simplesmente não fica registrado em lugar nenhum.
Não é um bug, é a mecânica do método. Uma foto tirada a cada ciclo só mostra o que existia na hora da foto. Isso normalmente não aparece como um erro de sistema. Aparece como uma pergunta sem resposta fácil: por que esse número não bate com o que o time de vendas ou de suporte está vendo ao vivo na ferramenta de origem. E quem recebe essa pergunta, na maioria das vezes, não foi quem desenhou o pipeline.
O que muda com Change Data Capture
Change Data Capture (CDC) ataca o problema de um lugar diferente. Em vez de perguntar ao banco "o que mudou desde a última vez", ele lê diretamente o log de transação que o próprio banco já mantém para garantir sua própria integridade e permitir recuperação de falhas. No PostgreSQL, esse log é o WAL, o Write Ahead Log: antes de qualquer alteração ser aplicada nas tabelas, ela é registrada nesse log, em ordem, com o detalhe completo da operação, incluindo inserts, updates e deletes.
Um processo de CDC lê esse log continuamente através de um slot de replicação lógica, e sabe, operação por operação, exatamente o que mudou e quando, sem depender de nenhuma coluna especial na tabela e sem precisar comparar fotos do antes e do depois. Isso resolve os três furos ao mesmo tempo:
Tabelas sem coluna de atualização deixam de ser um bloqueio, porque o método não depende dela.
Deletes deixam de ser invisíveis, porque a remoção também é uma operação registrada no log, não a ausência de uma linha.
Nenhum estado intermediário se perde entre um ciclo e outro, porque cada mudança já foi capturada individualmente no momento em que aconteceu, não inferida por comparação posterior. Isso vale tanto pra quem quer manter só o estado mais recente no warehouse (overwrite) quanto pra quem precisa do histórico completo linha a linha (append).
O que isso exige do seu banco
Vale ser honesto sobre o que entra em troca dessa confiabilidade, porque não é configuração zero.
No PostgreSQL, o wal_level precisa estar como logical, é preciso reservar slots de replicação e remetentes de WAL suficientes (max_replication_slots e max_wal_senders), e as tabelas que você quer capturar precisam ter a replica identity configurada como FULL, para que updates e deletes carreguem o estado completo do registro afetado, não só a chave primária. Isso aumenta o volume de WAL gerado pelo banco, então vale configurar um teto de retenção no slot de replicação (max_slot_wal_keep_size), para que uma falha temporária de conexão não acabe enchendo o disco do banco com WAL acumulado esperando ser consumido.
A conexão inicial também precisa decidir entre dois modos: um snapshot inicial completo da tabela seguido do streaming de mudanças a partir dali (initial), ou começar direto a partir do que está disponível no WAL, sem trazer o histórico anterior à conexão (no_data). A escolha certa depende de você precisar ou não do estado atual completo da tabela como ponto de partida.
Nada disso é motivo pra evitar CDC, mas é motivo pra não tratar como uma troca de configuração trivial. Vale planejar com o time responsável pelo banco antes de habilitar em produção.
CDC não é sinônimo de streaming, e isso é proposital
Existe uma confusão comum no mercado: como CDC captura mudanças em tempo real dentro do banco, muita gente assume que o objetivo final também precisa ser entrega em tempo real, com infraestrutura de streaming completa. Não é bem assim, e vale separar as duas coisas.
Capturar cada mudança no momento em que ela acontece, na origem, é uma questão de completude do dado, não de velocidade de entrega. O que você faz com essas mudanças depois é uma decisão separada, e ela deveria ser guiada por quem realmente consome o dado no fim da linha, não pela capacidade técnica da ferramenta.
Pense em quantos dos seus consumidores de dado realmente processam uma mudança no segundo em que ela chega. Um dashboard de Power BI, por exemplo, atualiza no máximo algumas vezes por dia, e na prática costuma ser olhado com frequência bem menor que isso. Modelos de BI, relatórios executivos e boa parte das análises operacionais funcionam perfeitamente bem em ciclos de minutos, não de milissegundos. Pagar por uma infraestrutura de streaming completa para alimentar algo que só vai ser lido algumas vezes ao dia é pagar por uma complexidade, e um custo, que ninguém no seu time vai efetivamente usar.
A escolha de arquitetura mais sensata para a maioria dos times de dados não é entre rápido e lento. É entre confiável e não confiável. CDC baseado em log resolve a parte de confiabilidade, garantindo que nenhuma mudança se perde na origem, incluindo deletes e estados intermediários. A frequência de entrega para o destino pode, e na maioria dos casos deve, continuar sendo um ciclo previsível e barato, não streaming forçado.
Quando sync por cursor ainda faz sentido
Vale dizer com a mesma honestidade: nem toda tabela precisa de CDC. Se a tabela é pequena, tem uma coluna de atualização confiável, nunca sofre delete físico (só soft delete com uma flag, por exemplo), e você só precisa do estado mais recente (overwrite), sync por cursor continua sendo uma opção mais leve, com menos configuração no banco. CDC compensa exatamente onde cursor falha: tabelas grandes, com deletes reais, com necessidade de histórico completo (append), ou onde a ausência de uma coluna de atualização já é, hoje, o motivo de você não conseguir sincronizar aquela tabela de forma incremental.
Fechamento
Se a sua ferramenta de movimentação de dados atual depende de uma coluna de atualização que sua tabela não tem, se deletes na origem continuam vivos no seu warehouse, ou se você já teve dificuldade em explicar por que um número não batia entre sistemas, o problema provavelmente não está no seu pipeline. Está no método.
O conector PostgreSQL da Erathos já suporta CDC baseado em WAL, tanto em overwrite quanto em append, com o passo a passo completo de configuração, incluindo os ajustes necessários no banco, disponível na documentação técnica: docs.erathos.com/connectors/databases/postgresql
Se preferir testar direto no seu próprio banco antes de decidir, você pode criar uma conta gratuita e conectar uma tabela Postgres com CDC habilitado em poucos minutos, sem cartão de crédito.
Toda ferramenta de movimentação de dados promete a mesma coisa: manter seu warehouse atualizado com o que acontece no banco de produção. A forma como isso é feito por baixo dos panos, porém, muda completamente o que você consegue confiar no dado que chega do outro lado.
A maioria das ferramentas do mercado, incluindo versões antigas de conectores que você provavelmente já usou, resolve isso com sync por cursor. Funciona, até parar de funcionar, e o motivo de parar de funcionar quase nunca aparece como erro.
O que é sync por cursor, na prática
A lógica é simples de explicar. A ferramenta olha uma coluna da sua tabela, normalmente algo como updated_at, guarda o maior valor que já viu numa execução anterior (o cursor), e na próxima execução busca só os registros com valor maior que esse. Algo como:
sql
SELECT * FROM leads WHERE updated_at > '2026-07-27 09:00:00'
Encontrou registros, sincroniza e avança o cursor para o novo valor máximo. Não encontrou, não faz nada até o próximo ciclo. É barato de rodar, simples de implementar, e funciona bem numa condição específica: a tabela precisa ter sido desenhada pensando nisso, e o padrão de mudança dos dados precisa ser simples o suficiente pra caber numa única coluna de timestamp.
Na prática, três coisas quebram essa condição com frequência maior do que se imagina.
Furo 1: nem toda tabela tem uma coluna confiável de atualização
Boa parte das tabelas em produção não tem uma coluna de atualização confiável, ou não tem nenhuma. Alguém desenhou aquele schema pensando na aplicação que vive dele, não em quem ia consumir esses dados depois para análise, e isso é absolutamente razoável do ponto de vista de quem construiu o sistema.
O problema aparece do outro lado. Sem uma coluna que marque com segurança quando um registro mudou, não existe forma segura de saber o que sincronizar de forma incremental. A saída que sobra é o full refresh: reler a tabela inteira a cada execução, para garantir que nada ficou de fora. Isso custa tempo de processamento, custo de infraestrutura no destino, e em tabelas grandes pode simplesmente não caber na janela de tempo que você tem disponível. É o motivo mais comum de um time de dados desistir de sincronizar uma tabela específica, não porque ela não importa, mas porque o método escolhido não dá conta dela.
Furo 2: deletes são invisíveis pra sync por cursor
Esse é o furo que menos aparece em discussão, e é estrutural, não uma questão de configuração. Sync por cursor busca registros com updated_at maior que o último valor visto. Um DELETE não atualiza nenhuma coluna, ele remove a linha. Não existe timestamp de "isso foi deletado às 14h32" pra uma query de cursor encontrar, porque a linha simplesmente não está mais lá pra ser lida.
Na prática, isso significa que registros excluídos na origem continuam existindo, para sempre, no seu warehouse, a menos que alguém rode uma reconciliação completa periodicamente, comparando o total de linhas na origem com o destino. Times que não sabem disso descobrem isso do jeito mais caro: um relatório que conta clientes ativos, ou pedidos válidos, incluindo registros que já foram cancelados ou removidos há meses na origem.
Furo 3: estados intermediários somem entre um ciclo e outro, se você precisa de histórico
Mesmo quando a coluna existe, está correta, e não há deletes envolvidos, o cursor só enxerga uma coisa: o estado do registro no exato momento em que a leitura aconteceu. Se esse mesmo registro mudou duas ou três vezes entre uma execução e a próxima, a leitura seguinte só vê o valor mais recente.
Isso importa de verdade quando o seu caso de uso exige modo append no warehouse, guardando cada mudança como uma linha de histórico, não só sobrescrevendo o estado atual (overwrite). Se você só precisa saber o estado mais recente de um registro, sync por cursor ainda entrega isso corretamente. O problema aparece quando você precisa reconstruir a jornada completa, e tudo que aconteceu entre um ciclo e outro simplesmente não fica registrado em lugar nenhum.
Não é um bug, é a mecânica do método. Uma foto tirada a cada ciclo só mostra o que existia na hora da foto. Isso normalmente não aparece como um erro de sistema. Aparece como uma pergunta sem resposta fácil: por que esse número não bate com o que o time de vendas ou de suporte está vendo ao vivo na ferramenta de origem. E quem recebe essa pergunta, na maioria das vezes, não foi quem desenhou o pipeline.
O que muda com Change Data Capture
Change Data Capture (CDC) ataca o problema de um lugar diferente. Em vez de perguntar ao banco "o que mudou desde a última vez", ele lê diretamente o log de transação que o próprio banco já mantém para garantir sua própria integridade e permitir recuperação de falhas. No PostgreSQL, esse log é o WAL, o Write Ahead Log: antes de qualquer alteração ser aplicada nas tabelas, ela é registrada nesse log, em ordem, com o detalhe completo da operação, incluindo inserts, updates e deletes.
Um processo de CDC lê esse log continuamente através de um slot de replicação lógica, e sabe, operação por operação, exatamente o que mudou e quando, sem depender de nenhuma coluna especial na tabela e sem precisar comparar fotos do antes e do depois. Isso resolve os três furos ao mesmo tempo:
Tabelas sem coluna de atualização deixam de ser um bloqueio, porque o método não depende dela.
Deletes deixam de ser invisíveis, porque a remoção também é uma operação registrada no log, não a ausência de uma linha.
Nenhum estado intermediário se perde entre um ciclo e outro, porque cada mudança já foi capturada individualmente no momento em que aconteceu, não inferida por comparação posterior. Isso vale tanto pra quem quer manter só o estado mais recente no warehouse (overwrite) quanto pra quem precisa do histórico completo linha a linha (append).
O que isso exige do seu banco
Vale ser honesto sobre o que entra em troca dessa confiabilidade, porque não é configuração zero.
No PostgreSQL, o wal_level precisa estar como logical, é preciso reservar slots de replicação e remetentes de WAL suficientes (max_replication_slots e max_wal_senders), e as tabelas que você quer capturar precisam ter a replica identity configurada como FULL, para que updates e deletes carreguem o estado completo do registro afetado, não só a chave primária. Isso aumenta o volume de WAL gerado pelo banco, então vale configurar um teto de retenção no slot de replicação (max_slot_wal_keep_size), para que uma falha temporária de conexão não acabe enchendo o disco do banco com WAL acumulado esperando ser consumido.
A conexão inicial também precisa decidir entre dois modos: um snapshot inicial completo da tabela seguido do streaming de mudanças a partir dali (initial), ou começar direto a partir do que está disponível no WAL, sem trazer o histórico anterior à conexão (no_data). A escolha certa depende de você precisar ou não do estado atual completo da tabela como ponto de partida.
Nada disso é motivo pra evitar CDC, mas é motivo pra não tratar como uma troca de configuração trivial. Vale planejar com o time responsável pelo banco antes de habilitar em produção.
CDC não é sinônimo de streaming, e isso é proposital
Existe uma confusão comum no mercado: como CDC captura mudanças em tempo real dentro do banco, muita gente assume que o objetivo final também precisa ser entrega em tempo real, com infraestrutura de streaming completa. Não é bem assim, e vale separar as duas coisas.
Capturar cada mudança no momento em que ela acontece, na origem, é uma questão de completude do dado, não de velocidade de entrega. O que você faz com essas mudanças depois é uma decisão separada, e ela deveria ser guiada por quem realmente consome o dado no fim da linha, não pela capacidade técnica da ferramenta.
Pense em quantos dos seus consumidores de dado realmente processam uma mudança no segundo em que ela chega. Um dashboard de Power BI, por exemplo, atualiza no máximo algumas vezes por dia, e na prática costuma ser olhado com frequência bem menor que isso. Modelos de BI, relatórios executivos e boa parte das análises operacionais funcionam perfeitamente bem em ciclos de minutos, não de milissegundos. Pagar por uma infraestrutura de streaming completa para alimentar algo que só vai ser lido algumas vezes ao dia é pagar por uma complexidade, e um custo, que ninguém no seu time vai efetivamente usar.
A escolha de arquitetura mais sensata para a maioria dos times de dados não é entre rápido e lento. É entre confiável e não confiável. CDC baseado em log resolve a parte de confiabilidade, garantindo que nenhuma mudança se perde na origem, incluindo deletes e estados intermediários. A frequência de entrega para o destino pode, e na maioria dos casos deve, continuar sendo um ciclo previsível e barato, não streaming forçado.
Quando sync por cursor ainda faz sentido
Vale dizer com a mesma honestidade: nem toda tabela precisa de CDC. Se a tabela é pequena, tem uma coluna de atualização confiável, nunca sofre delete físico (só soft delete com uma flag, por exemplo), e você só precisa do estado mais recente (overwrite), sync por cursor continua sendo uma opção mais leve, com menos configuração no banco. CDC compensa exatamente onde cursor falha: tabelas grandes, com deletes reais, com necessidade de histórico completo (append), ou onde a ausência de uma coluna de atualização já é, hoje, o motivo de você não conseguir sincronizar aquela tabela de forma incremental.
Fechamento
Se a sua ferramenta de movimentação de dados atual depende de uma coluna de atualização que sua tabela não tem, se deletes na origem continuam vivos no seu warehouse, ou se você já teve dificuldade em explicar por que um número não batia entre sistemas, o problema provavelmente não está no seu pipeline. Está no método.
O conector PostgreSQL da Erathos já suporta CDC baseado em WAL, tanto em overwrite quanto em append, com o passo a passo completo de configuração, incluindo os ajustes necessários no banco, disponível na documentação técnica: docs.erathos.com/connectors/databases/postgresql
Se preferir testar direto no seu próprio banco antes de decidir, você pode criar uma conta gratuita e conectar uma tabela Postgres com CDC habilitado em poucos minutos, sem cartão de crédito.
Toda ferramenta de movimentação de dados promete a mesma coisa: manter seu warehouse atualizado com o que acontece no banco de produção. A forma como isso é feito por baixo dos panos, porém, muda completamente o que você consegue confiar no dado que chega do outro lado.
A maioria das ferramentas do mercado, incluindo versões antigas de conectores que você provavelmente já usou, resolve isso com sync por cursor. Funciona, até parar de funcionar, e o motivo de parar de funcionar quase nunca aparece como erro.
O que é sync por cursor, na prática
A lógica é simples de explicar. A ferramenta olha uma coluna da sua tabela, normalmente algo como updated_at, guarda o maior valor que já viu numa execução anterior (o cursor), e na próxima execução busca só os registros com valor maior que esse. Algo como:
sql
SELECT * FROM leads WHERE updated_at > '2026-07-27 09:00:00'
Encontrou registros, sincroniza e avança o cursor para o novo valor máximo. Não encontrou, não faz nada até o próximo ciclo. É barato de rodar, simples de implementar, e funciona bem numa condição específica: a tabela precisa ter sido desenhada pensando nisso, e o padrão de mudança dos dados precisa ser simples o suficiente pra caber numa única coluna de timestamp.
Na prática, três coisas quebram essa condição com frequência maior do que se imagina.
Furo 1: nem toda tabela tem uma coluna confiável de atualização
Boa parte das tabelas em produção não tem uma coluna de atualização confiável, ou não tem nenhuma. Alguém desenhou aquele schema pensando na aplicação que vive dele, não em quem ia consumir esses dados depois para análise, e isso é absolutamente razoável do ponto de vista de quem construiu o sistema.
O problema aparece do outro lado. Sem uma coluna que marque com segurança quando um registro mudou, não existe forma segura de saber o que sincronizar de forma incremental. A saída que sobra é o full refresh: reler a tabela inteira a cada execução, para garantir que nada ficou de fora. Isso custa tempo de processamento, custo de infraestrutura no destino, e em tabelas grandes pode simplesmente não caber na janela de tempo que você tem disponível. É o motivo mais comum de um time de dados desistir de sincronizar uma tabela específica, não porque ela não importa, mas porque o método escolhido não dá conta dela.
Furo 2: deletes são invisíveis pra sync por cursor
Esse é o furo que menos aparece em discussão, e é estrutural, não uma questão de configuração. Sync por cursor busca registros com updated_at maior que o último valor visto. Um DELETE não atualiza nenhuma coluna, ele remove a linha. Não existe timestamp de "isso foi deletado às 14h32" pra uma query de cursor encontrar, porque a linha simplesmente não está mais lá pra ser lida.
Na prática, isso significa que registros excluídos na origem continuam existindo, para sempre, no seu warehouse, a menos que alguém rode uma reconciliação completa periodicamente, comparando o total de linhas na origem com o destino. Times que não sabem disso descobrem isso do jeito mais caro: um relatório que conta clientes ativos, ou pedidos válidos, incluindo registros que já foram cancelados ou removidos há meses na origem.
Furo 3: estados intermediários somem entre um ciclo e outro, se você precisa de histórico
Mesmo quando a coluna existe, está correta, e não há deletes envolvidos, o cursor só enxerga uma coisa: o estado do registro no exato momento em que a leitura aconteceu. Se esse mesmo registro mudou duas ou três vezes entre uma execução e a próxima, a leitura seguinte só vê o valor mais recente.
Isso importa de verdade quando o seu caso de uso exige modo append no warehouse, guardando cada mudança como uma linha de histórico, não só sobrescrevendo o estado atual (overwrite). Se você só precisa saber o estado mais recente de um registro, sync por cursor ainda entrega isso corretamente. O problema aparece quando você precisa reconstruir a jornada completa, e tudo que aconteceu entre um ciclo e outro simplesmente não fica registrado em lugar nenhum.
Não é um bug, é a mecânica do método. Uma foto tirada a cada ciclo só mostra o que existia na hora da foto. Isso normalmente não aparece como um erro de sistema. Aparece como uma pergunta sem resposta fácil: por que esse número não bate com o que o time de vendas ou de suporte está vendo ao vivo na ferramenta de origem. E quem recebe essa pergunta, na maioria das vezes, não foi quem desenhou o pipeline.
O que muda com Change Data Capture
Change Data Capture (CDC) ataca o problema de um lugar diferente. Em vez de perguntar ao banco "o que mudou desde a última vez", ele lê diretamente o log de transação que o próprio banco já mantém para garantir sua própria integridade e permitir recuperação de falhas. No PostgreSQL, esse log é o WAL, o Write Ahead Log: antes de qualquer alteração ser aplicada nas tabelas, ela é registrada nesse log, em ordem, com o detalhe completo da operação, incluindo inserts, updates e deletes.
Um processo de CDC lê esse log continuamente através de um slot de replicação lógica, e sabe, operação por operação, exatamente o que mudou e quando, sem depender de nenhuma coluna especial na tabela e sem precisar comparar fotos do antes e do depois. Isso resolve os três furos ao mesmo tempo:
Tabelas sem coluna de atualização deixam de ser um bloqueio, porque o método não depende dela.
Deletes deixam de ser invisíveis, porque a remoção também é uma operação registrada no log, não a ausência de uma linha.
Nenhum estado intermediário se perde entre um ciclo e outro, porque cada mudança já foi capturada individualmente no momento em que aconteceu, não inferida por comparação posterior. Isso vale tanto pra quem quer manter só o estado mais recente no warehouse (overwrite) quanto pra quem precisa do histórico completo linha a linha (append).
O que isso exige do seu banco
Vale ser honesto sobre o que entra em troca dessa confiabilidade, porque não é configuração zero.
No PostgreSQL, o wal_level precisa estar como logical, é preciso reservar slots de replicação e remetentes de WAL suficientes (max_replication_slots e max_wal_senders), e as tabelas que você quer capturar precisam ter a replica identity configurada como FULL, para que updates e deletes carreguem o estado completo do registro afetado, não só a chave primária. Isso aumenta o volume de WAL gerado pelo banco, então vale configurar um teto de retenção no slot de replicação (max_slot_wal_keep_size), para que uma falha temporária de conexão não acabe enchendo o disco do banco com WAL acumulado esperando ser consumido.
A conexão inicial também precisa decidir entre dois modos: um snapshot inicial completo da tabela seguido do streaming de mudanças a partir dali (initial), ou começar direto a partir do que está disponível no WAL, sem trazer o histórico anterior à conexão (no_data). A escolha certa depende de você precisar ou não do estado atual completo da tabela como ponto de partida.
Nada disso é motivo pra evitar CDC, mas é motivo pra não tratar como uma troca de configuração trivial. Vale planejar com o time responsável pelo banco antes de habilitar em produção.
CDC não é sinônimo de streaming, e isso é proposital
Existe uma confusão comum no mercado: como CDC captura mudanças em tempo real dentro do banco, muita gente assume que o objetivo final também precisa ser entrega em tempo real, com infraestrutura de streaming completa. Não é bem assim, e vale separar as duas coisas.
Capturar cada mudança no momento em que ela acontece, na origem, é uma questão de completude do dado, não de velocidade de entrega. O que você faz com essas mudanças depois é uma decisão separada, e ela deveria ser guiada por quem realmente consome o dado no fim da linha, não pela capacidade técnica da ferramenta.
Pense em quantos dos seus consumidores de dado realmente processam uma mudança no segundo em que ela chega. Um dashboard de Power BI, por exemplo, atualiza no máximo algumas vezes por dia, e na prática costuma ser olhado com frequência bem menor que isso. Modelos de BI, relatórios executivos e boa parte das análises operacionais funcionam perfeitamente bem em ciclos de minutos, não de milissegundos. Pagar por uma infraestrutura de streaming completa para alimentar algo que só vai ser lido algumas vezes ao dia é pagar por uma complexidade, e um custo, que ninguém no seu time vai efetivamente usar.
A escolha de arquitetura mais sensata para a maioria dos times de dados não é entre rápido e lento. É entre confiável e não confiável. CDC baseado em log resolve a parte de confiabilidade, garantindo que nenhuma mudança se perde na origem, incluindo deletes e estados intermediários. A frequência de entrega para o destino pode, e na maioria dos casos deve, continuar sendo um ciclo previsível e barato, não streaming forçado.
Quando sync por cursor ainda faz sentido
Vale dizer com a mesma honestidade: nem toda tabela precisa de CDC. Se a tabela é pequena, tem uma coluna de atualização confiável, nunca sofre delete físico (só soft delete com uma flag, por exemplo), e você só precisa do estado mais recente (overwrite), sync por cursor continua sendo uma opção mais leve, com menos configuração no banco. CDC compensa exatamente onde cursor falha: tabelas grandes, com deletes reais, com necessidade de histórico completo (append), ou onde a ausência de uma coluna de atualização já é, hoje, o motivo de você não conseguir sincronizar aquela tabela de forma incremental.
Fechamento
Se a sua ferramenta de movimentação de dados atual depende de uma coluna de atualização que sua tabela não tem, se deletes na origem continuam vivos no seu warehouse, ou se você já teve dificuldade em explicar por que um número não batia entre sistemas, o problema provavelmente não está no seu pipeline. Está no método.
O conector PostgreSQL da Erathos já suporta CDC baseado em WAL, tanto em overwrite quanto em append, com o passo a passo completo de configuração, incluindo os ajustes necessários no banco, disponível na documentação técnica: docs.erathos.com/connectors/databases/postgresql
Se preferir testar direto no seu próprio banco antes de decidir, você pode criar uma conta gratuita e conectar uma tabela Postgres com CDC habilitado em poucos minutos, sem cartão de crédito.