Ask most data platform teams how to stream changes out of a relational database, and you'll hear one answer: run Debezium on Kafka Connect, publish every change to Kafka, and let downstream jobs read the topics. It's the default for good reason, and we ran it ourselves.
But when we set out to bring every operational database behind our products into an Apache Iceberg lakehouse, we landed on two less common choices:
Flink as the capture layer. With Flink CDC, Flink reads the database binlog itself, so capture, filtering, and writing happen in one job, and dropping changes nobody needs becomes a one-line rule.
Iceberg as the Kafka substrate. Each source table streams into an append-only Iceberg table that downstream jobs read incrementally, the way a consumer reads a topic. The lake is both the log and the destination.
Why the default strained
For multiple security & operational features that Druva offers to its customers, we had a requirement to ingest multiple databases sitting inside dozens plus of RDS & Auroras, some of which were operating at a scale of ~1000 TPS.
Some of these operational databases rewrite the same rows constantly. Picture a backup_jobs table: while a job is RUNNING, the backup agent updates bytes_transferred and progress_pct every few seconds, yet for analytics only two moments matter: when the job was queued and how it ended. Debezium faithfully emits every update. Kafka stores every one. Every consumer reads them all and throws most away.
This is not a rounding error. Across millions of protected devices, backup-job tables alone produce about a billion change events a day, most of them progress updates. Keeping only the moments that matter cuts that to roughly one in ten.
At our scale, that showed up in three ways: Kafka capacity grew with churn rather than value; filtering happened only after the noise was stored; and our scheduled Spark consumers had to run so often to keep up that one run could still be running when the next was due.
The shape of the pipeline
Flink owns getting changes in; Spark owns turning them into tables people query. An append-only changelog table is how both connect.