Planteamiento y alcance
Un evento de pedido es producido y consumido por muchos equipos. Una nueva versión agrega un campo opcional, renombra un campo y retira otro; los consumidores no pueden actualizarse al mismo tiempo y los mensajes históricos se reproducen. Diseña compuertas desde la propuesta y las verificaciones de compatibilidad hasta el despliegue canary y el rollback. Explica cómo interactúan los esquemas de escritor/lector de Avro, los modos de Schema Registry y las verificaciones de calidad de datos.
Esto evalúa contratos de datos, evolución de esquemas, despliegues en streaming y gobernanza. Un Schema Registry centraliza versiones y comprobaciones de compatibilidad, pero las reglas exactas varían entre Avro, JSON Schema y Protobuf.
Qué está evaluando el entrevistador
- Si distingues entre compatibilidad backward, forward, full y transitive en lugar de tratar la compatibilidad como un simple valor booleano.
- Si deduces el orden de despliegue a partir de la ventana real de implementación de escritores y lectores.
- Si las verificaciones de registro, los IDs de esquemas en tiempo de ejecución, el retraso (lag) de consumidores y la calidad de datos conforman una única compuerta de liberación.
- Si gestionas renombres, valores predeterminados, eliminaciones, enums y cambios semánticos irreversibles mediante migración y rollback.
Preguntas para clarificar primero
- ¿El formato es Avro, JSON Schema o Protobuf? El parseo y los detalles de compatibilidad difieren.
- ¿Los consumidores son en tiempo real, batch o reproducen historial? ¿Durante cuánto tiempo debe permanecer legible la versión más antigua del escritor?
- ¿Qué lado se despliega primero y existen versiones de cola larga (long-tail) entre regiones o equipos?
- ¿Los subjects se agrupan por equipo, tipo de evento o entorno? ¿La compatibilidad es global o por subject?
- ¿El rollback restaura el productor antiguo, detiene el despliegue o requiere escritura dual de campos antiguos y nuevos?
Estructura de respuesta en 30 segundos
Mapea las versiones a una ventana de consumidores antes de elegir un modo: los nuevos lectores que consumen mensajes antiguos necesitan compatibilidad backward; los lectores antiguos que consumen mensajes nuevos necesitan compatibilidad forward; ambas direcciones sugieren full, mientras que las verificaciones transitivas cubren el historial retenido. Valida cada esquema en una compuerta de registro, luego despliega los lectores antes que los escritores. Asigna valores predeterminados a los campos nuevos, migra renombres con alias o campos duales y espera a que termine la ventana de consumidores antes de eliminarlos. Monitorea rechazos de registro, errores de deserialización, lag, dead letters y calidad de campos; detén la expansión y conserva la versión antigua ante cualquier anomalía.
Respuesta paso a paso
1. Haz explícita la matriz de escritores y lectores
Los mensajes portan o hacen referencia a un esquema de escritor; los consumidores lo resuelven con su esquema de lector. Dibuja las combinaciones de productor antiguo/nuevo contra consumidor antiguo/nuevo y marca cuáles deben funcionar. Una actualización progresiva en streaming normalmente hace que los nuevos consumidores lean datos antiguos antes de que los nuevos productores escriban datos nuevos.
2. Vincula el modo al riesgo
Backward verifica si un lector nuevo puede leer a un escritor antiguo. Forward verifica si un lector antiguo puede leer a un escritor nuevo. Full verifica ambos. Si los consumidores reproducen múltiples versiones históricas, utiliza semántica transitiva o prueba explícitamente el conjunto de versiones retenidas. Nunca asumas que un valor predeterminado de registro es universal para todos los formatos.
3. Define reglas de cambio
Agregar un campo requiere un valor predeterminado seguro cuyo significado de negocio esté verificado. Para un renombre, utiliza un alias soportado por el formato o escribe en paralelo campos antiguos y nuevos antes de migrar a los lectores. Elimina solo después de que expiren el consumidor más antiguo y la ventana de reproducción. Para nuevos valores de enum, prueba cómo manejan los lectores antiguos los valores desconocidos; reducir un tipo o cambiar su significado o unidad es un cambio disruptivo (breaking change).
propose -> lint -> compatibility-check -> consumer-matrix-test
-> register -> canary-producer -> observe -> expand4. Construye compuertas de registro y CI/CD
En el momento del pull request, ejecuta linting del formato, diferencias de esquemas normalizados, verificaciones de compatibilidad a nivel de subject y deserialización contra muestras históricas representativas. Mantén los IDs de esquema y las versiones en el registro; las herramientas de producción deben rechazar registros que eludan la verificación. Administra la compatibilidad por subject cuando sea necesario, con permisos auditados y sin invalidaciones globales no revisadas.
5. Despliega lectores antes que escritores
Aplica canary a los nuevos consumidores que leen el esquema antiguo y observa errores de deserialización y lag. Luego aplica escritura dual o libera el nuevo productor. Retira los consumidores y campos antiguos solo después de la ventana declarada. Los consumidores de cola larga necesitan un responsable, versión, fecha límite y plan de reproducción; asumir que "todos se actualizarán" no es un plano de control.
6. Mide la calidad y haz rollback de forma segura
Rastrea IDs de esquemas desconocidos, fallas de deserialización, volumen de dead-letters, tasa de campos faltantes, anomalías de unidades, lag del consumidor, éxito de reproducción y rechazos de registro. Haz rollback deteniendo el nuevo productor y restaurando un escritor aún compatible. Si la semántica cambió, aísla un nuevo topic o un subject versionado en lugar de interpretar datos antiguos bajo un nuevo significado.
Ejemplo de respuesta de alta calidad
Dibujaría la matriz de despliegue de escritor/lector, identificaría los productores, consumidores y versiones de reproducción más antiguos, y elegiría las reglas para el formato real. Un lector nuevo leyendo mensajes antiguos es backward; un lector antiguo leyendo mensajes nuevos es forward; ambas direcciones requieren full, y el historial retenido puede requerir verificaciones transitivas. Cada cambio pasa por linting, una verificación de registro a nivel de subject y deserialización de muestras históricas.
El orden de despliegue es primero los lectores, luego los escritores: canary de nuevos consumidores, luego escritura dual o cambio de productores, y eliminación de campos antiguos solo después de la ventana de consumidores. Agrega valores predeterminados, migra renombres con alias o campos duales y prueba el comportamiento de clientes antiguos ante eliminaciones y cambios de enum. Observa rechazos de registro, errores de deserialización, lag, dead letters y calidad de campos. Ante una anomalía, detén la expansión y mantén el esquema antiguo, regresando al productor antiguo o enrutando a topics versionados cuando la semántica haya cambiado. La compatibilidad debe verificarse para el formato elegido; los valores predeterminados de un producto no son semánticas compartidas para Avro, JSON Schema y Protobuf.
Modos de falla comunes
- Decir "habilitar backward" sin nombrar al lector y al escritor.
- Renombrar o eliminar un campo directamente, ignorando alias, escrituras duales y reproducción.
- Verificar solo el registro, sin probar con mensajes históricos reales y consumidores antiguos.
- Desplegar primero el nuevo productor y romper los consumidores antiguos.
- Tratar una configuración global de compatibilidad como la política del subject e ignorar permisos o desviaciones (drift).
- Observar únicamente el lag de Kafka, pasando por alto pérdida de campos, cambios de unidades, dead letters y errores de deserialización.
Preguntas de seguimiento y respuestas de referencia
¿Por qué los campos agregados suelen necesitar valores predeterminados?
Los mensajes antiguos no contienen el campo, por lo que un nuevo lector necesita un valor determinista para construir un registro. El valor predeterminado debe ser semánticamente válido en lugar de ocultar un defecto de datos obligatorios con null.
¿Debería cambiarse directamente un campo renombrado?
Normalmente no. Utiliza un alias o escribe los campos antiguos y nuevos juntos, migra a los lectores y elimina el campo antiguo únicamente después de la ventana de reproducción.
¿Cuál es la diferencia de riesgo entre full y full transitive?
Full usualmente verifica ambas direcciones contra la versión adyacente. Full transitive extiende las verificaciones a través de todas las versiones históricas retenidas, haciendo la compuerta más estricta y el costo de actualización más alto.
¿Por qué un esquema registrado aún puede perder datos?
La compatibilidad cubre la resolución estructural, no unidades, significado de enums, calidad de campos, autorización o SQL posterior. Agrega reproducción de muestras, reglas de calidad y observación en tiempo de ejecución.
¿Cómo se establece una condición de salida para consumidores de cola larga?
Registra un responsable, versión, hora del último consumo y necesidad de reproducción; define una fecha límite y genera alertas. Ofrece migración antes de la fecha límite, y luego aísla o rechaza la versión antigua en lugar de debilitar la compatibilidad indefinidamente.
¿Cuándo deberías crear un nuevo subject o topic?
Cuando la semántica, unidades, ciclo de vida o límites de autorización cambiaron de tal forma que la compatibilidad no puede expresar la transición. El aislamiento reduce el riesgo de mala interpretación pero agrega costos de escritura dual, reproducción y gobernanza.