¿Cómo funciona el Change Data Capture (CDC) con algo como Debezium, y qué problema resuelve?
El CDC convierte cada cambio a nivel de fila que ya ocurre en tu base de datos en un stream de eventos, sin tocar el código de la aplicación ni arriesgarse al problema de la doble escritura.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
¿Cómo funciona el Change Data Capture (CDC) con algo como Debezium, y qué problema resuelve?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#El problema que el CDC resuelve: la doble escritura
Imagina un servicio de pedidos que necesita guardar un pedido en su base de datos y publicar un evento OrderPlaced para que otros servicios puedan reaccionar. La implementación ingenua hace ambas cosas en el código de la aplicación:
save(order) // escritura #1: la base de datospublish(OrderPlaced) // escritura #2: el broker de mensajería
Son dos sistemas separados sin transacción compartida. Si el proceso falla entre ambas llamadas, o el broker no está disponible, terminas con una fila en la base de datos sin su evento correspondiente (un sistema downstream nunca se entera de un pedido real) o —si publicas primero— con un evento de un pedido que en realidad nunca se confirmó. Este es el problema de la doble escritura, y no es un caso extremo raro; ocurre en cada despliegue, cada intermitencia de red, cada caída del broker. El CDC lo resuelve eliminando por completo la segunda escritura: en lugar de que la aplicación publique explícitamente un evento, un proceso separado lee el log de cambios interno de la propia base de datos y convierte cada cambio de fila confirmado en un evento, garantizado a reflejar únicamente lo que realmente se confirmó.
#Cómo funciona Debezium realmente, a nivel mecánico
Debezium no hace polling de tablas con consultas SELECT —eso se perdería los deletes, sería lento y cargaría a la base de datos que intenta observar. En cambio, lee el write-ahead log del propio motor de base de datos: el stream de replicación lógica de PostgreSQL (a través de un replication slot y un plugin de salida como pgoutput), o el binlog de MySQL (en formato basado en filas). Es exactamente el mismo mecanismo que la base de datos ya usa para replicarse a sus propias réplicas de lectura — Debezium simplemente se conecta como otro consumidor de replicación, lo cual significa que capturar cambios agrega una carga insignificante al primario y, algo clave, no puede perderse un cambio confirmado, porque el WAL/binlog es la fuente de verdad de la que la propia base de datos depende para su durabilidad.
Debezium corre como un connector de origen dentro de Kafka Connect. Al iniciar, (opcionalmente) toma un snapshot consistente inicial de los datos existentes en la tabla, y luego cambia a modo streaming: cada insert, update y delete posterior se decodifica del log y se publica en un topic de Kafka, típicamente uno por tabla. Cada evento lleva el estado de la fila antes y después del cambio, el tipo de operación (c = create, u = update, d = delete, r = read/snapshot), y metadata de origen como el ID de transacción y la posición en el log — suficiente para que un consumidor reconstruya exactamente qué pasó sin nunca tener que consultar la base de datos de origen.
#Una configuración real de connector Debezium para PostgreSQL
Este es el cuerpo JSON que harías PUT/POST a la API REST de Kafka Connect para registrar el connector:
{"name": "orders-db-connector","config": {"connector.class": "io.debezium.connector.postgresql.PostgresConnector","database.hostname": "orders-db.internal","database.port": "5432","database.user": "debezium_replicator","database.password": "${file:/opt/kafka/secrets/db-creds.properties:debezium_password}","database.dbname": "orders","topic.prefix": "orders-db","table.include.list": "public.orders,public.order_items,public.outbox_event","plugin.name": "pgoutput","slot.name": "debezium_orders_slot","publication.name": "debezium_orders_pub","snapshot.mode": "initial","heartbeat.interval.ms": "10000","transforms": "outbox","transforms.outbox.type": "io.debezium.transforms.outbox.EventRouter","transforms.outbox.table.field.event.key": "aggregate_id","transforms.outbox.table.field.event.payload": "payload","transforms.outbox.route.by.field": "aggregate_type"}}
Algunos campos que vale la pena explicar en una entrevista: slot.name crea un replication slot durable en PostgreSQL — la base de datos retendrá los segmentos de WAL que el connector aún no ha consumido, lo cual es lo que hace al CDC resiliente a un reinicio del connector, pero también un riesgo operativo real (ver más abajo). table.include.list acota la captura a tablas específicas en lugar de toda la base de datos. snapshot.mode: initial significa que captura las filas existentes una vez al inicio antes de pasar a streaming.
#El patrón outbox: el acompañante de buenas prácticas del CDC
Capturar cambios de tabla en crudo directamente funciona, pero filtra el esquema interno hacia tu contrato de eventos — un consumidor downstream termina acoplado a las columnas exactas de tu tabla orders. El patrón más sólido, visible en la configuración de arriba, es el outbox transaccional: la aplicación escribe su cambio de negocio y una fila que describe el evento (aggregate_id, aggregate_type, payload) en una tabla outbox_event, dentro de la misma transacción de base de datos que la escritura de negocio. Debezium captura específicamente los cambios de esa tabla outbox, y la transformación de mensaje único EventRouter reconfigura cada fila capturada en un evento de dominio limpio, enrutado a un topic nombrado según aggregate_type — así los consumidores ven OrderPlaced en un topic orders, nunca una fila cruda de Postgres.
Esta combinación —una tabla outbox más CDC— te da la confiabilidad de "el evento existe si y solo si la escritura de negocio se confirmó" sin nunca realizar una doble escritura manual en el código de la aplicación, porque la única escritura que hace el código de la aplicación es una única transacción de base de datos local.
#Trampas que vale la pena nombrar en una entrevista
Otras cosas que vale la pena mencionar si te piden profundizar: un snapshot inicial en una tabla muy grande puede tardar horas y agrega carga justo cuando estás levantando un connector nuevo; los cambios de esquema en la tabla de origen (una columna nueva, un cambio de tipo) deben ser manejados por quien consuma el topic, ya que el CDC propaga fielmente la nueva forma de inmediato; y si necesitas orden estricto por entidad downstream, generalmente se particiona el topic de Kafka por la clave primaria de la fila para que todos los cambios de una misma fila caigan en la misma partición.
🧠 Check your understanding
0/3 · 0/3 answered1. Una aplicación escribe una fila en su tabla `orders`, y luego hace una llamada separada para publicar un evento OrderPlaced a un broker de mensajería. ¿Qué modo de falla específico crea este patrón de doble escritura?
2. ¿Por qué Debezium lee el write-ahead log/binlog de la base de datos en lugar de hacer polling periódico de tablas con consultas SELECT?
3. ¿Qué riesgo operativo introduce un replication slot de Debezium en PostgreSQL si el connector está caído durante un periodo largo?