lección 6
Día 4 (mañana) — Algo se rompe en producción
Una alerta de Airflow a las 9:15 AM. Tu primer incidente de producción: diagnosticar un DAG que falló, comunicar al equipo y aplicar un fix rápido mientras planeas el definitivo.
⏱ 55 min
### 9:15 AM — La alerta que te pone nervioso
Jueves por la mañana. Te sientas con el café, abres Slack y lo primero que ves es un mensaje en #data-alerts con un emoji rojo que no habías visto antes. Tu corazón se acelera un poco:
1#data-alerts 9:15 AM2AIRFLOW ALERT — DAG: raw_orders_daily — FAILED3Task: extract_orders failed at 2024-09-19 03:00 UTC4Error: ConnectionRefusedError: [Errno 111] Connection refused5Retries exhausted (3/3)6Next retry: NONE (max retries reached)78Dag run: scheduled__2024-09-19T03:00:00+00:009Duration before failure: 10m 01s
Tu primera alerta de producción — el DAG de pedidos no se ejecutó anoche
Esto significa que el pipeline que extrae los pedidos diarios de la base de datos de producción y los deja en el data lake... no se ejecutó anoche. Los datos de pedidos de ayer NO están en el lake. Cualquier dashboard que dependa de datos frescos está mostrando información de anteayer. No es tu DAG ni tu responsabilidad directa, pero estás a punto de aprender cómo se gestiona un incidente de verdad.
### 9:30 AM — Standup: Elena te asigna el diagnóstico
1Elena Torres 9:31 AM2¿Alguien ha visto la alerta de raw_orders? Lleva fallando desde las 3AM.3Raúl, ¿es tuyo ese DAG?45Raúl Vega 9:34 AM6sí, es mío... pero estoy en medio de lo del inventario7la verdad no sé por qué falla, la semana pasada iba bien8puede ser que RDS esté saturado?910Elena Torres 9:36 AM11tú, ¿puedes echarle un ojo? No te pido que lo arregles, solo12que diagnostiques qué pasa y me digas. Si es algo de RDS escala a13infra. Si es un bug en el DAG, dime qué ves.1415Tú 9:37 AM16Dale, me pongo con ello ahora mismo.
Elena confía en ti para diagnosticar — no para arreglar (aún)
Nota lo que acaba de pasar: Elena te pidió DIAGNOSTICAR, no arreglar. Esto es importante. Como junior, tu trabajo en un incidente es investigar, documentar hallazgos y proponer opciones. No es aplicar un fix a ciegas que pueda empeorar las cosas. Esa distinción entre "investigar" y "actuar" es una de las cosas más importantes que aprenderás en tus primeros meses.
Consejo de senior: en un incidente de producción, la PEOR respuesta es entrar en pánico y hacer cambios sin entender el problema. He visto juniors que ante una alerta hacen un "fix rápido" que rompe 3 cosas más. La secuencia correcta es siempre: (1) entender qué hace el sistema, (2) leer los logs, (3) formular una hipótesis, (4) verificarla, (5) proponer solución. Nunca saltes al paso 5 directamente.
### Paso 1: Entender qué hace el DAG raw_orders_daily
Antes de mirar los logs, necesitas entender QUÉ se supone que hace este DAG. Abres el repositorio en GitHub y buscas el archivo del DAG:
1# dags/raw_orders_daily.py (simplificado)2"""3DAG: raw_orders_daily4Schedule: Cada día a las 03:00 UTC5Qué hace:6 1. Conecta a PostgreSQL de producción (RDS)7 2. Extrae todos los pedidos del día anterior8 3. Los escribe como CSV en S3: raw/orders/{year}/{month}/{day}/9 4. Envía un mensaje a #data-alerts si falla10"""11from airflow import DAG12from airflow.operators.python import PythonOperator13from datetime import datetime, timedelta14import psycopg215import pandas as pd16import boto31718default_args = {19 "owner": "raul.vega",20 "retries": 3,21 "retry_delay": timedelta(minutes=5),22}2324def extract_orders(**context):25 """Extrae pedidos del día anterior de PostgreSQL."""26 execution_date = context["ds"]2728 # CONEXIÓN HARDCODEADA (esto lo verás después)29 conn = psycopg2.connect(30 host="freshmart-prod.xxxxx.eu-west-1.rds.amazonaws.com",31 port=5432,32 dbname="orders",33 user="data_readonly",34 password="FrM4rt_R0_2024!", # CREDENCIAL EN EL CÓDIGO35 )3637 query = f"""38 SELECT * FROM orders39 WHERE order_date = '{execution_date}'40 """41 df = pd.read_sql(query, conn)42 conn.close()4344 # Escribir a S345 output_path = f"s3://freshmart-lake/raw/orders/{execution_date}/"46 df.to_csv(f"{output_path}orders.csv", index=False)4748with DAG(49 "raw_orders_daily",50 default_args=default_args,51 schedule_interval="0 3 * * *",52 start_date=datetime(2024, 1, 1),53 catchup=False,54) as dag:55 extract = PythonOperator(56 task_id="extract_orders",57 python_callable=extract_orders,58 )
El DAG de Raúl: funcional pero con MUCHOS problemas de buenas prácticas
Al leer el código ya ves varios problemas (que no son la causa del fallo, pero que anotas mentalmente): la credencial está hardcodeada en el código (gravísimo), la query es vulnerable a inyección SQL, no hay validación del DataFrame antes de escribir. Pero ahora toca centrarse en el ERROR, no en refactorizar.
NUNCA pongas credenciales en el código. Ni en un script "temporal", ni en un notebook "de prueba", ni en un commit que "después borro". Las credenciales hardcodeadas son la causa número 1 de brechas de seguridad en empresas. Usa variables de entorno, AWS Secrets Manager, Airflow Connections o cualquier sistema de secrets — pero NUNCA el código fuente.
### Paso 2: Leer los logs del DAG en Airflow
Accedes a la interfaz web de Airflow (http://airflow.internal:8080 en la red de FreshMart) y navegas al DAG raw_orders_daily. Ves que el último run exitoso fue el martes 17 de septiembre. El del miércoles 18 falló. El del jueves 19 (hoy) también:
1# Logs del task extract_orders — run del 2024-09-1923[2024-09-19 03:00:05] INFO - Starting task extract_orders4[2024-09-19 03:00:05] INFO - Execution date: 2024-09-185[2024-09-19 03:00:06] INFO - Connecting to PostgreSQL at freshmart-prod.xxxxx.eu-west-1.rds.amazonaws.com:54326[2024-09-19 03:00:06] ERROR - Connection failed:7 psycopg2.OperationalError: FATAL: password authentication failed8 for user "data_readonly"9[2024-09-19 03:00:06] INFO - Retrying in 300 seconds (attempt 1/3)10[2024-09-19 03:05:06] ERROR - Connection failed (retry 2/3): password authentication failed11[2024-09-19 03:10:06] ERROR - Connection failed (retry 3/3): password authentication failed12[2024-09-19 03:10:06] ERROR - Max retries reached. Task FAILED.1314# Logs del run anterior (2024-09-18, que también falló)15[2024-09-18 03:00:06] ERROR - Connection failed:16 psycopg2.OperationalError: FATAL: password authentication failed17 for user "data_readonly"18[2024-09-18 03:05:06] ERROR - password authentication failed (retry 2/3)19[2024-09-18 03:10:06] ERROR - password authentication failed (retry 3/3)
Dos días seguidos con el mismo error — y el último run bueno fue el martes
### Paso 3: Formular hipótesis
Ahora piensas. Los logs te dicen dos cosas diferentes en dos días:
- 01.Miércoles 18: "password authentication failed" → las credenciales son incorrectas
- 02.Jueves 19: el mismo error, otra vez → no es un glitch puntual
- 03.El último run EXITOSO fue el martes 17 → algo cambió entre el martes y el miércoles
Hipótesis: alguien cambió las credenciales de la base de datos entre el martes y el miércoles. El miércoles el DAG intentó conectar con la contraseña vieja, falló. El jueves volvió a intentarlo con la misma contraseña vieja y volvió a fallar. El servidor sigue ahí (responde con un error de autenticación, no con un timeout), así que no es un problema de red — es de credenciales.
### Paso 4: Verificar la hipótesis
Preguntas en el canal #infra (infraestructura) si alguien rotó credenciales esta semana:
1Tú 10:15 AM en #infra2Hola equipo, estoy investigando un fallo en el DAG raw_orders_daily.3Los logs muestran "password authentication failed" desde el miércoles.4¿Se rotaron credenciales de RDS esta semana?56Carlos (Infra) 10:22 AM7Sí, rotamos las credenciales de todos los usuarios de solo lectura8el martes por la noche. Es parte del protocolo trimestral de seguridad.9Mandamos un email al canal de engineering el lunes avisando.10Las nuevas credenciales están en AWS Secrets Manager:11secret: freshmart/prod/rds/data_readonly1213Carlos (Infra) 10:23 AM14¿El equipo de data no usa Secrets Manager? 🤔1516Tú 10:25 AM17Aparentemente este DAG tiene las credenciales hardcodeadas en el código...18Vamos a arreglarlo. Gracias Carlos!
Verificación: infra rotó credenciales y el DAG usa las antiguas hardcodeadas
Hipótesis confirmada. El problema es claro: las credenciales se rotaron el martes por la noche, el DAG tiene la contraseña vieja escrita directamente en el código, y como nadie del equipo de data leyó el email de aviso, nadie se enteró hasta que la alerta saltó el miércoles.
### Paso 5: Reportar a Elena con opciones
No reportas solo el problema — reportas el problema Y propones soluciones. Esto es lo que diferencia a un junior que investiga de un ingeniero que resuelve:
1Tú 11:22 AM2Elena, encontré el problema del DAG raw_orders.3No es que RDS esté caído — las credenciales se rotaron el martes4por la noche (protocolo trimestral de seguridad de infra) y nadie5avisó al equipo de data. El DAG usa las credenciales antiguas6hardcodeadas en el script.78Opciones:91. Fix rápido (5 min): actualizar la contraseña en el código con la10 nueva. Mismo problema la próxima vez que roten (en 3 meses).112. Fix definitivo (2-3h): migrar la conexión a Airflow Connections12 o leer de AWS Secrets Manager. Nunca más se rompe por rotación.1314¿Cómo prefieres que proceda?1516Elena Torres 11:25 AM17Buena investigación. 👏18Haz el fix rápido AHORA (que llevamos sin datos de pedidos desde19ayer) y abre un ticket para el fix definitivo. Lo hacemos la semana20que viene con calma.21Y por favor, manda un mensaje a #data-team diciendo qué pasó y que22ya está arreglado. El equipo necesita saber.
Reporta: problema + causa raíz + opciones con trade-offs. Esto es profesional.
Consejo de senior: cuando reportes un incidente a tu lead, SIEMPRE presenta opciones con trade-offs. No digas "se rompió, ¿qué hago?". Di "se rompió por X, puedo hacer A (rápido pero temporal) o B (más trabajo pero definitivo). ¿Cuál prefieres?". Esto demuestra que no solo investigaste sino que PENSASTE en la solución. Y le das a tu lead la información para tomar una decisión informada.
### Paso 6: Aplicar el fix rápido
Elena dijo "fix rápido ahora". Así que actualizas la credencial en el código del DAG. Sí, es feo. Sí, es temporal. Pero la prioridad ahora es RESTAURAR EL SERVICIO — los datos llevan 2 días sin actualizarse.
1# Fix rápido: actualizar la credencial (temporal)2# Obtenemos la nueva credencial de AWS Secrets Manager3# (en la terminal, con aws cli)4# aws secretsmanager get-secret-value --secret-id freshmart/prod/rds/data_readonly --query SecretString --output text56# Luego actualizamos el DAG con la nueva password:7# (esto es el fix TEMPORAL — la semana que viene lo migramos a Connections)8conn = psycopg2.connect(9 host="freshmart-prod.xxxxx.eu-west-1.rds.amazonaws.com",10 port=5432,11 dbname="orders",12 user="data_readonly",13 password="NEW_FrM4rt_R0_Q4_2024!", # ← Nueva credencial (FIX TEMPORAL)14)
Fix temporal: actualizar la credencial. La semana que viene se hace bien.
Haces el cambio, lo subes a una rama, abres la PR (que Elena aprueba sin revisión detallada porque es urgente) y mergeas. Ahora, ojo con lo siguiente: este DAG tiene catchup=False, así que darle a «Trigger DAG» lanzaría una ejecución nueva con la fecha de hoy y dejaría los dos días caídos tal cual. Lo que hay que hacer es limpiar las dos ejecuciones fallidas (Browse → DAG Runs → seleccionar las del 18 y el 19 → Clear), que las relanza cada una con su fecha. A los tres minutos las dos están en verde y los pedidos del martes y del miércoles entran en el lake.
### Paso 7: Comunicar al equipo
Elena te pidió que comunicaras al equipo. No es opcional — es parte del protocolo de incidentes. El equipo necesita saber: (1) que hubo un problema, (2) cuál fue la causa, (3) que ya está resuelto, y (4) si hay impacto residual.
1Tú 12:05 en #data-team2Resumen del incidente raw_orders_daily:34**Qué pasó:** El DAG raw_orders_daily falló el miércoles 18 y el jueves 19. Los pedidos no se actualizaron durante ~33 horas.56**Causa raíz:** Infra rotó las credenciales del usuario data_readonly de RDS el martes por la noche (protocolo trimestral). El DAG tenía la contraseña escrita en el código y seguía usando la antigua.78**Fix aplicado:** Credencial actualizada. Relanzadas las dos ejecuciones caídas. Los pedidos del martes y del miércoles ya están en el lake.910**Impacto:** Entre el miércoles a las 3:00 y hoy a las 12:00, los dashboards de pedidos enseñaban datos del lunes. Ya están al día.1112**Próximos pasos:** Ticket DATA-162 para migrar la conexión a Airflow Connections / Secrets Manager. Y revisar si hay más DAGs con credenciales en el código.1314cc @elena.torres @raul.vega1516Elena Torres 12:0817Perfecto. Gracias por el resumen claro.1819Jorge Ruiz 12:1020Gracias! ¿Entonces los números de pedidos de ayer en Metabase ya son correctos?2122Tú 12:1123Sí, ya están los del martes y el miércoles. Los de hoy entrarán esta noche, como siempre.2425Raúl Vega 12:1526uf perdona, ese DAG lleva meses con las creds hardcodeadas 😅27lo de Secrets Manager lo tenía en mi TODO... gracias por el ticket
Comunicar el incidente: claro, estructurado y con next steps
Observa la reacción del equipo: Elena agradece la claridad, Jorge verifica el impacto en SU trabajo (normal), y Raúl reconoce la deuda técnica. Nadie te culpa — investigaste, resolviste y comunicaste. Eso es lo que se espera.
### El ticket para el fix definitivo
Antes de seguir con tu trabajo del día, abres el ticket en Linear para que el fix definitivo no se pierda:
1╔══════════════════════════════════════════════════════════════╗2║ TICKET: DATA-162 ║3║ Título: Migrar credenciales de raw_orders_daily a ║4║ Airflow Connections / AWS Secrets Manager ║5╠══════════════════════════════════════════════════════════════╣6║ Contexto: ║7║ El DAG raw_orders_daily tiene credenciales de RDS ║8║ hardcodeadas en el código. Cuando infra rotó las creds ║9║ (protocolo trimestral), el DAG falló 2 días hasta que ║10║ se detectó y se actualizó manualmente. ║11║ ║12║ Solución propuesta: ║13║ 1. Crear una Airflow Connection tipo 'postgres' con las ║14║ credenciales correctas ║15║ 2. O: leer la credential de AWS Secrets Manager en runtime ║16║ 3. Eliminar la password del código fuente ║17║ 4. Verificar que no haya más DAGs con creds hardcodeadas ║18║ ║19║ Prioridad: Media (fix rápido ya aplicado) ║20║ Asignado a: Por definir (¿Raúl? ¿Yo?) ║21║ Estimación: 2-3 horas ║22╚══════════════════════════════════════════════════════════════╝
El ticket documenta el problema para que el fix definitivo no se olvide
### Reflexión: lo que has aprendido sobre incidentes
Son las 12:30 y tu primer incidente de producción está resuelto. No era tu DAG, no era tu código, ni siquiera era un error de código — era un problema de configuración causado por falta de comunicación entre equipos. Y sin embargo, TÚ fuiste quien lo diagnosticó y resolvió. Eso es lo que hace un ingeniero de datos en la vida real.
- Los fallos en producción son NORMALES — ocurren cada semana en cualquier empresa
- La mayoría de fallos NO son bugs de código — son problemas de configuración, permisos, red o datos
- Diagnosticar metódicamente (logs → hipótesis → verificación) te ahorra horas de prueba y error
- Comunicar el incidente es parte del fix — si no avisas al equipo, el siguiente que mire los dashboards pensará que hay otro problema
- Fix rápido + ticket definitivo es una estrategia válida cuando el servicio está caído
Un error que veo en juniors: resolver el incidente y NO comunicarlo. Piensas "ya está arreglado, no hace falta molestar". ERROR. El equipo necesita saber que hubo un problema, por cuánto tiempo, y si les afecta. Jorge revisó sus números de ayer pensando que eran correctos — si tú no avisas, él puede tomar decisiones basadas en datos obsoletos sin saberlo.
Regístrate para guardar tu progreso.