Saltar al contenido

lección 2

Día 1 — Architecture Review: diseñar streaming

Evalúas 4 opciones de streaming, escribes un ADR completo con trade-offs y lo defiendes ante Clara en el Architecture Review Board.

45 min

### Martes, 9:15 AM — La pizarra de Alberto

Alberto te lleva a la sala Turing. La pizarra está llena de diagramas a medio borrar de la sesión anterior. Coge un rotulador azul y dibuja el pipeline actual: un DAG de Airflow ("txn_daily_load") que a las 4:00 AM extrae las transacciones del día anterior desde el topic de Kafka hacia S3 raw, las transforma con PySpark en EMR (limpieza + enriquecimiento + deduplicación) y las carga en Redshift gold. Latencia end-to-end: entre 4 y 28 horas dependiendo de si el job falla y hay que relanzarlo manualmente.

Alberto subraya dos números en rojo: "<5 min" y "<15 min". "El negocio necesita dos cosas", dice mientras golpea la pizarra. "Primera: fraude en menos de 5 minutos. El modelo de Miguel necesita features frescas — si tienen más de 5 minutos, la precisión baja del 92% al 67%. Con los 2.357 fraudes reales que detectamos al mes, eso son casi 600 fraudes que se nos escapan, unos 1,9 millones de euros. Segunda: alertas regulatorias en menos de 15 minutos. El Banco de España nos exige notificación inmediata de transacciones sospechosas por encima de 10.000 euros. Inmediata significa menos de 15 minutos, no al día siguiente."

1Alberto Reyes (pizarra, rotulador rojo):
2"No me traigas la solución. Tráeme 3 opciones con trade-offs
3claros, coste de cada una, y tu recomendación justificada. Si no
4puedes defender tu decisión ante Clara en 5 minutos, no está lista.
5
6El ADR debe estar escrito ANTES del Architecture Review del jueves.
7Clara lo va a leer. Si tiene preguntas que no puedes responder,
8te manda de vuelta. Y el jueves es el jueves -- no hay segunda
9oportunidad esta semana."

Alberto establece las reglas del ADR

### Evaluación de opciones

Te sientas en tu escritorio con un café doble y abres un documento en blanco. Necesitas evaluar las opciones viables para consumir del topic transactions.completed de Kafka MSK y reducir la latencia de horas a minutos. Después de investigar 2 horas, identificas 4 candidatos serios:

  1. 01.Spark Structured Streaming (EMR): Aprovechar la infraestructura existente de Spark. Micro-batch cada 30s-1min. Soporta watermarks para late data, aggregations con ventanas, y exactly-once con checkpoints en S3. El equipo ya conoce PySpark.
  2. 02.Kafka Connect + S3 Sink: Conector managed que escribe directamente de Kafka a S3. Simple de configurar, bajo mantenimiento. Pero sin transformación inline — necesitaría un paso adicional de procesamiento (Lambda o segundo job).
  3. 03.Consumer Group nativo (Python con confluent-kafka): Escribir un consumer con la librería de Confluent. Máximo control, mínima latencia (<10s). Pero máximo mantenimiento: gestión manual de offsets, errores, escalado, rebalanceo de particiones.
  4. 04.Amazon Kinesis Data Analytics (Flink managed): Servicio 100% managed de AWS para stream processing. Cero operación, auto-scaling. Pero coste alto, lock-in fuerte con AWS, y el equipo no conoce Flink SQL.

Analizas cada opción contra 5 criterios que Alberto siempre usa: latencia alcanzable, complejidad operativa, coste mensual estimado, integración con el stack existente, y escalabilidad a 10x volumen actual (preparación para Black Friday y campañas de Navidad).

