How PostgreSQL CDC Tools Work on YugabyteDB

Bakul Gupta

If you have built on PostgreSQL recently, you almost certainly used a change data capture (CDC) tool. CDC is a clean way to get every insert, update, and delete out of your database the moment it happens and into wherever it needs to go next (a Kafka topic, a data warehouse, a search index, or another database). CDC saves you from writing fragile polling jobs or risky dual writes.

The PostgreSQL community has a wide variety of these tools, most of them open source. YugabyteDB is PostgreSQL-compatible, so do these tools actually work with it, or do you have to start over?

We decided to test this and share our findings with the community. This blog details what happened when we took three popular open-source CDC tools built for PostgreSQL, connected them to YugabyteDB, and ran them under real-life conditions.

The three tools we tested were Sequin, PeerDB, and Supabase ETL.

All three worked with YugabyteDB. Each needed a small, easy-to-execute set of changes, and every one of those changes came back to the same idea. We tested all three CDC tools within a few weeks, and most of that time was dedicated to reading code and writing tests rather than wrestling with the database.

This blog breaks down what we found: why it works, the one concept worth understanding before you get started, and how each tool performed.

Why the Tools Work

YugabyteDB uses PostgreSQL’s own logical replication, the same machinery these tools are built around. So the familiar pieces are all there and behave as you would expect.

You still have replication slots that track where a consumer is in the stream, publications that say which tables and changes you care about, and the usual settings for how much detail each change carries.

That means a connector talks to YugabyteDB in the same way it talks to PostgreSQL. It connects, points at the tables it wants, and changes start arriving. For all three tools, this everyday streaming path worked without any changes to the tools themselves. That is the part people don’t expect, and it is the part that matters most.

Understanding the Virtual WAL

Before running any PostgreSQL CDC tool on YugabyteDB, there is really only one important thing to understand up front: the Virtual WAL.

A normal PostgreSQL server keeps a single running log of every change, called the write-ahead log, or WAL. Each change gets a position in that log, and since there is only one log, you can take any two positions and line them up. Plenty of tools quietly assume exactly this: one log, one set of positions, all comparable.

YugabyteDB is distributed, so it doesn’t keep a single log on a single machine. It spreads each table across many pieces, and each piece keeps its own log. To hand a connector the single, orderly stream it expects, YugabyteDB merges those logs together as it goes. That merged view is the Virtual WAL.

It pulls changes from every piece of the table, puts them back in commit order, and feeds them to the connector the way PostgreSQL would. To the tool, it looks like an ordinary log. Behind the scenes, it is being assembled on the fly.

There is one thing to keep in mind. A position in YugabyteDB’s stream only means something inside that stream. The logical replication docs make this clear: these positions are not global coordinates, so a position from one stream can’t be compared to a position from another.

Almost every small change a tool needs comes from here. A few PostgreSQL helper functions assume that one global log and don’t carry over. This means any logic that compares positions across two separate streams has to be rethought. When a tool truly needs a marker, it can compare across the whole database. YugabyteDB gives you just one.

A couple of other differences stem from YugabyteDB storing data in its own distributed manner rather than the classic single-machine layout. These differences apply to just one of the tools below, and the fixes were minor. If you want the full picture before you start, the key concepts and limitations pages are the two to read.

Below is how each tool performed with YugabyteDB.

Sequin

Sequin is an open-source CDC platform that streams Postgres changes to destinations such as Kafka. We pointed it at YugabyteDB with the standard setup, and both live streaming and the initial backfill came up without any changes to Sequin’s core logic. The changes it required applied to the Virtual WAL and to one or two older PostgreSQL features that YugabyteDB doesn’t support. They are small, and the complete set is in the fork here: https://github.com/yugabyte/yb-sequin.

