Arquitectura Interna

Diseño de Particiones

Cada topic del pipeline CDC está configurado con 3 particiones y 3 réplicas:

apiVersion: kafka.strimzi.io/v1beta2
kind: KafkaTopic
metadata:
  name: cdc.public.customers
  namespace: kafka-cdc
  labels:
    strimzi.io/cluster: cdc-cluster
spec:
  partitions: 3
  replicas: 3
  config:
    retention.ms: 604800000
    cleanup.policy: compact

¿Por qué 3 particiones?

Factor Decisión

Paralelismo

3 particiones permiten hasta 3 consumidores simultáneos en un consumer group

Replicación

Con RF=3 y min.insync.replicas=2, se tolera la pérdida de 1 broker sin perder datos

Orden

Debezium usa la primary key como partition key, garantizando orden por registro

Escalabilidad

Se puede aumentar particiones sin downtime (solo se agregan, nunca se eliminan)

Partition Key en CDC

Debezium usa el primary key del registro como partition key. Esto garantiza que todos los cambios de un mismo registro van a la misma partición, preservando el orden:

Registro con id=1  → Partition 0
Registro con id=2  → Partition 1
Registro con id=3  → Partition 2
Registro con id=4  → Partition 0 (hash wraps)

Consumer Groups y Escalabilidad

El pipeline tiene dos consumer groups principales:

Consumer Group Componente Réplicas

cdc-connect-cluster

KafkaConnect (Debezium + HTTP Sink)

2

camel-cdc-consumer

Apache Camel CDC Processor

2

Balanceo de particiones

Con 3 particiones y 2 consumidores:

Consumer 0: Partition 0, Partition 1
Consumer 1: Partition 2

Si un consumidor cae, Kafka rebalancea automáticamente:

Consumer 1: Partition 0, Partition 1, Partition 2

Al escalar a 3 réplicas, cada consumidor procesa exactamente 1 partición (distribución óptima).

Manejo de Offsets y Checkpoints

Offsets de Kafka

Cada consumer group mantiene su posición (offset) por partición en el topic interno __consumer_offsets:

oc exec -it cdc-cluster-kafka-0 -n kafka-cdc -- bin/kafka-consumer-groups.sh \
  --bootstrap-server localhost:9092 \
  --describe --group camel-cdc-consumer

Checkpoints de Debezium

Debezium mantiene su propio checkpoint via el replication slot de PostgreSQL y los offset topics de KafkaConnect:

config:
  offset.storage.topic: cdc-connect-offsets
  config.storage.topic: cdc-connect-configs
  status.storage.topic: cdc-connect-status
  offset.storage.replication.factor: 3
  config.storage.replication.factor: 3
  status.storage.replication.factor: 3
  • cdc-connect-offsets — posición actual en el WAL de PostgreSQL

  • cdc-connect-configs — configuración de los connectors

  • cdc-connect-status — estado de las tasks

Los 3 topics tienen replication.factor: 3 para sobrevivir la pérdida de un broker.

Replication Slot de PostgreSQL

Debezium crea un replication slot para no perder cambios si el connector se reinicia:

config:
  slot.name: debezium_cdc
  publication.name: cdc_publication
  snapshot.mode: initial
  • slot.name — nombre del slot lógico en PostgreSQL

  • snapshot.mode: initial — al iniciar por primera vez, Debezium captura un snapshot completo de las tablas

Para verificar el slot:

oc exec -it deploy/cdc-postgresql -n kafka-cdc -- psql -U cdcuser -d cdcdb -c \
  "SELECT slot_name, plugin, slot_type, active FROM pg_replication_slots;"

Retención y Reprocesamiento

Políticas de retención

Topic Retención Cleanup Policy

cdc.public.customers

7 días (604800000 ms)

compact

cdc.public.orders

7 días (604800000 ms)

compact

dlq.cdc-errors

30 días (2592000000 ms)

delete

dlq.cdc-camel-errors

30 días (2592000000 ms)

delete

¿Qué es cleanup.policy: compact?

Con compact, Kafka retiene solo el último valor para cada key, eliminando versiones anteriores. Para CDC:

  • Se preserva el estado actual de cada registro

  • Se eliminan updates intermedios después de la compactación

  • Permite reconstruir el estado completo de una tabla consumiendo el topic desde el inicio

Reprocesamiento

Para reprocesar eventos, se puede resetear el offset del consumer group:

oc exec -it cdc-cluster-kafka-0 -n kafka-cdc -- bin/kafka-consumer-groups.sh \
  --bootstrap-server localhost:9092 \
  --group camel-cdc-consumer \
  --topic cdc.public.customers \
  --reset-offsets --to-earliest \
  --execute
Esto causa que todos los mensajes del topic se reprocesen. Usar con precaución en producción.

Configuración de Replicación

El cluster Kafka tiene los siguientes parámetros de replicación para garantizar durabilidad:

config:
  offsets.topic.replication.factor: 3
  transaction.state.log.replication.factor: 3
  transaction.state.log.min.isr: 2
  default.replication.factor: 3
  min.insync.replicas: 2
  • min.insync.replicas: 2 — un write es confirmado solo si 2 de las 3 réplicas lo persisten

  • default.replication.factor: 3 — todos los topics nuevos se crean con RF=3

Esto significa que el cluster puede tolerar la pérdida de 1 broker sin perder datos ni disponibilidad.

How it Works

Escritura en Kafka: de producer a disco

Cuando Debezium (o cualquier producer) envía un mensaje a Kafka, internamente ocurre:

  1. El producer serializa el key y value, calcula hash(key) % numPartitions para determinar la partición destino.

  2. El producer agrupa mensajes en batches por partición (configurable via batch.size y linger.ms) para optimizar I/O.

  3. El batch se envía al broker leader de la partición via TCP.

  4. El leader escribe el batch en un segment file en disco — un append-only log. Los segments se rotan al llegar a 1GB (configurable).

  5. Cada mensaje recibe un offset secuencial: un entero de 64 bits, monotónicamente creciente, único dentro de la partición.

  6. Los followers fetch el batch del leader, lo escriben en sus propios segments, y envían un ACK al leader.

  7. Con acks=all (default en Strimzi) y min.insync.replicas=2, el leader solo confirma al producer cuando al menos 2 réplicas persisten el dato.

Lectura en Kafka: consumer groups

  1. Cada consumer del grupo se conecta al group coordinator (un broker elegido para gestionar el grupo).

  2. El coordinator ejecuta un partition assignment protocol (range o round-robin) distribuyendo las particiones entre los consumers.

  3. Cada consumer hace poll() periódicamente al leader de sus particiones asignadas, recibiendo batches de mensajes desde su último offset.

  4. El consumer procesa los mensajes y luego commitea el offset al topic interno __consumer_offsets.

  5. Si un consumer se desconecta (no envía heartbeat en session.timeout.ms), el coordinator dispara un rebalance: las particiones huérfanas se reasignan a los consumers restantes.

Compactación: cleanup.policy=compact

Para topics CDC con cleanup.policy: compact:

  1. Kafka ejecuta periódicamente un log cleaner que escanea los segments cerrados.

  2. Para cada key, retiene solo el mensaje con el mayor offset (el más reciente).

  3. Los mensajes con key duplicada y offset menor se eliminan físicamente del disco.

  4. Un mensaje con value null (tombstone) marca el key para eliminación completa después del periodo delete.retention.ms.

  5. Resultado: el topic contiene exactamente un mensaje por key — el estado actual de cada registro de la base de datos.

Network Policies

El namespace kafka-cdc está protegido con NetworkPolicy que restringe el tráfico de ingress:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: kafka-cdc-allow-internal
  namespace: kafka-cdc
spec:
  podSelector: {}
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kafka-cdc
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: openshift-cluster-observability-operator
      ports:
        - protocol: TCP
          port: 9404
  • Solo pods dentro de kafka-cdc pueden comunicarse entre sí

  • El namespace de observabilidad puede acceder al puerto de métricas (9404) para scraping de Prometheus

Pod Disruption Budgets

Los PDBs garantizan que las operaciones de mantenimiento (drains, upgrades) no dejen el pipeline sin capacidad:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: kafka-brokers-pdb
  namespace: kafka-cdc
spec:
  minAvailable: 2
  selector:
    matchLabels:
      strimzi.io/cluster: cdc-cluster
      strimzi.io/kind: Kafka
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: kafka-connect-pdb
  namespace: kafka-cdc
spec:
  minAvailable: 1
  selector:
    matchLabels:
      strimzi.io/cluster: cdc-connect
      strimzi.io/kind: KafkaConnect
  • kafka-brokers-pdb — siempre hay al menos 2 brokers de los 3 disponibles

  • kafka-connect-pdb — siempre hay al menos 1 worker de Connect de los 2 disponibles

Documentación Oficial