1# Evaluación cuantitativa de las 4 opciones
2# Criterios ponderados según prioridades de NeoBank
3
4opciones = {
5 "Spark Structured Streaming": {
6 "latencia_seg": 45, # Micro-batch cada 30s + processing
7 "complejidad_ops": 3, # Media (ya tenemos EMR)
8 "coste_mensual_eur": 800, # Cluster EMR siempre encendido
9 "integración": 5, # Perfecto: ya usamos PySpark
10 "escalabilidad_10x": 4, # Auto-scaling de executors
11 "exactly_once": True, # Checkpoints en S3
12 "equipo_conoce": True, # 8 jobs batch existentes
13 },
14 "Kafka Connect S3 Sink": {
15 "latencia_seg": 180, # flush.size + rotate.interval
16 "complejidad_ops": 2, # Baja (managed connector)
17 "coste_mensual_eur": 400, # Solo connector + S3
18 "integración": 3, # Necesita pasó adicional
19 "escalabilidad_10x": 3, # Limitado por flush config
20 "exactly_once": False, # At-least-once nativo
21 "equipo_conoce": False, # Nadie ha usado Connect
22 },
23 "Consumer Python nativo": {
24 "latencia_seg": 8, # Procesamiento inmediato
25 "complejidad_ops": 5, # Alta (todo manual)
26 "coste_mensual_eur": 300, # ECS Fargate
27 "integración": 4, # Python, compatible
28 "escalabilidad_10x": 2, # Manual, frágil
29 "exactly_once": False, # Requiere implementación manual
30 "equipo_conoce": True, # Python sí, pero no a este nivel
31 },
32 "Kinesis Data Analytics": {
33 "latencia_seg": 45, # Similar a Spark micro-batch
34 "complejidad_ops": 1, # Zero ops (managed)
35 "coste_mensual_eur": 1500, # KPU pricing es caro
36 "integración": 2, # Flink SQL != PySpark
37 "escalabilidad_10x": 5, # Auto-scaling nativo
38 "exactly_once": True, # Built-in
39 "equipo_conoce": False, # Nadie conoce Flink
40 },
41}
42
43# Pesos de cada criterio (según prioridades de Alberto/Clara)
44pesos = {
45 "latencia": 0.25, # SLA es crítico
46 "complejidad": 0.20, # Menos ops = más tiempo para features
47 "coste": 0.15, # Presupuesto limitado pero no bloqueante
48 "integración": 0.25, # Reutilizar stack reduce riesgo
49 "escalabilidad": 0.15, # Importante pero a medio plazo
50}
51
52print("╔══════════════════════════════════════════════════════════════╗")
53print("║ EVALUACIÓN DE OPCIONES -- STREAMING NEOBANK ║")
54print("╠══════════════════════════════════════════════════════════════╣")
55for nombre, datos in opciones.items():
56 cumple_sla = datos["latencia_seg"] < 300 # 5 min = 300 seg
57 print(f"║ {nombre:35s} ║")
58 print(f"║ Latencia: {datos['latencia_seg']:>3}s | Coste: {datos['coste_mensual_eur']:>4} EUR/mes ║")
59 print(f"║ Exactly-once: {'Sí' if datos['exactly_once'] else 'No':2s} | SLA: {'✓ CUMPLE' if cumple_sla else '✗ FALLA'} ║")
60 print(f"║ Equipo conoce: {'Sí' if datos['equipo_conoce'] else 'No'} ║")
61 print("╠══════════════════════════════════════════════════════════════╣")
62print("║ RECOMENDACIÓN: Spark Structured Streaming ║")
63print("╚══════════════════════════════════════════════════════════════╝")

Evaluación cuantitativa de las 4 opciones de streaming

### El ADR — Architecture Decision Record

Con la evaluación clara, escribes el ADR. Alberto tiene un template estándar que todo el equipo usa — basado en el formato de Michael Nygard pero adaptado a NeoBank con una sección de "Impacto regulatorio" que no existe en ADRs estándar.

1# ADR-007: Migración a Near-Real-Time para Pipeline de Transacciones
2
3## Estado
4Propuesto (pendiente aprobación ARB 2024-09-19)
5
6## Contexto
7El pipeline batch actual (Airflow DAG "txn_daily_load") procesa
8transacciones con latencia de 4-28h. Los SLAs de negocio exigen:
9- Modelo de fraude: features con frescura <5 min (impacto: 1.9M EUR/mes)
10- Alertas regulatorias BdE: notificación de txns >10K EUR en <15 min
11- Feature store: alimentar 5 features para scoring de fraude
12
13Topic Kafka: transactions.completed
14Volumen actual: ~4.5M eventos/día (~52 eventos/seg, picos 200/s)
15Formato: Avro con Schema Registry (schema v3.2)
16Retención Kafka: 7 días
17
18## Opciones evaluadas
19
20La comparación de las 4 opciones frente a los criterios clave está
21en la tabla siguiente (la columna elegida aparece resaltada).

ADR-007 (parte 1): estado, contexto y opciones sobre la mesa

Spark Str. = Spark Streaming · Kafka Conn. = Kafka Connect · Consumer Py = Consumer Python · Kinesis An. = Kinesis Analytics. La columna resaltada (Spark) es la opción elegida.

### Qué es exactly-once y por qué en banca no es opcional

Antes de seguir, conviene tener claro qué significa la fila de «exactly-once» de la tabla, porque es la que decide la comparación.

