lección 10
Bonus avanzado - Monitoring de Kafka y health de eventos
Marcos te pide construir un dashboard de health del pipeline: consumer lag de Kafka, volumen por hora, detección de tracking roto y alertas de schema drift.
⏱ 35 min
### El reto final: monitoring a nivel de infraestructura
Las alertas de calidad de datos que montaste en la lección anterior monitorizan los datos UNA VEZ que están en Redshift. Pero Marcos quiere ir un paso más allí: monitorizar el pipeline COMPLETO, desde que Kafka emite el evento hasta que llega a la tabla final. El objetivo es detectar problemas en CUALQUIER punto de la cadena, no solo al final.
1Marcos Delgado (call, lunes semana 3)23"Las alertas que montaste la semana pasada son buenas para4detectar problemas en los datos ya cargados. Pero hay un5gap: si el Kafka Connect se cae y los eventos dejan de6llegar a S3, tu alerta de frescura no se dispara hasta72 horas después. Son 2 horas de datos perdidos.89Quiero que montes un health dashboard con 3 capas:10111. Kafka consumer lag: cuantos mensajes hay en el topic12 que el consumer todavía no ha procesado. Si sube, el13 consumer está atrasado.14152. S3 landing check: verificar que cada hora se crea un16 archivo nuevo en S3. Si no hay archivo en la última17 hora, el Connect está roto.18193. Pipeline throughput: eventos por minuto que tu pipeline20 procesa. Si baja a 0, algo está mal en TU código.2122No necesitas montarlo en Grafana todavía. Hazlo con Python23y que escriba métricas a un archivo o las imprima. La24infraestructura de dashboarding la montamos después."
### Capa 1: Consumer Lag de Kafka (conceptual)
El consumer lag es la diferencia entre el último mensaje PRODUCIDO a un topic y el último mensaje CONSUMIDO por un consumer group. Si produces 1000 mensajes/segundo y consumes 800, el lag crece 200/segundo. Si crece indefinidamente, tu consumer está saturado o roto.
1# kafka_health.py - Monitor de consumer lag (simulado)2# En producción usarias kafka-python o confluent_kafka3# Aqui simulamos leyendo las métricas de un archivo de estado45import json6from datetime import datetime7from typing import Dict, List89def get_consumer_lag(state_file: str = 'kafka_state.json') -> Dict:10 """11 Lee el estado del consumer group y calcula el lag.12 En producción: kafka.admin.AdminClient.list_consumer_group_offsets()13 """14 with open(state_file) as f:15 state = json.load(f)1617 total_lag = 018 partition_lags = []1920 for partition in state['partitions']:21 lag = partition['latest_offset'] - partition['consumer_offset']22 total_lag += lag23 partition_lags.append({24 'partition': partition['id'],25 'lag': lag,26 'latest_offset': partition['latest_offset'],27 'consumer_offset': partition['consumer_offset'],28 })2930 # Clasificar severidad31 if total_lag > 100000:32 status = 'CRITICAL'33 elif total_lag > 10000:34 status = 'WARNING'35 else:36 status = 'OK'3738 return {39 'check': 'kafka_consumer_lag',40 'status': status,41 'total_lag': total_lag,42 'partitions': partition_lags,43 'message': f'Lag total: {total_lag:,} mensajes ({status})',44 'timestamp': datetime.utcnow().isoformat(),45 }4647def check_s3_landing(s3_path: str, max_age_minutes: int = 60) -> Dict:48 """49 Verifica que hay archivos recientes en S3.50 En producción: boto3 list_objects con filtro por fecha.51 Aqui simulamos con el filesystem local.52 """53 from pathlib import Path54 import os5556 path = Path(s3_path)57 if not path.exists():58 return {59 'check': 's3_landing',60 'status': 'CRITICAL',61 'message': f'Path no existe: {s3_path}',62 }6364 # Buscar el archivo más reciente65 files = sorted(path.glob('**/*.jsonl'), key=os.path.getmtime, reverse=True)6667 if not files:68 return {69 'check': 's3_landing',70 'status': 'CRITICAL',71 'message': 'No hay archivos JSONL en el path',72 }7374 latest_file = files[0]75 age_minutes = (datetime.now() - datetime.fromtimestamp(76 os.path.getmtime(latest_file)77 )).total_seconds() / 607879 if age_minutes > max_age_minutes * 2:80 status = 'CRITICAL'81 elif age_minutes > max_age_minutes:82 status = 'WARNING'83 else:84 status = 'OK'8586 return {87 'check': 's3_landing',88 'status': status,89 'latest_file': str(latest_file.name),90 'age_minutes': round(age_minutes),91 'message': f'Ultimo archivo: {latest_file.name} ({age_minutes:.0f} min ago)',92 }
Monitoring de Kafka y S3: detectar problemas antes de que lleguen a Redshift
### Capa 3: Pipeline throughput
1# pipeline_throughput.py - Medir cuantos eventos procesa tu pipeline2import pandas as pd3from datetime import datetime, timedelta4from pathlib import Path56def check_pipeline_throughput(7 output_path: str = 'output/staging/',8 window_minutes: int = 60,9) -> Dict:10 """11 Verifica cuantos eventos ha procesado el pipeline recientemente.12 Lee el campo ingested_at del output para calcularlo.13 """14 path = Path(output_path)15 parquet_files = list(path.glob('*.parquet'))1617 if not parquet_files:18 return {19 'check': 'pipeline_throughput',20 'status': 'CRITICAL',21 'message': 'No hay archivos de output',22 'events_per_minute': 0,23 }2425 # Leer el más reciente26 df = pd.read_parquet(parquet_files[-1])27 df['ingested_at'] = pd.to_datetime(df['ingested_at'])2829 now = datetime.utcnow()30 recent = df[df['ingested_at'] >= now - timedelta(minutes=window_minutes)]3132 events_per_minute = len(recent) / window_minutes if window_minutes > 0 else 03334 # Umbrales (basados en 2.5M eventos/día = ~1736/min)35 if events_per_minute == 0:36 status = 'CRITICAL'37 elif events_per_minute < 500:38 status = 'WARNING'39 else:40 status = 'OK'4142 return {43 'check': 'pipeline_throughput',44 'status': status,45 'events_per_minute': round(events_per_minute),46 'events_last_window': len(recent),47 'message': f'{events_per_minute:.0f} eventos/min (esperado: ~1700/min)',48 }4950# --- Dashboard completo ---51def run_health_dashboard():52 """Ejecuta todos los checks de health del pipeline."""53 print("=" * 60)54 print(f"PIPELINE HEALTH DASHBOARD - {datetime.utcnow().isoformat()}")55 print("=" * 60)5657 checks = [58 get_consumer_lag(),59 check_s3_landing('datos/taskflow_events/'),60 check_pipeline_throughput(),61 ]6263 for check in checks:64 icon = {'OK': '?', 'WARNING': '??', 'CRITICAL': '??'}[check['status']]65 print(f"\n{icon} {check['check']}")66 print(f" Status: {check['status']}")67 print(f" {check['message']}")6869 critical = [c for c in checks if c['status'] == 'CRITICAL']70 if critical:71 print(f"\n{'='*60}")72 print(f"?? {len(critical)} CHECKS CRITICOS - INVESTIGAR")73 else:74 print(f"\n{'='*60}")75 print("? Todos los sistemas operando normalmente")7677run_health_dashboard()
Health dashboard completo: Kafka + S3 + Pipeline en un solo reporte
Consejo de senior: un buen health dashboard tiene 3 niveles: (1) el pipeline RECIBE datos? (Kafka lag, S3 landing), (2) el pipeline PROCESA datos? (throughput, errores), (3) los datos SON CORRECTOS? (calidad, distribución). Si monitorizas los 3 niveles, detectas cualquier problema en minutos en vez de horas.
### Deteccion de tracking roto
El último componente que Marcos quiere: detectar cuando un tipo de evento DEJA de emitirse. Esto es lo que pasó con board_viewed el mes pasado. La solución: mantener un registro de "último evento visto" por tipo y alertar si alguno supera un umbral de silencio.
1# tracking_health.py - Detectar tipos de evento que dejan de emitirse2import pandas as pd3from datetime import datetime, timedelta45def check_event_type_health(6 df: pd.DataFrame,7 max_silence_hours: int = 4,8) -> list:9 """10 Para cada tipo de evento, verifica cuando fue el último que se recibió.11 Si un tipo que normalmente emite cada hora lleva más de max_silence_hours12 sin emitir, es CRITICAL.13 """14 now = datetime.utcnow()1516 # Ultimo evento por tipo17 last_by_type = df.groupby('event_type')['event_timestamp'].max().reset_index()18 last_by_type.columns = ['event_type', 'last_seen']19 last_by_type['hours_since_last'] = (20 (now - last_by_type['last_seen']).dt.total_seconds() / 360021 )2223 # Tipos esperados (que deberian emitir al menos cada hora)24 expected_frequent = [25 'board_viewed', 'task_created', 'task_completed',26 'comment_added', 'user_login', 'task_assigned',27 ]2829 alerts = []30 for _, row in last_by_type.iterrows():31 if row['event_type'] in expected_frequent:32 if row['hours_since_last'] > max_silence_hours:33 alerts.append({34 'event_type': row['event_type'],35 'hours_silent': round(row['hours_since_last'], 1),36 'status': 'CRITICAL',37 'action': f'Verificar con David si el tracking de {row["event_type"]} sigue activo',38 })39 elif row['hours_since_last'] > max_silence_hours / 2:40 alerts.append({41 'event_type': row['event_type'],42 'hours_silent': round(row['hours_since_last'], 1),43 'status': 'WARNING',44 'action': 'Monitorizar - podria ser horario de baja actividad',45 })4647 return alerts4849# Uso50events = pd.read_parquet('datos/stg_product_events.parquet')51tracking_alerts = check_event_type_health(events)5253if tracking_alerts:54 print("?? Alertas de tracking:")55 for a in tracking_alerts:56 print(f" [{a['status']}] {a['event_type']}: {a['hours_silent']}h sin eventos")57 print(f" Accion: {a['action']}")58else:59 print("? Todos los tipos de evento emitiendo normalmente")
Deteccion de tracking roto: nunca más 3 días sin darnos cuenta
Con este sistema completo, TaskFlow tiene visibilidad end-to-end de su pipeline de datos. Desde que un usuario hace click en el producto hasta que la métrica de retención se actualiza en Looker, cada pasó está monitorizado. Si algo falla, el equipo se entera en minutos, no en días.
En la vida real, este tipo de monitoring se construye incrementalmente. No montas todo en un día. Empiezas con los checks más críticos (volumen y frescura), demuestras el valor, y luego anadir los demas. Lo importante es tener ALGO desde el día 1, no esperar a tener el sistema perfecto.
### El cierre: Marcos reconoce tu trabajo
1Marcos Delgado (viernes semana 3, después de la demo)23"Has montado en 3 semanas lo que yo esperaba que tardases4un mes y medio. El pipeline, las cohortes, las alertas,5el health dashboard. Y todo clean, testeado, documentado.67Te voy a ser honesto: cuando Pablo dijo que contrataba a8alguien de FreshMart con 6 meses de experiencia, pense9'va a necesitar mucha mano'. Me equivoque.1011La diferencia es que no solo escribes código -- entiendes12el PROBLEMA. Cuando encontraste Simpson no fuiste y dijiste13'mira que anomalia técnica'. Dijiste 'esto cambia la14narrativa de negocio'. Eso es lo que necesitamos.1516El proximo trimestre quiero que lideres el proyecto de17consumer lag monitoring con Kafka. Es más infra, menos18analítica. Te va a sacar de tu zona de comfort. Te interesa?"1920Tu: "Me interesa mucho. Es justo el gap que quiero cerrar:21entender la infra debajo del pipeline, no solo los datos22que salen."2324Marcos: "Bien. El lunes hablamos de scope."
Con este sistema completo, TaskFlow tiene visibilidad end-to-end de su pipeline de datos. Desde que un usuario hace click en el producto hasta que la métrica de retención se actualiza en Looker, cada pasó está monitorizado. Si algo falla, el equipo se entera en minutos, no en días. Y tu eres la persona que lo construyo.
## ejercicios
Simular y detectar consumer lag de Kafka
Dada una lista de particiones con su offset de producer y consumer, calcula el lag total, identifica la particion con mayor lag y determina la severidad.
Dashboard de health completo con puntuacion
Combina todos los checks (consumer lag, S3 landing, throughput, distribución, duplicados) en un dashboard que genera una puntuacion de 0-100 del health del pipeline.
Regístrate para guardar tu progreso.
## comentarios
Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.
Inicia sesión para comentar y responder.
cargando comentarios...