The clearest read on how close the fit is comes from Sequin’s own test suite, which ships with around 1,300 tests. Before any changes, about 158 of them failed. After the changes, fewer than 20 failed, and only 14 of those are real, expected gaps tied to features that YugabyteDB doesn’t currently support. The rest pass on their own and only trip up when the whole suite runs back-to-back, which is a timing quirk between tests, rather than a database issue. For an off-the-shelf tool meeting YugabyteDB for the first time, this was a result we were happy with.

PeerDB

PeerDB is an open-source, Postgres-first replication and ETL engine. We set YugabyteDB as the source, Redpanda (a Kafka-compatible message broker) as the destination, and built a pipeline between them.

Plain streaming worked as is. The part that needed attention was the very first step, where PeerDB copies the data already sitting in the tables before it starts following live changes. To do that copy quickly, PeerDB reaches for a couple of internal PostgreSQL shortcuts: one for taking a consistent point-in-time snapshot, and one for slicing a big table into chunks it can load in parallel.

Because YugabyteDB stores data in its own way, those particular shortcuts don’t apply. The fix was to use what YugabyteDB offers instead, such as a table’s primary key for splitting the work and YugabyteDB’s own snapshot. After that, changes flowed through to Redpanda, and the pipeline kept up. The full set of changes is in the fork here: https://github.com/yugabyte/yb-peerdb.

Supabase ETL

Supabase ETL is an open-source replication framework, and it was the most interesting of the three. Streaming came up quickly. The real work applies to how it copies existing data and then hands a table over to live replication.

It uses two kinds of workers.

  1. One handles the ongoing flow of changes for the whole pipeline.
  2. The other does the one-time copy for a single table and brings it up to date.

When a table finishes copying, it must pass from the second worker to the first without dropping or reordering any changes.

The two workers used to coordinate that handoff by comparing their positions in the stream. On YugabyteDB, it never finished. This is due to the reason detailed in the Virtual WAL section: the workers read from two separate streams, and a position in one can’t be matched against a position in the other. The position they were waiting for never showed up, so the table sat there forever.

The fix was to coordinate the handoff on something meaningful across the whole database, rather than a position that only makes sense inside one stream. With that change, the handoff completes every time. The full set of changes is in the fork here: https://github.com/yugabyte/yb-etl.

The Pattern

Line up the three tools, and the same shape appears each time. Streaming works as is, and the minor changes each tool requires come back to one idea (and they are small enough to read through in an afternoon).

ToolStreaming worked as isWhat needed a small changeThe changes
SequinYesA couple of older PostgreSQL features and the stream-position modelview fork
PeerDBYesHow existing data is snapshotted and copied in parallelview fork
Supabase ETLYesHow two workers hand a table off mid-streamview fork

We didn’t need to rethink how any of these tools work. The changes were small and contained, and they kept landing in the same place: the gap between one log on one machine and a Virtual WAL stitched together across many.

That is what made the work fast. Most of the surface simply matches PostgreSQL. The places where a distributed database acts differently are few, and you just have to learn them once.

One thing to be clear about: these are proof-of-concept validations, not production-ready integrations. They show that each tool can run against YugabyteDB and what it takes to get there. Before you use them for real traffic, test them against your own schema, your data volumes, and your failure cases, and run each fork through the same review and hardening you would give any other change to your data pipeline. Treat these forks as a solid head start, not a finished product.

What This Means For You

Not every PostgreSQL tool runs untouched on a distributed database. However, the gap is small, predictable, and the same each time.

Because YugabyteDB runs on PostgreSQL’s own logical replication, the tools you already know start streaming with little more than a connection string. The few changes they require come down to the Virtual WAL.

If your team has already settled on PostgreSQL CDC tooling, moving to YugabyteDB looks more like carrying it over than starting from scratch.

Want to know more about CDC and YugabyteDB? Check out these recent blogs.

Bakul Gupta

Related Posts

Explore Distributed SQL and YugabyteDB in Depth

Discover the future of data management.
Learn at Yugabyte University
Get Started
Browse Yugabyte Docs
Explore docs
PostgreSQL For Cloud Native World
Read for Free