Cuando un sistema lee eventos de una cola y algo falla a mitad — se cae el proceso, se corta la red, el clúster se reinicia — hay tres comportamientos posibles al recuperarse:

  • At-most-once: procesa cada evento como mucho una vez. Si falla, ese evento se pierde. Rápido y sin duplicados, pero pierde datos. En banca, inaceptable.
  • At-least-once: no pierde nada, pero al recuperarse puede volver a procesar lo que ya había procesado. Es lo que da Kafka Connect por defecto. En un almacén de métricas se tolera; aquí significa que una transferencia puede aparecer dos veces.
  • Exactly-once: cada evento acaba reflejado una vez y solo una, pase lo que pase.

Lo que hay que entender es que exactly-once no es magia: es una contabilidad. Spark guarda en el checkpointLocation qué offsets de Kafka ha procesado y qué ficheros ha escrito, y lo hace de forma que las dos cosas se confirman juntas o no se confirma ninguna. Al reiniciar, lee ese registro y sabe exactamente por dónde iba. Por eso el checkpointLocation no es un detalle de configuración: es la garantía. Si lo borras, o lo apuntas a otro sitio, o dos jobs comparten el mismo, el exactly-once desaparece sin que nadie avise.

Exactly-once cubre el camino dentro del pipeline. Si tu job, además de escribir en S3, llama a una API que envía una notificación, esa llamada no entra en la contabilidad. Al reprocesar, la notificación se manda dos veces. Eso es exactamente lo que va a pasar el viernes de madrugada, así que apúntalo.

1### Opciones descartadas y justificación
2
3**Kafka Connect S3 Sink:** Cumple SLA de latencia marginalmente (1-3 min)
4pero no permite transformación inline. Necesitaríamos un segundo pasó
5(Lambda o Spark batch) para enriquecer y calcular features, lo que añade
6complejidad y latencia. Descartado por arquitectura fragmentada.
7
8**Consumer Python nativo:** Latencia mínima pero escalabilidad manual y
9fragilidad operativa. Con 4.5M eventos/día y picos de 200/s, un solo
10consumer no basta. Gestionar consumer groups, rebalanceo y exactly-once
11en Python es un proyecto en sí mismo. Descartado por riesgo operativo.
12
13**Kinesis Data Analytics:** Técnicamente viable pero 1.500 EUR/mes para
14un volumen que Spark maneja por 800 EUR. Además, el equipo no conoce
15Flink SQL y la migración de lógica PySpark a Flink tiene coste oculto
16significativo. Descartado por coste + lock-in + curva de aprendizaje.
17
18## Decisión
19**Spark Structured Streaming en EMR** con micro-batch de 30 segundos.
20
21## Justificación
221. Ya tenemos EMR y 8 jobs PySpark batch -- el equipo conoce la API
232. Exactly-once garantizado con checkpoints en S3 (crítico en banca)
243. Watermarks nativos para manejar late data (txns que llegan tarde)
254. Escala horizontalmente añadiendo executors (auto-scaling configurado)
265. Micro-batch de 30s cumple SLA de 5 min con margen de 6x
276. Reutiliza schemas Avro + lógica de transformación existente
28
29## Impacto regulatorio
30- Mejora cumplimiento de notificación al BdE (de 24h a <1 min)
31- Datos financieros permanecen encriptados en tránsito (TLS 1.3)
32- Checkpoints en S3 proporcionan audit trail de procesamiento
33- Sara (DPO) válida que no hay impacto en retención de datos personales
34
35## Consecuencias
36- [+] Latencia baja a ~45s (cumple ambos SLAs con margen)
37- [+] Features de fraude frescas -> modelo recupera 92% precisión
38- [-] Cluster EMR siempre encendido (~800 EUR/mes adicionales)
39- [-] Equipo debe dominar Structured Streaming (formación 1-2 semanas)
40- [-] Necesitamos definir estrategia de checkpoint y recovery
41- [~] Pipeline batch se mantiene como fallback durante 2 sprints
42
43## Métricas de éxito
44- Latencia p95 < 60 segundos (medida en Datadog)
45- Zero data loss durante 30 días consecutivos
46- Consumer lag promedio < 1.000 eventos
47- Features de fraude con freshness < 2 minutos
48
49## Autor y fecha
50- Autor: [Data Engineer]
51- Fecha: 2024-09-17
52- Revisores: Alberto Reyes, Paula García

ADR-007 (parte 2): opciones descartadas, decisión final y métricas de éxito

Un ADR (Architecture Decision Record) no es burocracia — es la memoria del equipo. Dentro de 6 meses, cuando alguien pregunte "¿por qué usamos Spark y no Kinesis?", el ADR responde sin necesidad de reuniones arqueológicas. Alberto dice: "Si la decisión cuesta más de 2 días revertir, merece un ADR. Si cuesta más de 2 semanas, merece un ADR Y una prueba de concepto."

