Capa de interoperabilidad · Prueba técnica de arquitectura

ADR-034: Kafka en pod y Confluent Cloud siempre arriba, con un cambio rápido entre ellos

aceptado el 15/11/2026. Requisito de Andrés (15/11/2026, 14:15 hora de Colombia): "kafka debe estar funcionando siempre en los dos ambientes: pod y en confluent. El switch debe ser algo muy rápido cuando sea necesario como en este momento." Escrito por el carril `nube`. Complementa ADR-028 y no lo reemplaza: sigue habiendo dos modos y un interruptor, `KAFKA_MODE`; cambia qué hace el interruptor.

Contexto

ADR-028 dejó dos modos de Kafka: un Kafka de un nodo en un pod del clúster y Confluent Cloud. Con KAFKA_MODE se elegía cuál instalar. Pasar a Confluent desinstalaba el Kafka del pod con su disco; volver lo instalaba vacío y creaba los tópicos. Cada cambio era un make k8s-eks-deploy completo: el del 15/11/2026 de vuelta al pod tardó unos 15 minutos de reloj (tiempo exacto por medir).

El 15/11/2026, con la capa en Confluent, apareció un defecto de código: los consumidores de wms-adapter y oms-adapter se crean sin SASL_SSL. Contra Confluent no consumen; en el pod no se nota, porque ahí no hay TLS (platform/k8s/README.md, "Defectos y pendientes conocidos"). Resultado: stock stale y órdenes confirmadas sin registrar en el OMS. Volver al pod como respaldo costó un redespliegue completo, con el Kafka del pod reinstalado vacío. Un respaldo que tarda varios minutos y arranca vacío no sirve en medio de un demo.

Opciones

Opción A favor En contra
A. Como ADR-028: un Kafka a la vez, el cambio reinstala Menos memoria y menos costo en el modo pod Un despliegue completo por cambio (unos 15 minutos de reloj el 15/11/2026, sin medición exacta); el Kafka que vuelve arranca vacío; el respaldo está frío
B. Los dos siempre arriba; KAFKA_MODE solo decide a cuál se conectan las cargas; una orden cambia la conexión El respaldo está caliente, con sus tópicos; el cambio toca solo las cargas con Kafka, en paralelo Memoria del Kafka del pod siempre ocupada; Confluent cobra aunque no se use; dos Kafka que vigilar
C. Replicar entre los dos (MirrorMaker 2 o Cluster Linking) El cambio no pierde mensajes ni offsets Otra pieza que operar; Cluster Linking no existe en Confluent Basic; demasiado para el demo

Decisión

Opción B.

  1. Dos Kafka arriba. La release kafka se instala siempre, en kind y en EKS, con sus tópicos desde el contrato (factor 1). La raíz kafka mantiene Confluent encendido. La salida de la malla hacia Confluent (optional/egress-confluent.yaml) queda siempre aplicada en EKS. make k8s-eks-deploy ya no desinstala nada al pasar a confluent.
  2. El interruptor solo decide la conexión. KAFKA_MODE=pod: sin SASL a kafka.data.svc.cluster.local:9092. KAFKA_MODE=confluent: SASL_SSL con la llave de cada carga (<carga>-kafka). Igual que en ADR-028.
  3. Cambio rápido. make kafka-switch MODE=pod|confluent (platform/k8s/scripts/k8s.sh kafka-switch eks) hace un helm upgrade en paralelo solo de las releases con Kafka (secrets.kafka: true en sus valores; hoy son ocho: los cinco servicios y los tres adaptadores). Cada una cambia su ConfigMap y el montaje de <carga>-kafka, y su Deployment se reinicia de forma ordenada. No instala ni desinstala nada y no toca OpenTofu. La dirección de Confluent sale del secreto de una carga y el registro de imágenes, de la cuenta: la orden no necesita más argumentos. Al terminar imprime cuánto tardó.
  4. Drenar antes de salir del pod. Saliendo del pod, la orden espera a que los grupos de consumidores no tengan atraso (kafka-consumer-groups.sh --describe --all-groups, columna LAG) antes de reconfigurar. Por defecto espera hasta 120 segundos (DRAIN_TIMEOUT). DRAIN=off cambia igual; por ejemplo, cuando el Kafka de origen está roto. Saliendo de Confluent no se mide el atraso desde el clúster: se mira en la consola de Confluent (usuario de solo lectura, rol Operator) antes de cambiar.

Qué pasa con una orden a medio camino

Los mensajes y los offsets no se migran entre los dos Kafka.

Cómo se cambia sin perder una orden: se detiene la entrada nueva unos segundos (sin compras en la tienda ni carga de k6) y se deja que el drenaje del paso 4 lleve el atraso a cero. Recién ahí se reconfigura. Con atraso cero y el outbox en la base, ningún evento queda varado.

Qué se sacrifica

Verificación