### El Architecture Review — defendiendo ante Clara

Jueves, 10:00 AM. Sala Grace Hopper. Clara Mendoza preside. Están Alberto, Sara, Miguel, Paula y tú. Clara tiene el ADR impreso con anotaciones en rojo. "Tienes 10 minutos para presentar y 5 para preguntas. Empieza."

Presentas: contexto, opciones, decisión, justificación. Clara interrumpe dos veces:

1Clara Mendoza (ARB, minuto 4):
2"Pregunta 1: Si el cluster EMR se cae a las 3AM, ¿cuál es el
3Recovery Time Objective? ¿Cuántos minutos perdemos?"
4
5Tu respuesta:
6"Los checkpoints en S3 guardan el estado cada micro-batch (30s).
7Si el cluster cae, EMR auto-restart lo levanta en ~3 minutos.
8El job retoma desde el último checkpoint -- RTO efectivo de
93-5 minutos. Zero data loss porque Kafka retiene 7 días."
10
11Clara Mendoza (minuto 7):
12"Pregunta 2: Black Friday. El año pasado tuvimos 10x el volumen
13normal durante 72 horas. ¿Qué pasa con tu pipeline?"
14
15Tu respuesta:
16"EMR tiene auto-scaling configurado en Terraform. Puede pasar
17de 4 a 12 executors en 4 minutos basándose en consumer lag.
18He validado con los números: a 10x volumen (520 eventos/seg),
19con 12 executors el micro-batch completa en 24 segundos.
20Margen suficiente dentro del trigger de 30 segundos."
21
22Clara (asiente):
23"Aprobado. Siguiente tema."

Architecture Review: las preguntas de Clara y tus respuestas preparadas

Cuando presentes un ADR ante un VP o arquitecto senior, prepara respuestas para las "preguntas asesinas": ¿Qué pasa si se cae? ¿Qué pasa a 10x escala? ¿Cuál es el plan B? ¿Cuánto cuesta si nos equivocamos? Si no puedes responder alguna, di "no lo he evaluado, lo investigo y actualizo el ADR mañana". NUNCA inventes una respuesta.

### Los imprevistos del día

Mientras preparas el ADR (martes por la tarde), recibes dos interrupciones que son completamente normales en un equipo real:

1# Slack DM de Miguel Ortiz -- 14:32
2Miguel Ortiz:
3"Ey, he visto que estás montando el streaming. Necesito que
4incluyas el cálculo de features en la misma pipeline --
5txn_count_1h, avg_amount_1h y ratio vs histórico. Si las
6calculas en streaming me ahorras una consulta a Redis que
7tarda 200ms por predicción. Son 52 predicciones/segundo,
8200ms cada una... haz la cuenta."
9
10Tú:
11"Entendido. Lo tengo en el plan como tarea separada del día 3.
12¿Puedes pasarme la spec exacta de las 5 features que necesitas?
13Nombres, ventanas temporales, y cómo las consume tu modelo."
14
15Miguel Ortiz:
16"Te mando el doc ahora. Gracias por no decirme 'ya veremos' 👍"

Interrupción de Miguel — legítima pero hay que priorizarla

1# Email de Sara Vidal -- 16:02 (con copia a legal@neobank.es)
2Asunto: URGENTE - Solicitud de supresión RGPD - acc_00028473
3---
4Equipo de datos,
5
6El cliente Jenaro Portero Llorente (acc_00028473) ejerció su derecho
7de supresión según el artículo 17 del RGPD el pasado 7 de
8septiembre de 2024. El plazo legal es de 30 días naturales.
9
10Fecha límite: 7 de octubre de 2024.
11Hoy es 18 de septiembre. Quedan 19 días naturales (13 laborables).
12
13Necesito confirmación ESCRITA de que el equipo de datos puede
14completar la supresión/anonimización irreversible en todos los
15sistemas antes de la fecha límite.
16
17Por favor, agendad una reunión conmigo mañana a primera hora
18para evaluar el alcance.
19
20Un saludo,
21Sara Vidal
22DPO - NeoBank
23---

Email de Sara — deadline regulatorio no negociable

En un equipo real, SIEMPRE hay interrupciones. Un día nunca es "una sola tarea". La habilidad no es evitar las interrupciones sino gestionarlas: registrar la petición, confirmar que la has recibido, dar un timeline realista, y seguir con tu prioridad actual. Miguel no es molesto — es un compañero con una necesidad legítima. Sara no es un obstáculo — es la persona que protege a la empresa de multas millonarias.

Regístrate para guardar tu progreso.